@bpmnkit/plugins 0.0.16 → 0.0.18

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.
@@ -0,0 +1,479 @@
1
+ import { injectLiveModeStyles } from "./css.js";
2
+ const DB_NAME = "bpmnkit-live-mode-v1";
3
+ const STORE_NAME = "live-state";
4
+ function openDb() {
5
+ return new Promise((resolve, reject) => {
6
+ const req = indexedDB.open(DB_NAME, 1);
7
+ req.onupgradeneeded = () => {
8
+ req.result.createObjectStore(STORE_NAME);
9
+ };
10
+ req.onsuccess = () => resolve(req.result);
11
+ req.onerror = () => reject(req.error);
12
+ });
13
+ }
14
+ async function loadLiveState(key) {
15
+ try {
16
+ const db = await openDb();
17
+ return new Promise((resolve, reject) => {
18
+ const tx = db.transaction(STORE_NAME, "readonly");
19
+ const req = tx.objectStore(STORE_NAME).get(key);
20
+ req.onsuccess = () => resolve(req.result ?? null);
21
+ req.onerror = () => reject(req.error);
22
+ });
23
+ }
24
+ catch {
25
+ return null;
26
+ }
27
+ }
28
+ async function saveLiveState(key, record) {
29
+ try {
30
+ const db = await openDb();
31
+ await new Promise((resolve, reject) => {
32
+ const tx = db.transaction(STORE_NAME, "readwrite");
33
+ const req = tx.objectStore(STORE_NAME).put(record, key);
34
+ req.onsuccess = () => resolve();
35
+ req.onerror = () => reject(req.error);
36
+ });
37
+ }
38
+ catch {
39
+ // ignore persistence errors
40
+ }
41
+ }
42
+ async function clearLiveState(key) {
43
+ try {
44
+ const db = await openDb();
45
+ await new Promise((resolve, reject) => {
46
+ const tx = db.transaction(STORE_NAME, "readwrite");
47
+ const req = tx.objectStore(STORE_NAME).delete(key);
48
+ req.onsuccess = () => resolve();
49
+ req.onerror = () => reject(req.error);
50
+ });
51
+ }
52
+ catch {
53
+ // ignore
54
+ }
55
+ }
56
+ // ── Plugin factory ────────────────────────────────────────────────────────────
57
+ export function createLiveModePlugin(options) {
58
+ const proxyUrl = (options.proxyUrl ?? "http://localhost:3033").replace(/\/$/, "");
59
+ const pollIntervalMs = options.pollIntervalMs ?? 3000;
60
+ let canvasApi = null;
61
+ const unsubs = [];
62
+ let _status = "off";
63
+ let _enabled = false;
64
+ let _profile = null;
65
+ let _instanceKey = null;
66
+ let _processDefinitionKey = null;
67
+ let _bpmnProcessId = null;
68
+ let _currentDefs = null;
69
+ let _activeElementIds = new Set();
70
+ let _pollTimer = null;
71
+ let _deployDebounce = null;
72
+ // ── Conflict banner ───────────────────────────────────────────────────────
73
+ let _conflictBanner = null;
74
+ function removeConflictBanner() {
75
+ if (_conflictBanner) {
76
+ _conflictBanner.remove();
77
+ _conflictBanner = null;
78
+ }
79
+ }
80
+ function showConflictBanner(conflictIds, onFresh) {
81
+ removeConflictBanner();
82
+ const container = canvasApi?.container;
83
+ if (!container)
84
+ return;
85
+ const banner = document.createElement("div");
86
+ banner.className = "bpmnkit-live-conflict";
87
+ const title = document.createElement("div");
88
+ title.className = "bpmnkit-live-conflict-title";
89
+ title.textContent = "Migration conflict — instance is at removed elements";
90
+ banner.appendChild(title);
91
+ const list = document.createElement("ul");
92
+ list.className = "bpmnkit-live-conflict-list";
93
+ for (const id of conflictIds) {
94
+ const item = document.createElement("li");
95
+ item.className = "bpmnkit-live-conflict-item";
96
+ item.textContent = id;
97
+ list.appendChild(item);
98
+ }
99
+ banner.appendChild(list);
100
+ const btn = document.createElement("button");
101
+ btn.className = "bpmnkit-live-btn";
102
+ btn.textContent = "Start fresh";
103
+ btn.addEventListener("click", () => {
104
+ removeConflictBanner();
105
+ onFresh();
106
+ });
107
+ banner.appendChild(btn);
108
+ container.appendChild(banner);
109
+ _conflictBanner = banner;
110
+ }
111
+ // ── Variable inspector tooltip ────────────────────────────────────────────
112
+ const tooltipEl = document.createElement("div");
113
+ tooltipEl.className = "bpmnkit-live-vars-tooltip";
114
+ tooltipEl.style.display = "none";
115
+ document.body.appendChild(tooltipEl);
116
+ let _tooltipDebounce = null;
117
+ let _tooltipElementId = null;
118
+ const _varsCache = new Map();
119
+ function hideVarsTooltip() {
120
+ tooltipEl.style.display = "none";
121
+ _tooltipElementId = null;
122
+ }
123
+ function showVarsTooltip(vars, x, y) {
124
+ while (tooltipEl.firstChild !== null)
125
+ tooltipEl.removeChild(tooltipEl.firstChild);
126
+ if (vars.length === 0) {
127
+ hideVarsTooltip();
128
+ return;
129
+ }
130
+ for (const v of vars) {
131
+ const row = document.createElement("div");
132
+ row.className = "bpmnkit-live-vars-row";
133
+ const nameEl = document.createElement("span");
134
+ nameEl.className = "bpmnkit-live-vars-name";
135
+ nameEl.textContent = v.name;
136
+ const valEl = document.createElement("span");
137
+ valEl.className = "bpmnkit-live-vars-value";
138
+ valEl.textContent = typeof v.value === "object" ? JSON.stringify(v.value) : String(v.value);
139
+ row.appendChild(nameEl);
140
+ row.appendChild(valEl);
141
+ tooltipEl.appendChild(row);
142
+ }
143
+ tooltipEl.style.display = "block";
144
+ tooltipEl.style.left = `${x + 12}px`;
145
+ tooltipEl.style.top = `${y + 12}px`;
146
+ }
147
+ function onMouseMove(e) {
148
+ if (!_enabled || _instanceKey === null) {
149
+ hideVarsTooltip();
150
+ return;
151
+ }
152
+ const target = e.target?.closest("[data-bpmnkit-id]");
153
+ const elementId = target?.getAttribute("data-bpmnkit-id") ?? null;
154
+ if (!elementId || !_activeElementIds.has(elementId)) {
155
+ hideVarsTooltip();
156
+ return;
157
+ }
158
+ if (_tooltipElementId === elementId)
159
+ return;
160
+ _tooltipElementId = elementId;
161
+ if (_tooltipDebounce !== null)
162
+ clearTimeout(_tooltipDebounce);
163
+ _tooltipDebounce = setTimeout(async () => {
164
+ if (_tooltipElementId !== elementId || _instanceKey === null)
165
+ return;
166
+ try {
167
+ const cached = _varsCache.get(elementId);
168
+ if (cached !== undefined) {
169
+ showVarsTooltip(cached, e.clientX, e.clientY);
170
+ return;
171
+ }
172
+ const result = await proxyPost("/api/v2/variables/search", { filter: { processInstanceKey: _instanceKey } });
173
+ const vars = result.items ?? [];
174
+ _varsCache.set(elementId, vars);
175
+ if (_tooltipElementId === elementId) {
176
+ showVarsTooltip(vars, e.clientX, e.clientY);
177
+ }
178
+ }
179
+ catch {
180
+ hideVarsTooltip();
181
+ }
182
+ }, 300);
183
+ }
184
+ // ── Proxy helpers ─────────────────────────────────────────────────────────
185
+ async function proxyGet(path) {
186
+ const headers = { accept: "application/json" };
187
+ if (_profile !== null)
188
+ headers["x-profile"] = _profile;
189
+ const res = await fetch(`${proxyUrl}${path}`, { headers });
190
+ if (!res.ok)
191
+ throw new Error(`HTTP ${res.status}`);
192
+ return res.json();
193
+ }
194
+ async function proxyPost(path, body) {
195
+ const headers = {
196
+ "content-type": "application/json",
197
+ accept: "application/json",
198
+ };
199
+ if (_profile !== null)
200
+ headers["x-profile"] = _profile;
201
+ const res = await fetch(`${proxyUrl}${path}`, {
202
+ method: "POST",
203
+ headers,
204
+ body: JSON.stringify(body),
205
+ });
206
+ if (!res.ok) {
207
+ const text = await res.text();
208
+ throw new Error(`HTTP ${res.status}: ${text}`);
209
+ }
210
+ return res.json();
211
+ }
212
+ async function proxyPostMultipart(path, form) {
213
+ const headers = { accept: "application/json" };
214
+ if (_profile !== null)
215
+ headers["x-profile"] = _profile;
216
+ const res = await fetch(`${proxyUrl}${path}`, {
217
+ method: "POST",
218
+ headers,
219
+ body: form,
220
+ });
221
+ if (!res.ok) {
222
+ const text = await res.text();
223
+ throw new Error(`HTTP ${res.status}: ${text}`);
224
+ }
225
+ return res.json();
226
+ }
227
+ // ── Status management ─────────────────────────────────────────────────────
228
+ const toggleEl = document.createElement("button");
229
+ toggleEl.className = "bpmnkit-live-toggle";
230
+ toggleEl.textContent = "⚡ Live";
231
+ const statusEl = document.createElement("span");
232
+ statusEl.className = "bpmnkit-live-status bpmnkit-live-status--off";
233
+ statusEl.textContent = "OFF";
234
+ function setStatus(s) {
235
+ _status = s;
236
+ options.onStatusChange?.(s);
237
+ // Update toggle class
238
+ toggleEl.className = "bpmnkit-live-toggle";
239
+ if (s === "live")
240
+ toggleEl.classList.add("bpmnkit-live-toggle--on");
241
+ if (s === "blocked-production" || s === "tests-failing" || s === "error")
242
+ toggleEl.classList.add("bpmnkit-live-toggle--blocked");
243
+ // Update status pill
244
+ const labelMap = {
245
+ off: "OFF",
246
+ connecting: "CONNECTING",
247
+ live: "LIVE",
248
+ error: "ERROR",
249
+ "blocked-production": "BLOCKED",
250
+ "tests-failing": "TESTS FAIL",
251
+ };
252
+ statusEl.textContent = labelMap[s];
253
+ statusEl.className = "bpmnkit-live-status";
254
+ if (s === "off")
255
+ statusEl.classList.add("bpmnkit-live-status--off");
256
+ else if (s === "connecting")
257
+ statusEl.classList.add("bpmnkit-live-status--connecting");
258
+ else if (s === "live")
259
+ statusEl.classList.add("bpmnkit-live-status--live");
260
+ else if (s === "error")
261
+ statusEl.classList.add("bpmnkit-live-status--error");
262
+ else
263
+ statusEl.classList.add("bpmnkit-live-status--blocked");
264
+ }
265
+ // ── Start new instance ────────────────────────────────────────────────────
266
+ async function startNewInstance(bpmnProcessId, stateKey) {
267
+ const res = await proxyPost("/api/v2/process-instances", { bpmnProcessId, variables: {} });
268
+ const key = res.processInstanceKey ?? res.key ?? "";
269
+ if (!key)
270
+ throw new Error("No instanceKey returned from start-instance");
271
+ await saveLiveState(stateKey, {
272
+ processDefinitionKey: _processDefinitionKey ?? "",
273
+ instanceKey: key,
274
+ });
275
+ return key;
276
+ }
277
+ // ── Polling ───────────────────────────────────────────────────────────────
278
+ function stopPolling() {
279
+ if (_pollTimer !== null) {
280
+ clearInterval(_pollTimer);
281
+ _pollTimer = null;
282
+ }
283
+ }
284
+ function startPolling() {
285
+ stopPolling();
286
+ _pollTimer = setInterval(async () => {
287
+ if (!_enabled || _instanceKey === null)
288
+ return;
289
+ try {
290
+ const res = await proxyPost("/api/v2/element-instances/search", {
291
+ filter: { processInstanceKey: _instanceKey, state: "ACTIVE" },
292
+ });
293
+ const ids = (res.items ?? []).flatMap((item) => item.elementId !== undefined ? [item.elementId] : []);
294
+ _activeElementIds = new Set(ids);
295
+ options.tokenHighlight?.api.setActive(ids);
296
+ options.tokenHighlight?.api.addVisited(ids);
297
+ _varsCache.clear();
298
+ }
299
+ catch {
300
+ // Instance may be gone — try to start a new one
301
+ if (_bpmnProcessId !== null && _profile !== null) {
302
+ const stateKey = `${_profile}:${_bpmnProcessId}`;
303
+ try {
304
+ await clearLiveState(stateKey);
305
+ const newKey = await startNewInstance(_bpmnProcessId, stateKey);
306
+ _instanceKey = newKey;
307
+ }
308
+ catch {
309
+ // give up polling silently
310
+ }
311
+ }
312
+ }
313
+ }, pollIntervalMs);
314
+ }
315
+ // ── Migration ─────────────────────────────────────────────────────────────
316
+ async function attemptMigration(instanceKey, targetProcessDefinitionKey, currentDefs) {
317
+ // Build mapping instructions using current element IDs
318
+ const currentProcess = currentDefs?.processes[0];
319
+ const currentElementIds = currentProcess
320
+ ? new Set(currentProcess.flowElements.map((el) => el.id))
321
+ : new Set();
322
+ const mappingInstructions = [...currentElementIds].map((id) => ({
323
+ sourceElementId: id,
324
+ targetElementId: id,
325
+ }));
326
+ await proxyPost(`/api/v2/process-instances/${instanceKey}/migration`, {
327
+ targetProcessDefinitionKey,
328
+ mappingInstructions,
329
+ });
330
+ }
331
+ // ── Deploy flow ───────────────────────────────────────────────────────────
332
+ async function deploy() {
333
+ try {
334
+ const profile = options.getProfile?.() ?? null;
335
+ if (profile?.isProduction === true) {
336
+ setStatus("blocked-production");
337
+ return;
338
+ }
339
+ _profile = profile?.name ?? null;
340
+ if (options.requireTestsGreen === true && options.runTests !== undefined) {
341
+ const passed = await options.runTests();
342
+ if (!passed) {
343
+ setStatus("tests-failing");
344
+ return;
345
+ }
346
+ }
347
+ setStatus("connecting");
348
+ const xml = options.getXml();
349
+ if (!xml)
350
+ throw new Error("No XML available");
351
+ const fileName = options.getFileName?.() ?? "process.bpmn";
352
+ const form = new FormData();
353
+ form.append("resources", new Blob([xml], { type: "application/xml" }), fileName);
354
+ const deployResult = await proxyPostMultipart("/api/deployments", form);
355
+ const firstProcess = deployResult.processes?.[0];
356
+ const newProcessDefinitionKey = firstProcess?.processDefinitionKey ?? "";
357
+ const newBpmnProcessId = firstProcess?.bpmnProcessId ?? "";
358
+ _processDefinitionKey = newProcessDefinitionKey;
359
+ _bpmnProcessId = newBpmnProcessId;
360
+ const stateKey = `${_profile ?? "_"}:${newBpmnProcessId}`;
361
+ const stored = await loadLiveState(stateKey);
362
+ if (stored?.instanceKey) {
363
+ // Try migration
364
+ try {
365
+ await attemptMigration(stored.instanceKey, newProcessDefinitionKey, _currentDefs);
366
+ _instanceKey = stored.instanceKey;
367
+ await saveLiveState(stateKey, {
368
+ processDefinitionKey: newProcessDefinitionKey,
369
+ instanceKey: stored.instanceKey,
370
+ });
371
+ }
372
+ catch {
373
+ // Migration failed — show conflict banner and start fresh
374
+ removeConflictBanner();
375
+ const currentProcess = _currentDefs?.processes[0];
376
+ const currentIds = new Set(currentProcess?.flowElements.map((el) => el.id) ?? []);
377
+ const conflicts = [...currentIds].filter((id) => !currentIds.has(id));
378
+ showConflictBanner(conflicts.length > 0 ? conflicts : [stored.instanceKey], async () => {
379
+ await clearLiveState(stateKey);
380
+ try {
381
+ const newKey = await startNewInstance(newBpmnProcessId, stateKey);
382
+ _instanceKey = newKey;
383
+ startPolling();
384
+ }
385
+ catch {
386
+ setStatus("error");
387
+ }
388
+ });
389
+ const newKey = await startNewInstance(newBpmnProcessId, stateKey);
390
+ _instanceKey = newKey;
391
+ }
392
+ }
393
+ else {
394
+ const newKey = await startNewInstance(newBpmnProcessId, stateKey);
395
+ _instanceKey = newKey;
396
+ }
397
+ setStatus("live");
398
+ startPolling();
399
+ }
400
+ catch {
401
+ setStatus("error");
402
+ }
403
+ }
404
+ function scheduleDeploy() {
405
+ if (!_enabled)
406
+ return;
407
+ if (_deployDebounce !== null)
408
+ clearTimeout(_deployDebounce);
409
+ _deployDebounce = setTimeout(() => {
410
+ deploy().catch(() => setStatus("error"));
411
+ }, 500);
412
+ }
413
+ // ── Toggle logic ──────────────────────────────────────────────────────────
414
+ function enable() {
415
+ _enabled = true;
416
+ scheduleDeploy();
417
+ }
418
+ function disable() {
419
+ _enabled = false;
420
+ stopPolling();
421
+ if (_deployDebounce !== null)
422
+ clearTimeout(_deployDebounce);
423
+ options.tokenHighlight?.api.clear();
424
+ _activeElementIds.clear();
425
+ _instanceKey = null;
426
+ setStatus("off");
427
+ removeConflictBanner();
428
+ hideVarsTooltip();
429
+ }
430
+ toggleEl.addEventListener("click", () => {
431
+ if (_enabled) {
432
+ disable();
433
+ }
434
+ else {
435
+ enable();
436
+ }
437
+ });
438
+ // ── Canvas install/uninstall ──────────────────────────────────────────────
439
+ return {
440
+ name: "live-mode",
441
+ toggle: toggleEl,
442
+ status: statusEl,
443
+ disable,
444
+ install(api) {
445
+ canvasApi = api;
446
+ injectLiveModeStyles();
447
+ // Listen for diagram changes
448
+ const onAny = api.on;
449
+ unsubs.push(onAny("diagram:load", (arg) => {
450
+ const defs = arg?.definitions ?? null;
451
+ _currentDefs = defs;
452
+ if (_enabled)
453
+ scheduleDeploy();
454
+ }));
455
+ unsubs.push(onAny("diagram:change", (arg) => {
456
+ const defs = arg?.definitions ?? null;
457
+ if (defs !== null)
458
+ _currentDefs = defs;
459
+ if (_enabled)
460
+ scheduleDeploy();
461
+ }));
462
+ // Variable inspector mousemove
463
+ const viewport = api.container;
464
+ if (viewport) {
465
+ viewport.addEventListener("mousemove", onMouseMove);
466
+ unsubs.push(() => viewport.removeEventListener("mousemove", onMouseMove));
467
+ }
468
+ },
469
+ uninstall() {
470
+ disable();
471
+ for (const unsub of unsubs)
472
+ unsub();
473
+ unsubs.length = 0;
474
+ tooltipEl.remove();
475
+ canvasApi = null;
476
+ },
477
+ };
478
+ }
479
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,4 @@
1
+ export declare const STYLE_ID = "bpmnkit-pattern-advisor-v1";
2
+ export declare const CSS = "\n/* \u2500\u2500 Side panel \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.bpmnkit-pa-panel {\n display: flex;\n flex-direction: column;\n height: 100%;\n font-family: var(--bpmnkit-font, system-ui, -apple-system, sans-serif);\n font-size: 13px;\n color: var(--bpmnkit-fg, #1a1a2e);\n background: var(--bpmnkit-surface, #ffffff);\n}\n\n.bpmnkit-pa-header {\n padding: 10px 12px 8px;\n border-bottom: 1px solid var(--bpmnkit-border, #d0d0e8);\n flex-shrink: 0;\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n}\n\n.bpmnkit-pa-title {\n font-size: 13px;\n font-weight: 600;\n color: var(--bpmnkit-fg, #1a1a2e);\n margin: 0;\n}\n\n.bpmnkit-pa-counts {\n display: flex;\n gap: 6px;\n align-items: center;\n}\n\n.bpmnkit-pa-badge {\n font-size: 11px;\n font-weight: 700;\n padding: 1px 5px;\n border-radius: 4px;\n letter-spacing: 0.02em;\n}\n.bpmnkit-pa-badge-error {\n background: rgba(220, 50, 50, 0.15);\n color: var(--bpmnkit-danger, #dc2626);\n}\n.bpmnkit-pa-badge-warning {\n background: rgba(217, 119, 6, 0.15);\n color: var(--bpmnkit-warn, #d97706);\n}\n.bpmnkit-pa-badge-info {\n background: rgba(26, 86, 219, 0.12);\n color: var(--bpmnkit-accent, #1a56db);\n}\n\n.bpmnkit-pa-body {\n flex: 1;\n overflow-y: auto;\n padding: 4px 0 8px;\n}\n\n.bpmnkit-pa-empty {\n padding: 24px 16px;\n text-align: center;\n color: var(--bpmnkit-fg-muted, #6666a0);\n font-size: 12px;\n line-height: 1.5;\n}\n\n/* \u2500\u2500 Finding group \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.bpmnkit-pa-group {\n padding: 6px 0 0;\n border-bottom: 1px solid var(--bpmnkit-border, #d0d0e8);\n margin-bottom: 0;\n}\n.bpmnkit-pa-group:last-child {\n border-bottom: none;\n}\n\n.bpmnkit-pa-group-header {\n padding: 2px 12px 4px;\n font-size: 11px;\n font-weight: 600;\n color: var(--bpmnkit-fg-muted, #6666a0);\n text-transform: uppercase;\n letter-spacing: 0.04em;\n}\n\n/* \u2500\u2500 Individual finding \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.bpmnkit-pa-finding {\n display: flex;\n align-items: flex-start;\n gap: 8px;\n padding: 6px 12px;\n}\n\n.bpmnkit-pa-severity {\n flex-shrink: 0;\n font-size: 10px;\n font-weight: 700;\n letter-spacing: 0.04em;\n text-transform: uppercase;\n padding: 2px 5px;\n border-radius: 4px;\n margin-top: 1px;\n white-space: nowrap;\n}\n.bpmnkit-pa-severity-error {\n background: rgba(220, 50, 50, 0.2);\n color: var(--bpmnkit-danger, #dc2626);\n}\n.bpmnkit-pa-severity-warning {\n background: rgba(217, 119, 6, 0.2);\n color: var(--bpmnkit-warn, #d97706);\n}\n.bpmnkit-pa-severity-info {\n background: rgba(26, 86, 219, 0.15);\n color: var(--bpmnkit-accent, #1a56db);\n}\n\n.bpmnkit-pa-finding-body {\n flex: 1;\n min-width: 0;\n}\n\n.bpmnkit-pa-finding-msg {\n color: var(--bpmnkit-fg, #1a1a2e);\n line-height: 1.45;\n word-break: break-word;\n font-size: 12.5px;\n}\n\n.bpmnkit-pa-finding-sug {\n color: var(--bpmnkit-fg-muted, #6666a0);\n font-size: 11.5px;\n margin-top: 2px;\n line-height: 1.4;\n}\n\n.bpmnkit-pa-finding-actions {\n display: flex;\n gap: 6px;\n margin-top: 5px;\n flex-wrap: wrap;\n}\n\n.bpmnkit-pa-btn {\n font-size: 11px;\n padding: 2px 8px;\n border-radius: 4px;\n border: 1px solid var(--bpmnkit-border, #d0d0e8);\n background: var(--bpmnkit-surface-2, #eeeef8);\n color: var(--bpmnkit-fg, #1a1a2e);\n cursor: pointer;\n white-space: nowrap;\n}\n.bpmnkit-pa-btn:hover {\n background: var(--bpmnkit-accent-subtle, rgba(26,86,219,0.12));\n border-color: var(--bpmnkit-accent, #1a56db);\n color: var(--bpmnkit-accent, #1a56db);\n}\n\n.bpmnkit-pa-btn-fix {\n border-color: var(--bpmnkit-accent, #1a56db);\n color: var(--bpmnkit-accent, #1a56db);\n}\n.bpmnkit-pa-btn-fix:hover {\n background: var(--bpmnkit-accent, #1a56db);\n color: #fff;\n}\n\n/* \u2500\u2500 Canvas element badges \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.bpmnkit-pa-error-ring > .bpmnkit-shape-body,\n.bpmnkit-pa-error-ring > rect:first-child {\n stroke: var(--bpmnkit-danger, #dc2626) !important;\n stroke-width: 2.5px !important;\n}\n.bpmnkit-pa-warning-ring > .bpmnkit-shape-body,\n.bpmnkit-pa-warning-ring > rect:first-child {\n stroke: var(--bpmnkit-warn, #d97706) !important;\n stroke-width: 2px !important;\n}\n.bpmnkit-pa-info-ring > .bpmnkit-shape-body,\n.bpmnkit-pa-info-ring > rect:first-child {\n stroke: var(--bpmnkit-accent, #1a56db) !important;\n stroke-width: 1.5px !important;\n}\n";
3
+ export declare function injectPatternAdvisorStyles(): void;
4
+ //# sourceMappingURL=css.d.ts.map
@@ -0,0 +1,198 @@
1
+ export const STYLE_ID = "bpmnkit-pattern-advisor-v1";
2
+ export const CSS = `
3
+ /* ── Side panel ───────────────────────────────────────────────────────────── */
4
+ .bpmnkit-pa-panel {
5
+ display: flex;
6
+ flex-direction: column;
7
+ height: 100%;
8
+ font-family: var(--bpmnkit-font, system-ui, -apple-system, sans-serif);
9
+ font-size: 13px;
10
+ color: var(--bpmnkit-fg, #1a1a2e);
11
+ background: var(--bpmnkit-surface, #ffffff);
12
+ }
13
+
14
+ .bpmnkit-pa-header {
15
+ padding: 10px 12px 8px;
16
+ border-bottom: 1px solid var(--bpmnkit-border, #d0d0e8);
17
+ flex-shrink: 0;
18
+ display: flex;
19
+ align-items: center;
20
+ justify-content: space-between;
21
+ gap: 8px;
22
+ }
23
+
24
+ .bpmnkit-pa-title {
25
+ font-size: 13px;
26
+ font-weight: 600;
27
+ color: var(--bpmnkit-fg, #1a1a2e);
28
+ margin: 0;
29
+ }
30
+
31
+ .bpmnkit-pa-counts {
32
+ display: flex;
33
+ gap: 6px;
34
+ align-items: center;
35
+ }
36
+
37
+ .bpmnkit-pa-badge {
38
+ font-size: 11px;
39
+ font-weight: 700;
40
+ padding: 1px 5px;
41
+ border-radius: 4px;
42
+ letter-spacing: 0.02em;
43
+ }
44
+ .bpmnkit-pa-badge-error {
45
+ background: rgba(220, 50, 50, 0.15);
46
+ color: var(--bpmnkit-danger, #dc2626);
47
+ }
48
+ .bpmnkit-pa-badge-warning {
49
+ background: rgba(217, 119, 6, 0.15);
50
+ color: var(--bpmnkit-warn, #d97706);
51
+ }
52
+ .bpmnkit-pa-badge-info {
53
+ background: rgba(26, 86, 219, 0.12);
54
+ color: var(--bpmnkit-accent, #1a56db);
55
+ }
56
+
57
+ .bpmnkit-pa-body {
58
+ flex: 1;
59
+ overflow-y: auto;
60
+ padding: 4px 0 8px;
61
+ }
62
+
63
+ .bpmnkit-pa-empty {
64
+ padding: 24px 16px;
65
+ text-align: center;
66
+ color: var(--bpmnkit-fg-muted, #6666a0);
67
+ font-size: 12px;
68
+ line-height: 1.5;
69
+ }
70
+
71
+ /* ── Finding group ─────────────────────────────────────────────────────────── */
72
+ .bpmnkit-pa-group {
73
+ padding: 6px 0 0;
74
+ border-bottom: 1px solid var(--bpmnkit-border, #d0d0e8);
75
+ margin-bottom: 0;
76
+ }
77
+ .bpmnkit-pa-group:last-child {
78
+ border-bottom: none;
79
+ }
80
+
81
+ .bpmnkit-pa-group-header {
82
+ padding: 2px 12px 4px;
83
+ font-size: 11px;
84
+ font-weight: 600;
85
+ color: var(--bpmnkit-fg-muted, #6666a0);
86
+ text-transform: uppercase;
87
+ letter-spacing: 0.04em;
88
+ }
89
+
90
+ /* ── Individual finding ────────────────────────────────────────────────────── */
91
+ .bpmnkit-pa-finding {
92
+ display: flex;
93
+ align-items: flex-start;
94
+ gap: 8px;
95
+ padding: 6px 12px;
96
+ }
97
+
98
+ .bpmnkit-pa-severity {
99
+ flex-shrink: 0;
100
+ font-size: 10px;
101
+ font-weight: 700;
102
+ letter-spacing: 0.04em;
103
+ text-transform: uppercase;
104
+ padding: 2px 5px;
105
+ border-radius: 4px;
106
+ margin-top: 1px;
107
+ white-space: nowrap;
108
+ }
109
+ .bpmnkit-pa-severity-error {
110
+ background: rgba(220, 50, 50, 0.2);
111
+ color: var(--bpmnkit-danger, #dc2626);
112
+ }
113
+ .bpmnkit-pa-severity-warning {
114
+ background: rgba(217, 119, 6, 0.2);
115
+ color: var(--bpmnkit-warn, #d97706);
116
+ }
117
+ .bpmnkit-pa-severity-info {
118
+ background: rgba(26, 86, 219, 0.15);
119
+ color: var(--bpmnkit-accent, #1a56db);
120
+ }
121
+
122
+ .bpmnkit-pa-finding-body {
123
+ flex: 1;
124
+ min-width: 0;
125
+ }
126
+
127
+ .bpmnkit-pa-finding-msg {
128
+ color: var(--bpmnkit-fg, #1a1a2e);
129
+ line-height: 1.45;
130
+ word-break: break-word;
131
+ font-size: 12.5px;
132
+ }
133
+
134
+ .bpmnkit-pa-finding-sug {
135
+ color: var(--bpmnkit-fg-muted, #6666a0);
136
+ font-size: 11.5px;
137
+ margin-top: 2px;
138
+ line-height: 1.4;
139
+ }
140
+
141
+ .bpmnkit-pa-finding-actions {
142
+ display: flex;
143
+ gap: 6px;
144
+ margin-top: 5px;
145
+ flex-wrap: wrap;
146
+ }
147
+
148
+ .bpmnkit-pa-btn {
149
+ font-size: 11px;
150
+ padding: 2px 8px;
151
+ border-radius: 4px;
152
+ border: 1px solid var(--bpmnkit-border, #d0d0e8);
153
+ background: var(--bpmnkit-surface-2, #eeeef8);
154
+ color: var(--bpmnkit-fg, #1a1a2e);
155
+ cursor: pointer;
156
+ white-space: nowrap;
157
+ }
158
+ .bpmnkit-pa-btn:hover {
159
+ background: var(--bpmnkit-accent-subtle, rgba(26,86,219,0.12));
160
+ border-color: var(--bpmnkit-accent, #1a56db);
161
+ color: var(--bpmnkit-accent, #1a56db);
162
+ }
163
+
164
+ .bpmnkit-pa-btn-fix {
165
+ border-color: var(--bpmnkit-accent, #1a56db);
166
+ color: var(--bpmnkit-accent, #1a56db);
167
+ }
168
+ .bpmnkit-pa-btn-fix:hover {
169
+ background: var(--bpmnkit-accent, #1a56db);
170
+ color: #fff;
171
+ }
172
+
173
+ /* ── Canvas element badges ─────────────────────────────────────────────────── */
174
+ .bpmnkit-pa-error-ring > .bpmnkit-shape-body,
175
+ .bpmnkit-pa-error-ring > rect:first-child {
176
+ stroke: var(--bpmnkit-danger, #dc2626) !important;
177
+ stroke-width: 2.5px !important;
178
+ }
179
+ .bpmnkit-pa-warning-ring > .bpmnkit-shape-body,
180
+ .bpmnkit-pa-warning-ring > rect:first-child {
181
+ stroke: var(--bpmnkit-warn, #d97706) !important;
182
+ stroke-width: 2px !important;
183
+ }
184
+ .bpmnkit-pa-info-ring > .bpmnkit-shape-body,
185
+ .bpmnkit-pa-info-ring > rect:first-child {
186
+ stroke: var(--bpmnkit-accent, #1a56db) !important;
187
+ stroke-width: 1.5px !important;
188
+ }
189
+ `;
190
+ export function injectPatternAdvisorStyles() {
191
+ if (document.getElementById(STYLE_ID) !== null)
192
+ return;
193
+ const style = document.createElement("style");
194
+ style.id = STYLE_ID;
195
+ style.textContent = CSS;
196
+ document.head.appendChild(style);
197
+ }
198
+ //# sourceMappingURL=css.js.map