@bpmnkit/plugins 0.0.33 → 0.1.0

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.
package/README.md CHANGED
@@ -9,7 +9,7 @@
9
9
  [![ai-assisted](https://img.shields.io/badge/AI--assisted-claude-8b5cf6?style=flat-square)](https://github.com/bpmnkit/monorepo)
10
10
  [![experimental](https://img.shields.io/badge/status-experimental-f59e0b?style=flat-square)](https://github.com/bpmnkit/monorepo)
11
11
 
12
- [Website](https://bpmnkit.com) · [Documentation](https://docs.bpmnkit.com) · [GitHub](https://github.com/bpmnkit/monorepo) · [Changelog](https://github.com/bpmnkit/monorepo/blob/main/packages/plugins/CHANGELOG.md)
12
+ [Website](https://bpmnkit.com) · [Documentation](https://bpmnkit.com/docs) · [GitHub](https://github.com/bpmnkit/monorepo) · [Changelog](https://github.com/bpmnkit/monorepo/blob/main/packages/plugins/CHANGELOG.md)
13
13
  </div>
14
14
 
15
15
  ---
@@ -65,14 +65,17 @@ async function* streamChat(serverUrl, messages, context, backend, signal, action
65
65
  reader.releaseLock();
66
66
  }
67
67
  }
68
- async function* streamImprove(serverUrl, compactDiagram, backend, signal, onOps, onXml) {
68
+ async function* streamImprove(serverUrl, xml, backend, signal, onOps, onXml) {
69
69
  let res;
70
70
  try {
71
71
  res = await fetch(`${serverUrl}/improve`, {
72
72
  method: "POST",
73
73
  headers: { "Content-Type": "application/json" },
74
74
  body: JSON.stringify({
75
- context: compactDiagram,
75
+ // The whole model, not a compact projection of it: the server applies
76
+ // the operations to this, so pools, lanes, data wiring and ioMapping
77
+ // detail survive an edit. It compactifies for the prompt itself.
78
+ xml,
76
79
  instruction: null,
77
80
  backend: backend === "auto" ? null : backend,
78
81
  }),
@@ -931,13 +934,12 @@ export function createAiPanel(options) {
931
934
  addMessage("user", "Improve this diagram");
932
935
  const aiMsgEl = addMessage("ai", "");
933
936
  aiMsgEl.classList.add("ai-msg-cursor");
934
- const compactDiagram = compactify(defs);
935
937
  let capturedOps = [];
936
938
  let capturedAutoFixCount = 0;
937
939
  let capturedXml;
938
940
  let fullText = "";
939
941
  try {
940
- for await (const token of streamImprove(options.serverUrl, compactDiagram, backendSelect.value, signal, (ops, autoFixCount) => {
942
+ for await (const token of streamImprove(options.serverUrl, Bpmn.export(defs), backendSelect.value, signal, (ops, autoFixCount) => {
941
943
  capturedOps = ops;
942
944
  capturedAutoFixCount = autoFixCount;
943
945
  }, (xml) => {
@@ -50,7 +50,7 @@ export interface CommandPaletteOptions {
50
50
  aiServerUrl?: string;
51
51
  /**
52
52
  * Base URL for documentation links.
53
- * Defaults to `https://docs.bpmnkit.com`.
53
+ * Defaults to `https://bpmnkit.com/docs`.
54
54
  */
55
55
  docsBaseUrl?: string;
56
56
  }
@@ -39,31 +39,31 @@ function wordsMatch(text, query) {
39
39
  .every((w) => target.includes(w));
40
40
  }
41
41
  // ── Docs registry ─────────────────────────────────────────────────────────────
42
- const DOCS_BASE_DEFAULT = "https://docs.bpmnkit.com";
42
+ const DOCS_BASE_DEFAULT = "https://bpmnkit.com/docs";
43
43
  const DOC_ENTRIES = [
44
44
  {
45
45
  title: "Quick Start",
46
- path: "/getting-started/quick-start/",
46
+ path: "/getting-started/quick-start",
47
47
  keywords: ["start", "install", "begin", "setup", "first", "hello", "quickstart"],
48
48
  },
49
49
  {
50
50
  title: "Installation",
51
- path: "/getting-started/installation/",
51
+ path: "/getting-started/installation",
52
52
  keywords: ["install", "npm", "pnpm", "yarn", "add", "package", "require"],
53
53
  },
54
54
  {
55
55
  title: "Concepts",
56
- path: "/getting-started/concepts/",
56
+ path: "/getting-started/concepts",
57
57
  keywords: ["concept", "bpmn", "process", "element", "overview", "intro", "what"],
58
58
  },
59
59
  {
60
60
  title: "Building Processes",
61
- path: "/guides/building-processes/",
61
+ path: "/guides/building-processes",
62
62
  keywords: ["build", "create", "process", "task", "flow", "sequence", "diagram", "guide"],
63
63
  },
64
64
  {
65
65
  title: "Gateways",
66
- path: "/guides/gateways/",
66
+ path: "/guides/gateways",
67
67
  keywords: [
68
68
  "gateway",
69
69
  "exclusive",
@@ -79,17 +79,17 @@ const DOC_ENTRIES = [
79
79
  },
80
80
  {
81
81
  title: "Simulation",
82
- path: "/guides/simulation/",
82
+ path: "/guides/simulation",
83
83
  keywords: ["simulate", "run", "test", "engine", "execute", "play", "simulation"],
84
84
  },
85
85
  {
86
86
  title: "Deployment",
87
- path: "/guides/deployment/",
87
+ path: "/guides/deployment",
88
88
  keywords: ["deploy", "camunda", "cluster", "zeebe", "publish", "production", "upload"],
89
89
  },
90
90
  {
91
91
  title: "AI Guide",
92
- path: "/guides/ai/",
92
+ path: "/guides/ai",
93
93
  keywords: [
94
94
  "ai",
95
95
  "artificial",
@@ -105,7 +105,7 @@ const DOC_ENTRIES = [
105
105
  },
106
106
  {
107
107
  title: "@bpmnkit/core",
108
- path: "/packages/core/",
108
+ path: "/packages/core",
109
109
  keywords: [
110
110
  "core",
111
111
  "parser",
@@ -120,32 +120,32 @@ const DOC_ENTRIES = [
120
120
  },
121
121
  {
122
122
  title: "@bpmnkit/editor",
123
- path: "/packages/editor/",
123
+ path: "/packages/editor",
124
124
  keywords: ["editor", "edit", "modeler", "interactive", "canvas", "plugin"],
125
125
  },
126
126
  {
127
127
  title: "@bpmnkit/engine",
128
- path: "/packages/engine/",
128
+ path: "/packages/engine",
129
129
  keywords: ["engine", "execute", "run", "simulate", "worker", "job", "token", "fire"],
130
130
  },
131
131
  {
132
132
  title: "@bpmnkit/api",
133
- path: "/packages/api/",
133
+ path: "/packages/api",
134
134
  keywords: ["api", "client", "camunda", "rest", "http", "zeebe", "operate", "instance"],
135
135
  },
136
136
  {
137
137
  title: "@bpmnkit/canvas",
138
- path: "/packages/canvas/",
138
+ path: "/packages/canvas",
139
139
  keywords: ["canvas", "viewer", "svg", "render", "view", "display", "embed"],
140
140
  },
141
141
  {
142
142
  title: "Connector Generator",
143
- path: "/packages/connector-gen/",
143
+ path: "/packages/connector-gen",
144
144
  keywords: ["connector", "openapi", "swagger", "template", "generate", "rest", "http", "api"],
145
145
  },
146
146
  {
147
147
  title: "casen CLI",
148
- path: "/cli/casen/",
148
+ path: "/cli/casen",
149
149
  keywords: [
150
150
  "cli",
151
151
  "command",
@@ -161,7 +161,7 @@ const DOC_ENTRIES = [
161
161
  },
162
162
  {
163
163
  title: "casen connector",
164
- path: "/cli/connector/",
164
+ path: "/cli/connector",
165
165
  keywords: ["connector", "cli", "generate", "api", "catalog", "swagger", "openapi"],
166
166
  },
167
167
  ];
@@ -5,9 +5,37 @@ export function xmlLocalName(qname) {
5
5
  return idx >= 0 ? qname.slice(idx + 1) : qname;
6
6
  }
7
7
  // ── Element lookup / update ───────────────────────────────────────────────────
8
+ // id → index per flowElements array. Every badge refresh looks up every shape,
9
+ // which made the panel O(S²) per change. Positions are verified against the
10
+ // array on each hit, so an array mutated in place still resolves correctly.
11
+ const positionIndex = new WeakMap();
12
+ function indexedFind(elements, id) {
13
+ let index = positionIndex.get(elements);
14
+ if (index === undefined) {
15
+ index = new Map();
16
+ for (let i = 0; i < elements.length; i++)
17
+ index.set(elements[i].id, i);
18
+ positionIndex.set(elements, index);
19
+ }
20
+ const at = index.get(id);
21
+ if (at !== undefined) {
22
+ const hit = elements[at];
23
+ if (hit !== undefined && hit.id === id)
24
+ return hit;
25
+ }
26
+ // Stale or missing entry: fall back to a scan and repair the index.
27
+ for (let i = 0; i < elements.length; i++) {
28
+ const el = elements[i];
29
+ if (el.id === id) {
30
+ index.set(id, i);
31
+ return el;
32
+ }
33
+ }
34
+ return undefined;
35
+ }
8
36
  export function findFlowElement(defs, id) {
9
37
  for (const process of defs.processes) {
10
- const el = process.flowElements.find((e) => e.id === id);
38
+ const el = indexedFind(process.flowElements, id);
11
39
  if (el)
12
40
  return el;
13
41
  }
@@ -1,10 +1,12 @@
1
+ /** Content to save, or a producer that renders it when the save actually runs. */
2
+ export type SaveContent = string | (() => string);
1
3
  export declare class AutoSave {
2
4
  private readonly _timers;
3
5
  private readonly _latest;
4
6
  private readonly _delay;
5
7
  private readonly _onSave;
6
8
  constructor(onSave: (fileId: string, content: string) => Promise<void>, delay?: number);
7
- schedule(fileId: string, content: string): void;
9
+ schedule(fileId: string, content: SaveContent): void;
8
10
  flush(): Promise<void>;
9
11
  private _save;
10
12
  }
@@ -30,7 +30,7 @@ export class AutoSave {
30
30
  this._timers.delete(fileId);
31
31
  }
32
32
  this._latest.clear();
33
- await Promise.all(entries.map(([id, content]) => this._onSave(id, content)));
33
+ await Promise.all(entries.map(([id, content]) => this._onSave(id, resolveContent(content))));
34
34
  }
35
35
  async _save(fileId) {
36
36
  const content = this._latest.get(fileId);
@@ -38,7 +38,10 @@ export class AutoSave {
38
38
  return;
39
39
  this._latest.delete(fileId);
40
40
  this._timers.delete(fileId);
41
- await this._onSave(fileId, content);
41
+ await this._onSave(fileId, resolveContent(content));
42
42
  }
43
43
  }
44
+ function resolveContent(content) {
45
+ return typeof content === "function" ? content() : content;
46
+ }
44
47
  //# sourceMappingURL=auto-save.js.map
@@ -184,9 +184,9 @@ export function createStoragePlugin(options) {
184
184
  const currentId = storageApi.getCurrentFileId();
185
185
  if (!currentId)
186
186
  return;
187
+ // Export lazily: only the last change before the debounced save is serialized.
187
188
  const defs = rawDefs;
188
- const xml = Bpmn.export(defs);
189
- storageApi.scheduleSave(currentId, xml);
189
+ storageApi.scheduleSave(currentId, () => Bpmn.export(defs));
190
190
  }
191
191
  offDiagramChange = anyOn("diagram:change", onDiagramUpdate);
192
192
  anyOn("diagram:load", onDiagramUpdate);
@@ -1,3 +1,4 @@
1
+ import type { SaveContent } from "./auto-save.js";
1
2
  import type { FileRecord, FileType, ProjectRecord, WorkspaceRecord } from "./types.js";
2
3
  export interface StorageApiOptions {
3
4
  /** Called when the user opens a file from the menu. */
@@ -60,7 +61,7 @@ export declare class StorageApi {
60
61
  projectId: string;
61
62
  fileId: string;
62
63
  } | null;
63
- scheduleSave(fileId: string, content: string): void;
64
+ scheduleSave(fileId: string, content: SaveContent): void;
64
65
  flush(): Promise<void>;
65
66
  private _persistContent;
66
67
  private _updateProjectCacheTimestamp;
@@ -699,15 +699,18 @@ export function createTabsPlugin(options = {}) {
699
699
  // --- BPMN process tracking ---
700
700
  function trackBpmnProcesses(tabId, xml) {
701
701
  try {
702
- for (const proc of Bpmn.parse(xml).processes) {
703
- bpmnProcessToTabId.set(proc.id, tabId);
704
- bpmnProcessNames.set(proc.id, proc.name ?? proc.id);
705
- }
702
+ trackBpmnDefinitions(tabId, Bpmn.parse(xml));
706
703
  }
707
704
  catch {
708
705
  // Malformed XML — tab still opens, process map unchanged
709
706
  }
710
707
  }
708
+ function trackBpmnDefinitions(tabId, defs) {
709
+ for (const proc of defs.processes) {
710
+ bpmnProcessToTabId.set(proc.id, tabId);
711
+ bpmnProcessNames.set(proc.id, proc.name ?? proc.id);
712
+ }
713
+ }
711
714
  function untrackBpmnProcesses(tabId) {
712
715
  for (const [procId, tid] of [...bpmnProcessToTabId]) {
713
716
  if (tid === tabId) {
@@ -1128,9 +1131,10 @@ export function createTabsPlugin(options = {}) {
1128
1131
  const xml = Bpmn.export(defs);
1129
1132
  // Update stored XML so onTabActivate always receives the latest content
1130
1133
  activeTab.config = { ...activeTab.config, xml };
1131
- // Refresh process map for this tab
1134
+ // Refresh process map for this tab from the model we already have,
1135
+ // rather than parsing the XML we just produced.
1132
1136
  untrackBpmnProcesses(activeId);
1133
- trackBpmnProcesses(activeId, xml);
1137
+ trackBpmnDefinitions(activeId, defs);
1134
1138
  // Keep raw pane in sync
1135
1139
  if (isRawMode)
1136
1140
  updateRawPane();
@@ -1,4 +1,4 @@
1
- import { injectTokenHighlightStyles } from "./css";
1
+ import { injectTokenHighlightStyles } from "./css.js";
2
2
  // ── Plugin factory ─────────────────────────────────────────────────────────────
3
3
  export function createTokenHighlightPlugin() {
4
4
  let canvasApi = null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bpmnkit/plugins",
3
- "version": "0.0.33",
3
+ "version": "0.1.0",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "exports": {
@@ -132,13 +132,13 @@
132
132
  "dist/**/*.d.ts"
133
133
  ],
134
134
  "dependencies": {
135
- "@bpmnkit/ascii": "0.0.30",
136
- "@bpmnkit/canvas": "0.0.30",
135
+ "@bpmnkit/canvas": "0.0.31",
136
+ "@bpmnkit/ascii": "0.0.31",
137
137
  "@bpmnkit/connector-gen": "0.0.15",
138
- "@bpmnkit/connectors": "0.0.2",
139
- "@bpmnkit/core": "0.1.2",
140
- "@bpmnkit/editor": "0.0.33",
141
- "@bpmnkit/feel": "0.0.20"
138
+ "@bpmnkit/connectors": "0.0.3",
139
+ "@bpmnkit/core": "0.2.0",
140
+ "@bpmnkit/feel": "0.0.20",
141
+ "@bpmnkit/editor": "0.0.34"
142
142
  },
143
143
  "description": "23 composable canvas plugins for BPMN editors and viewers — minimap, AI chat, process simulation, storage, and more",
144
144
  "keywords": [