@bpmnkit/operate 0.0.5

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 (72) hide show
  1. package/README.md +131 -0
  2. package/dist/components/badge.d.ts +2 -0
  3. package/dist/components/badge.js +2 -0
  4. package/dist/components/card.d.ts +2 -0
  5. package/dist/components/card.js +2 -0
  6. package/dist/components/chart.d.ts +6 -0
  7. package/dist/components/chart.js +161 -0
  8. package/dist/components/filter-table.d.ts +20 -0
  9. package/dist/components/filter-table.js +215 -0
  10. package/dist/components/table.d.ts +2 -0
  11. package/dist/components/table.js +2 -0
  12. package/dist/css.d.ts +2 -0
  13. package/dist/css.js +1071 -0
  14. package/dist/index.d.ts +3 -0
  15. package/dist/index.js +2 -0
  16. package/dist/mock-data.d.ts +18 -0
  17. package/dist/mock-data.js +509 -0
  18. package/dist/operate.d.ts +3 -0
  19. package/dist/operate.js +308 -0
  20. package/dist/router.d.ts +9 -0
  21. package/dist/router.js +49 -0
  22. package/dist/stores/base.d.ts +20 -0
  23. package/dist/stores/base.js +32 -0
  24. package/dist/stores/dashboard.d.ts +6 -0
  25. package/dist/stores/dashboard.js +19 -0
  26. package/dist/stores/decisions.d.ts +9 -0
  27. package/dist/stores/decisions.js +19 -0
  28. package/dist/stores/definitions.d.ts +9 -0
  29. package/dist/stores/definitions.js +19 -0
  30. package/dist/stores/incidents.d.ts +10 -0
  31. package/dist/stores/incidents.js +27 -0
  32. package/dist/stores/instances.d.ts +15 -0
  33. package/dist/stores/instances.js +33 -0
  34. package/dist/stores/jobs.d.ts +10 -0
  35. package/dist/stores/jobs.js +19 -0
  36. package/dist/stores/tasks.d.ts +10 -0
  37. package/dist/stores/tasks.js +19 -0
  38. package/dist/stream.d.ts +12 -0
  39. package/dist/stream.js +42 -0
  40. package/dist/types.d.ts +48 -0
  41. package/dist/types.js +2 -0
  42. package/dist/views/dashboard.d.ts +6 -0
  43. package/dist/views/dashboard.js +98 -0
  44. package/dist/views/decision-detail.d.ts +15 -0
  45. package/dist/views/decision-detail.js +167 -0
  46. package/dist/views/decisions.d.ts +7 -0
  47. package/dist/views/decisions.js +78 -0
  48. package/dist/views/definition-detail.d.ts +15 -0
  49. package/dist/views/definition-detail.js +339 -0
  50. package/dist/views/definitions.d.ts +7 -0
  51. package/dist/views/definitions.js +70 -0
  52. package/dist/views/header.d.ts +9 -0
  53. package/dist/views/header.js +49 -0
  54. package/dist/views/incident-detail.d.ts +14 -0
  55. package/dist/views/incident-detail.js +540 -0
  56. package/dist/views/incidents.d.ts +7 -0
  57. package/dist/views/incidents.js +108 -0
  58. package/dist/views/instance-detail.d.ts +16 -0
  59. package/dist/views/instance-detail.js +728 -0
  60. package/dist/views/instances.d.ts +7 -0
  61. package/dist/views/instances.js +249 -0
  62. package/dist/views/jobs.d.ts +6 -0
  63. package/dist/views/jobs.js +60 -0
  64. package/dist/views/messages.d.ts +11 -0
  65. package/dist/views/messages.js +431 -0
  66. package/dist/views/nav.d.ts +10 -0
  67. package/dist/views/nav.js +60 -0
  68. package/dist/views/task-detail.d.ts +13 -0
  69. package/dist/views/task-detail.js +187 -0
  70. package/dist/views/tasks.d.ts +7 -0
  71. package/dist/views/tasks.js +72 -0
  72. package/package.json +46 -0
@@ -0,0 +1,728 @@
1
+ import { BpmnCanvas } from "@bpmnkit/canvas";
2
+ import { createConfigPanelPlugin } from "@bpmnkit/plugins/config-panel";
3
+ import { createConfigPanelBpmnPlugin } from "@bpmnkit/plugins/config-panel-bpmn";
4
+ import { createTokenHighlightPlugin } from "@bpmnkit/plugins/token-highlight";
5
+ import { badge } from "../components/badge.js";
6
+ import { MOCK_ACTIVE_ELEMENTS, MOCK_BPMN_XML, MOCK_VARIABLES, MOCK_VISITED_ELEMENTS, } from "../mock-data.js";
7
+ import { IncidentsStore } from "../stores/incidents.js";
8
+ function relTime(iso) {
9
+ if (!iso)
10
+ return "—";
11
+ const diff = Date.now() - new Date(iso).getTime();
12
+ const m = Math.floor(diff / 60_000);
13
+ if (m < 1)
14
+ return "just now";
15
+ if (m < 60)
16
+ return `${m}m ago`;
17
+ const h = Math.floor(m / 60);
18
+ if (h < 24)
19
+ return `${h}h ago`;
20
+ return `${Math.floor(h / 24)}d ago`;
21
+ }
22
+ function detectType(value) {
23
+ if (!value || value === "null")
24
+ return "null";
25
+ if (value === "true" || value === "false")
26
+ return "boolean";
27
+ if (value.trim() !== "" && !Number.isNaN(Number(value)))
28
+ return "number";
29
+ const trimmed = value.trim();
30
+ if (trimmed.startsWith("{") || trimmed.startsWith("[")) {
31
+ try {
32
+ JSON.parse(value);
33
+ return "json";
34
+ }
35
+ catch {
36
+ // not valid json
37
+ }
38
+ }
39
+ return "string";
40
+ }
41
+ function buildJsonDom(container, value, indent) {
42
+ const pad = " ".repeat(indent);
43
+ if (value === null) {
44
+ const s = document.createElement("span");
45
+ s.className = "op-json-null";
46
+ s.textContent = "null";
47
+ container.appendChild(s);
48
+ }
49
+ else if (typeof value === "boolean") {
50
+ const s = document.createElement("span");
51
+ s.className = "op-json-bool";
52
+ s.textContent = String(value);
53
+ container.appendChild(s);
54
+ }
55
+ else if (typeof value === "number") {
56
+ const s = document.createElement("span");
57
+ s.className = "op-json-number";
58
+ s.textContent = String(value);
59
+ container.appendChild(s);
60
+ }
61
+ else if (typeof value === "string") {
62
+ const s = document.createElement("span");
63
+ s.className = "op-json-string";
64
+ s.textContent = JSON.stringify(value);
65
+ container.appendChild(s);
66
+ }
67
+ else if (Array.isArray(value)) {
68
+ container.appendChild(document.createTextNode("[\n"));
69
+ for (let i = 0; i < value.length; i++) {
70
+ container.appendChild(document.createTextNode(`${pad} `));
71
+ buildJsonDom(container, value[i], indent + 1);
72
+ container.appendChild(document.createTextNode(i < value.length - 1 ? ",\n" : "\n"));
73
+ }
74
+ container.appendChild(document.createTextNode(`${pad}]`));
75
+ }
76
+ else if (typeof value === "object") {
77
+ const entries = Object.entries(value);
78
+ container.appendChild(document.createTextNode("{\n"));
79
+ for (let i = 0; i < entries.length; i++) {
80
+ const entry = entries[i];
81
+ if (!entry)
82
+ continue;
83
+ const [k, v] = entry;
84
+ container.appendChild(document.createTextNode(`${pad} `));
85
+ const keySpan = document.createElement("span");
86
+ keySpan.className = "op-json-key";
87
+ keySpan.textContent = JSON.stringify(k);
88
+ container.appendChild(keySpan);
89
+ container.appendChild(document.createTextNode(": "));
90
+ buildJsonDom(container, v, indent + 1);
91
+ container.appendChild(document.createTextNode(i < entries.length - 1 ? ",\n" : "\n"));
92
+ }
93
+ container.appendChild(document.createTextNode(`${pad}}`));
94
+ }
95
+ }
96
+ function highlightTextNodes(container, query) {
97
+ if (!query)
98
+ return;
99
+ const lower = query.toLowerCase();
100
+ const walker = document.createTreeWalker(container, NodeFilter.SHOW_TEXT);
101
+ const hits = [];
102
+ let node = walker.nextNode();
103
+ while (node) {
104
+ const t = node;
105
+ if ((t.textContent ?? "").toLowerCase().includes(lower) && t.parentNode) {
106
+ hits.push({ parent: t.parentNode, node: t });
107
+ }
108
+ node = walker.nextNode();
109
+ }
110
+ for (const { parent, node: t } of hits) {
111
+ const text = t.textContent ?? "";
112
+ const lowerText = text.toLowerCase();
113
+ const fragment = document.createDocumentFragment();
114
+ let last = 0;
115
+ let idx = lowerText.indexOf(lower, 0);
116
+ while (idx !== -1) {
117
+ if (idx > last)
118
+ fragment.appendChild(document.createTextNode(text.slice(last, idx)));
119
+ const mark = document.createElement("mark");
120
+ mark.className = "op-json-match";
121
+ mark.textContent = text.slice(idx, idx + query.length);
122
+ fragment.appendChild(mark);
123
+ last = idx + query.length;
124
+ idx = lowerText.indexOf(lower, last);
125
+ }
126
+ if (last < text.length)
127
+ fragment.appendChild(document.createTextNode(text.slice(last)));
128
+ parent.replaceChild(fragment, t);
129
+ }
130
+ }
131
+ export function createInstanceDetailView(instanceKey, instancesStore, cfg, onBack) {
132
+ const el = document.createElement("div");
133
+ el.className = "op-view op-instance-detail";
134
+ // Breadcrumb
135
+ const breadcrumb = document.createElement("div");
136
+ breadcrumb.className = "op-breadcrumb";
137
+ const backBtn = document.createElement("button");
138
+ backBtn.className = "op-back-btn";
139
+ backBtn.textContent = "← Instances";
140
+ backBtn.addEventListener("click", onBack);
141
+ breadcrumb.appendChild(backBtn);
142
+ el.appendChild(breadcrumb);
143
+ // Process hierarchy chain (shown for sub-process instances)
144
+ const processChainEl = document.createElement("div");
145
+ processChainEl.className = "op-process-chain";
146
+ el.appendChild(processChainEl);
147
+ // Meta row
148
+ const meta = document.createElement("div");
149
+ meta.className = "op-instance-meta";
150
+ el.appendChild(meta);
151
+ const cancelFeedback = document.createElement("div");
152
+ cancelFeedback.className = "op-action-feedback";
153
+ cancelFeedback.style.display = "none";
154
+ el.appendChild(cancelFeedback);
155
+ // Canvas + sidebar layout
156
+ const layout = document.createElement("div");
157
+ layout.className = "op-detail-layout";
158
+ el.appendChild(layout);
159
+ // Canvas pane
160
+ const canvasWrap = document.createElement("div");
161
+ canvasWrap.className = "op-detail-canvas";
162
+ layout.appendChild(canvasWrap);
163
+ // Sidebar pane
164
+ const sidebar = document.createElement("div");
165
+ sidebar.className = "op-detail-sidebar";
166
+ sidebar.dataset.bpmnHudTheme = cfg.theme;
167
+ layout.appendChild(sidebar);
168
+ // Tabs in sidebar
169
+ const tabs = ["Variables", "Incidents", "Properties"];
170
+ const tabBar = document.createElement("div");
171
+ tabBar.className = "op-detail-tabs";
172
+ const tabPanels = [];
173
+ let activeTab = 0;
174
+ for (let i = 0; i < tabs.length; i++) {
175
+ const btn = document.createElement("button");
176
+ btn.className = `op-detail-tab${i === 0 ? " op-detail-tab--active" : ""}`;
177
+ btn.textContent = tabs[i] ?? "";
178
+ const idx = i;
179
+ btn.addEventListener("click", () => {
180
+ activeTab = idx;
181
+ for (let j = 0; j < tabs.length; j++) {
182
+ const tabEl = tabBar.children[j];
183
+ const panelEl = tabPanels[j];
184
+ if (tabEl) {
185
+ if (j === activeTab)
186
+ tabEl.classList.add("op-detail-tab--active");
187
+ else
188
+ tabEl.classList.remove("op-detail-tab--active");
189
+ }
190
+ if (panelEl) {
191
+ panelEl.style.display = j === activeTab ? "" : "none";
192
+ }
193
+ }
194
+ });
195
+ tabBar.appendChild(btn);
196
+ }
197
+ sidebar.appendChild(tabBar);
198
+ const varPanel = document.createElement("div");
199
+ varPanel.className = "op-detail-panel op-var-panel";
200
+ tabPanels.push(varPanel);
201
+ sidebar.appendChild(varPanel);
202
+ const incPanel = document.createElement("div");
203
+ incPanel.className = "op-detail-panel";
204
+ incPanel.style.display = "none";
205
+ tabPanels.push(incPanel);
206
+ sidebar.appendChild(incPanel);
207
+ const propsPane = document.createElement("div");
208
+ propsPane.className = "op-detail-panel op-props-pane";
209
+ propsPane.style.display = "none";
210
+ tabPanels.push(propsPane);
211
+ sidebar.appendChild(propsPane);
212
+ const propsPlaceholder = document.createElement("div");
213
+ propsPlaceholder.className = "op-props-placeholder";
214
+ propsPlaceholder.textContent = "Click an element to view its properties";
215
+ propsPane.appendChild(propsPlaceholder);
216
+ // Config panel (read-only: applyChange is a no-op)
217
+ let latestDefs = null;
218
+ const configPanel = createConfigPanelPlugin({
219
+ getDefinitions: () => latestDefs,
220
+ applyChange: () => { },
221
+ container: propsPane,
222
+ readonly: true,
223
+ onPanelShow: () => {
224
+ propsPlaceholder.style.display = "none";
225
+ },
226
+ onPanelHide: () => {
227
+ propsPlaceholder.style.display = "";
228
+ },
229
+ });
230
+ const configPanelBpmn = createConfigPanelBpmnPlugin(configPanel);
231
+ const bridgePlugin = {
232
+ name: "op-select-bridge",
233
+ install(api) {
234
+ const emit = api.emit.bind(api);
235
+ api.on("element:click", (id) => emit("editor:select", [id]));
236
+ api.on("diagram:load", (defs) => {
237
+ latestDefs = defs;
238
+ });
239
+ },
240
+ };
241
+ // Token-highlight plugin + canvas
242
+ const tokenHighlight = createTokenHighlightPlugin();
243
+ let canvas = null;
244
+ function loadCanvas(xml) {
245
+ canvas?.destroy();
246
+ canvasWrap.innerHTML = "";
247
+ canvas = new BpmnCanvas({
248
+ container: canvasWrap,
249
+ xml,
250
+ theme: cfg.theme,
251
+ plugins: [tokenHighlight, bridgePlugin, configPanel, configPanelBpmn],
252
+ });
253
+ }
254
+ function applyTokens(activeIds, visitedIds) {
255
+ tokenHighlight.api.setActive(activeIds);
256
+ tokenHighlight.api.addVisited(visitedIds);
257
+ }
258
+ // Instance lookup
259
+ function getInstance() {
260
+ return (instancesStore.state.data?.items.find((i) => i.processInstanceKey === instanceKey) ?? null);
261
+ }
262
+ function renderMeta(inst) {
263
+ meta.innerHTML = "";
264
+ cancelFeedback.style.display = "none";
265
+ if (!inst)
266
+ return;
267
+ const key = document.createElement("span");
268
+ key.className = "op-instance-key";
269
+ key.textContent = inst.processInstanceKey;
270
+ meta.appendChild(key);
271
+ meta.appendChild(badge(inst.state));
272
+ if (inst.businessId) {
273
+ const biz = document.createElement("span");
274
+ biz.className = "op-instance-biz";
275
+ biz.textContent = inst.businessId;
276
+ meta.appendChild(biz);
277
+ }
278
+ const started = document.createElement("span");
279
+ started.className = "op-instance-time";
280
+ started.textContent = `Started ${relTime(inst.startDate)}`;
281
+ meta.appendChild(started);
282
+ if (inst.state === "ACTIVE") {
283
+ const cancelBtn = document.createElement("button");
284
+ cancelBtn.className = "op-action-btn op-action-btn--danger";
285
+ cancelBtn.textContent = "✕ Cancel";
286
+ cancelBtn.style.marginLeft = "auto";
287
+ cancelBtn.addEventListener("click", () => {
288
+ if (cancelBtn.dataset.confirm !== "true") {
289
+ cancelBtn.dataset.confirm = "true";
290
+ cancelBtn.textContent = "Confirm Cancel?";
291
+ cancelBtn.style.fontWeight = "700";
292
+ setTimeout(() => {
293
+ if (cancelBtn.dataset.confirm === "true") {
294
+ cancelBtn.dataset.confirm = "";
295
+ cancelBtn.textContent = "✕ Cancel";
296
+ cancelBtn.style.fontWeight = "";
297
+ }
298
+ }, 4000);
299
+ return;
300
+ }
301
+ cancelBtn.disabled = true;
302
+ cancelBtn.textContent = "Cancelling…";
303
+ if (cfg.mock) {
304
+ cancelFeedback.textContent = "Mock mode — cancel not sent to server.";
305
+ cancelFeedback.className = "op-action-feedback op-action-feedback--ok";
306
+ cancelFeedback.style.display = "";
307
+ return;
308
+ }
309
+ const headers = { "Content-Type": "application/json" };
310
+ if (cfg.profile)
311
+ headers["x-profile"] = cfg.profile;
312
+ fetch(`${cfg.proxyUrl}/api/process-instances/${instanceKey}/cancellation`, {
313
+ method: "POST",
314
+ headers,
315
+ body: JSON.stringify({}),
316
+ })
317
+ .then((r) => {
318
+ if (r.ok || r.status === 204) {
319
+ cancelFeedback.textContent = "Instance cancelled.";
320
+ cancelFeedback.className = "op-action-feedback op-action-feedback--ok";
321
+ }
322
+ else {
323
+ cancelFeedback.textContent = `Error: ${r.status}`;
324
+ cancelFeedback.className = "op-action-feedback op-action-feedback--err";
325
+ cancelBtn.disabled = false;
326
+ cancelBtn.textContent = "✕ Cancel";
327
+ }
328
+ cancelFeedback.style.display = "";
329
+ })
330
+ .catch((err) => {
331
+ cancelFeedback.textContent = String(err);
332
+ cancelFeedback.className = "op-action-feedback op-action-feedback--err";
333
+ cancelFeedback.style.display = "";
334
+ cancelBtn.disabled = false;
335
+ cancelBtn.textContent = "✕ Cancel";
336
+ });
337
+ });
338
+ meta.appendChild(cancelBtn);
339
+ }
340
+ }
341
+ // ── Process chain ────────────────────────────────────────────────────────
342
+ function renderProcessChain(segments) {
343
+ processChainEl.innerHTML = "";
344
+ for (let i = 0; i < segments.length; i++) {
345
+ const seg = segments[i];
346
+ if (!seg)
347
+ continue;
348
+ if (i > 0) {
349
+ const sep = document.createElement("span");
350
+ sep.className = "op-process-chain-sep";
351
+ sep.textContent = " / ";
352
+ processChainEl.appendChild(sep);
353
+ }
354
+ const btn = document.createElement("button");
355
+ btn.className = "op-process-chain-link";
356
+ btn.textContent = seg.name;
357
+ if (cfg.navigate) {
358
+ const key = seg.instanceKey;
359
+ btn.addEventListener("click", () => cfg.navigate?.(`/instances/${key}`));
360
+ }
361
+ else {
362
+ btn.disabled = true;
363
+ }
364
+ processChainEl.appendChild(btn);
365
+ }
366
+ }
367
+ async function fetchProcessChain(startKey) {
368
+ const headers = { accept: "application/json" };
369
+ if (cfg.profile)
370
+ headers["x-profile"] = cfg.profile;
371
+ const chain = [];
372
+ let key = startKey;
373
+ const seen = new Set();
374
+ while (key && !seen.has(key)) {
375
+ seen.add(key);
376
+ try {
377
+ const r = await fetch(`${cfg.proxyUrl}/api/process-instances/${key}`, { headers });
378
+ if (!r.ok)
379
+ break;
380
+ const inst = (await r.json());
381
+ chain.unshift({
382
+ name: inst.processDefinitionName ?? inst.processDefinitionId,
383
+ instanceKey: inst.processInstanceKey,
384
+ });
385
+ key = inst.parentProcessInstanceKey || null;
386
+ }
387
+ catch {
388
+ break;
389
+ }
390
+ }
391
+ if (chain.length > 1)
392
+ renderProcessChain(chain);
393
+ }
394
+ // ── Variables panel ──────────────────────────────────────────────────────
395
+ let allVars = [];
396
+ let varSortDir = "asc";
397
+ let varTypeFilter = "all";
398
+ let varSearch = "";
399
+ let varPanelBuilt = false;
400
+ let varListEl = null;
401
+ let varSortBtn = null;
402
+ const varTypeBtns = new Map();
403
+ function showVarModal(name, value) {
404
+ const overlay = document.createElement("div");
405
+ overlay.className = "op-modal-overlay";
406
+ const dialog = document.createElement("div");
407
+ dialog.className = "op-modal";
408
+ const header = document.createElement("div");
409
+ header.className = "op-modal-header";
410
+ const title = document.createElement("span");
411
+ title.className = "op-modal-title";
412
+ title.textContent = name;
413
+ header.appendChild(title);
414
+ const modalSearch = document.createElement("input");
415
+ modalSearch.type = "search";
416
+ modalSearch.className = "op-modal-search-input";
417
+ modalSearch.placeholder = "Search…";
418
+ header.appendChild(modalSearch);
419
+ const closeBtn = document.createElement("button");
420
+ closeBtn.className = "op-modal-close";
421
+ closeBtn.textContent = "✕";
422
+ closeBtn.addEventListener("click", () => overlay.remove());
423
+ header.appendChild(closeBtn);
424
+ const pre = document.createElement("pre");
425
+ pre.className = "op-modal-body";
426
+ let parsed = null;
427
+ let isJson = false;
428
+ try {
429
+ parsed = JSON.parse(value);
430
+ if (typeof parsed === "object" && parsed !== null)
431
+ isJson = true;
432
+ }
433
+ catch {
434
+ // plain text
435
+ }
436
+ function renderModalContent(query) {
437
+ pre.textContent = "";
438
+ if (isJson) {
439
+ buildJsonDom(pre, parsed, 0);
440
+ }
441
+ else {
442
+ pre.textContent = value;
443
+ }
444
+ if (query)
445
+ highlightTextNodes(pre, query);
446
+ }
447
+ modalSearch.addEventListener("input", () => renderModalContent(modalSearch.value));
448
+ renderModalContent("");
449
+ dialog.appendChild(header);
450
+ dialog.appendChild(pre);
451
+ overlay.appendChild(dialog);
452
+ overlay.addEventListener("click", (e) => {
453
+ if (e.target === overlay)
454
+ overlay.remove();
455
+ });
456
+ el.appendChild(overlay);
457
+ setTimeout(() => modalSearch.focus(), 0);
458
+ }
459
+ function renderVarRows() {
460
+ if (!varListEl)
461
+ return;
462
+ varListEl.innerHTML = "";
463
+ let vars = [...allVars];
464
+ vars.sort((a, b) => {
465
+ const cmp = a.name.localeCompare(b.name);
466
+ return varSortDir === "asc" ? cmp : -cmp;
467
+ });
468
+ if (varTypeFilter !== "all") {
469
+ vars = vars.filter((v) => detectType(v.value) === varTypeFilter);
470
+ }
471
+ if (varSearch) {
472
+ const q = varSearch.toLowerCase();
473
+ vars = vars.filter((v) => v.name.toLowerCase().includes(q) || (v.value ?? "").toLowerCase().includes(q));
474
+ }
475
+ if (vars.length === 0) {
476
+ const empty = document.createElement("div");
477
+ empty.className = "op-panel-empty";
478
+ empty.textContent = "No matches";
479
+ varListEl.appendChild(empty);
480
+ return;
481
+ }
482
+ for (const v of vars) {
483
+ const row = document.createElement("div");
484
+ row.className = "op-var-row";
485
+ const name = document.createElement("span");
486
+ name.className = "op-var-name";
487
+ name.textContent = v.name;
488
+ row.appendChild(name);
489
+ const type = detectType(v.value);
490
+ const typeBadge = document.createElement("span");
491
+ typeBadge.className = `op-var-type op-var-type--${type}`;
492
+ typeBadge.textContent =
493
+ type === "json" ? "{}" : type === "string" ? '""' : type === "null" ? "∅" : type;
494
+ row.appendChild(typeBadge);
495
+ const val = document.createElement("span");
496
+ val.className = "op-var-value";
497
+ val.textContent = v.value ?? "—";
498
+ row.appendChild(val);
499
+ if (v.value) {
500
+ row.classList.add("op-var-row--clickable");
501
+ row.title = "Click to expand";
502
+ row.addEventListener("click", () => showVarModal(v.name, v.value ?? ""));
503
+ }
504
+ varListEl.appendChild(row);
505
+ }
506
+ }
507
+ function buildVarHeader() {
508
+ const controls = document.createElement("div");
509
+ controls.className = "op-var-controls";
510
+ // Sort + type filter row
511
+ const row = document.createElement("div");
512
+ row.className = "op-var-controls-row";
513
+ const sortBtn = document.createElement("button");
514
+ sortBtn.className = "op-var-sort-btn";
515
+ sortBtn.textContent = "↑ Name";
516
+ sortBtn.addEventListener("click", () => {
517
+ varSortDir = varSortDir === "asc" ? "desc" : "asc";
518
+ sortBtn.textContent = varSortDir === "asc" ? "↑ Name" : "↓ Name";
519
+ renderVarRows();
520
+ });
521
+ varSortBtn = sortBtn;
522
+ row.appendChild(sortBtn);
523
+ const sep = document.createElement("span");
524
+ sep.className = "op-var-controls-sep";
525
+ row.appendChild(sep);
526
+ const typeOpts = [
527
+ { label: "All", value: "all" },
528
+ { label: "str", value: "string" },
529
+ { label: "num", value: "number" },
530
+ { label: "bool", value: "boolean" },
531
+ { label: "{}", value: "json" },
532
+ { label: "null", value: "null" },
533
+ ];
534
+ for (const opt of typeOpts) {
535
+ const btn = document.createElement("button");
536
+ btn.className = `op-var-type-btn${varTypeFilter === opt.value ? " op-var-type-btn--active" : ""}`;
537
+ btn.textContent = opt.label;
538
+ btn.addEventListener("click", () => {
539
+ varTypeFilter = opt.value;
540
+ for (const [, b] of varTypeBtns)
541
+ b.classList.remove("op-var-type-btn--active");
542
+ btn.classList.add("op-var-type-btn--active");
543
+ renderVarRows();
544
+ });
545
+ varTypeBtns.set(opt.value, btn);
546
+ row.appendChild(btn);
547
+ }
548
+ controls.appendChild(row);
549
+ const searchInput = document.createElement("input");
550
+ searchInput.type = "search";
551
+ searchInput.className = "op-search";
552
+ searchInput.placeholder = "Search variables…";
553
+ searchInput.value = varSearch;
554
+ searchInput.addEventListener("input", () => {
555
+ varSearch = searchInput.value;
556
+ renderVarRows();
557
+ });
558
+ controls.appendChild(searchInput);
559
+ varPanel.appendChild(controls);
560
+ const list = document.createElement("div");
561
+ list.className = "op-var-list";
562
+ varListEl = list;
563
+ varPanel.appendChild(list);
564
+ }
565
+ function deduplicateVars(vars) {
566
+ const map = new Map();
567
+ for (const v of vars) {
568
+ const existing = map.get(v.name);
569
+ if (!existing || BigInt(v.variableKey) > BigInt(existing.variableKey)) {
570
+ map.set(v.name, v);
571
+ }
572
+ }
573
+ return Array.from(map.values());
574
+ }
575
+ function renderVariables(vars) {
576
+ allVars = deduplicateVars(vars);
577
+ if (!varPanelBuilt) {
578
+ varPanel.innerHTML = "";
579
+ if (vars.length === 0) {
580
+ varPanel.innerHTML = `<div class="op-panel-empty">No variables</div>`;
581
+ return;
582
+ }
583
+ buildVarHeader();
584
+ varPanelBuilt = true;
585
+ }
586
+ else if (varSortBtn) {
587
+ // Update sort button text in case direction was preserved
588
+ varSortBtn.textContent = varSortDir === "asc" ? "↑ Name" : "↓ Name";
589
+ }
590
+ renderVarRows();
591
+ }
592
+ // ── Incidents sub-store ──────────────────────────────────────────────────
593
+ const incStore = new IncidentsStore();
594
+ incStore.connect(cfg.proxyUrl, cfg.profile, cfg.interval, cfg.mock, instanceKey);
595
+ function renderIncidents() {
596
+ incPanel.innerHTML = "";
597
+ const items = incStore.state.data?.items ?? [];
598
+ if (items.length === 0) {
599
+ incPanel.innerHTML = `<div class="op-panel-empty">No incidents</div>`;
600
+ return;
601
+ }
602
+ for (const inc of items) {
603
+ const row = document.createElement("div");
604
+ row.className = "op-incident-row";
605
+ const type = document.createElement("span");
606
+ type.className = "op-incident-type";
607
+ type.textContent = inc.errorType ?? "UNKNOWN";
608
+ row.appendChild(type);
609
+ const msg = document.createElement("span");
610
+ msg.className = "op-incident-msg";
611
+ msg.textContent = inc.errorMessage ?? "—";
612
+ row.appendChild(msg);
613
+ row.appendChild(badge(inc.state ?? "UNKNOWN"));
614
+ incPanel.appendChild(row);
615
+ }
616
+ }
617
+ const incUnsub = incStore.subscribe(renderIncidents);
618
+ // ── Instance data loading ────────────────────────────────────────────────
619
+ let instUnsub;
620
+ if (cfg.mock) {
621
+ loadCanvas(MOCK_BPMN_XML);
622
+ applyTokens(MOCK_ACTIVE_ELEMENTS, MOCK_VISITED_ELEMENTS);
623
+ renderVariables(MOCK_VARIABLES.filter((v) => v.processInstanceKey === instanceKey));
624
+ setTimeout(() => applyTokens(MOCK_ACTIVE_ELEMENTS, MOCK_VISITED_ELEMENTS), 100);
625
+ const mockInst = {
626
+ processInstanceKey: instanceKey,
627
+ processDefinitionKey: "pd-1",
628
+ processDefinitionId: "order-process",
629
+ processDefinitionName: "Order Processing",
630
+ state: "ACTIVE",
631
+ hasIncident: false,
632
+ businessId: "ORD-10042",
633
+ startDate: new Date(Date.now() - 2 * 3_600_000).toISOString(),
634
+ endDate: null,
635
+ };
636
+ renderMeta(mockInst);
637
+ instUnsub = instancesStore.subscribe(() => renderMeta(getInstance()));
638
+ }
639
+ else {
640
+ let xmlStarted = false;
641
+ function startXmlFetch(inst) {
642
+ if (xmlStarted)
643
+ return;
644
+ xmlStarted = true;
645
+ renderMeta(inst);
646
+ fetchProcessChain(inst.processInstanceKey).catch(() => { });
647
+ const pdKey = inst.processDefinitionKey;
648
+ fetch(`${cfg.proxyUrl}/api/process-definitions/${pdKey}/xml`, {
649
+ headers: {
650
+ accept: "text/xml",
651
+ ...(cfg.profile ? { "x-profile": cfg.profile } : {}),
652
+ },
653
+ })
654
+ .then((r) => r.text())
655
+ .then((xml) => {
656
+ loadCanvas(xml);
657
+ return fetch(`${cfg.proxyUrl}/api/element-instances/search`, {
658
+ method: "POST",
659
+ headers: {
660
+ "Content-Type": "application/json",
661
+ ...(cfg.profile ? { "x-profile": cfg.profile } : {}),
662
+ },
663
+ body: JSON.stringify({ filter: { processInstanceKey: instanceKey } }),
664
+ });
665
+ })
666
+ .then((r) => r.json())
667
+ .then((result) => {
668
+ const activeIds = result.items.filter((e) => e.state === "ACTIVE").map((e) => e.elementId);
669
+ const visitedIds = result.items
670
+ .filter((e) => e.state !== "ACTIVE")
671
+ .map((e) => e.elementId);
672
+ applyTokens(activeIds, visitedIds);
673
+ })
674
+ .catch(() => {
675
+ // canvas still shows without tokens
676
+ });
677
+ fetch(`${cfg.proxyUrl}/api/variables/search`, {
678
+ method: "POST",
679
+ headers: {
680
+ "Content-Type": "application/json",
681
+ ...(cfg.profile ? { "x-profile": cfg.profile } : {}),
682
+ },
683
+ body: JSON.stringify({ filter: { processInstanceKey: instanceKey } }),
684
+ })
685
+ .then((r) => r.json())
686
+ .then((result) => renderVariables(result.items))
687
+ .catch(() => renderVariables([]));
688
+ }
689
+ // Try immediately if store already has data
690
+ const existing = getInstance();
691
+ if (existing) {
692
+ startXmlFetch(existing);
693
+ }
694
+ else {
695
+ // Deep-link: instance not in store yet — fetch it directly
696
+ fetch(`${cfg.proxyUrl}/api/process-instances/${instanceKey}`, {
697
+ headers: { ...(cfg.profile ? { "x-profile": cfg.profile } : {}) },
698
+ })
699
+ .then((r) => (r.ok ? r.json() : null))
700
+ .then((inst) => {
701
+ if (inst && !xmlStarted)
702
+ startXmlFetch(inst);
703
+ })
704
+ .catch(() => { });
705
+ }
706
+ instUnsub = instancesStore.subscribe(() => {
707
+ const inst = getInstance();
708
+ if (inst)
709
+ startXmlFetch(inst);
710
+ renderMeta(inst);
711
+ });
712
+ }
713
+ renderIncidents();
714
+ return {
715
+ el,
716
+ setTheme(t) {
717
+ canvas?.setTheme(t);
718
+ sidebar.dataset.bpmnHudTheme = t;
719
+ },
720
+ destroy() {
721
+ canvas?.destroy();
722
+ instUnsub();
723
+ incUnsub();
724
+ incStore.destroy();
725
+ },
726
+ };
727
+ }
728
+ //# sourceMappingURL=instance-detail.js.map