@formicoidea/labre-framework-bpmn 0.40.0 → 0.42.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.
@@ -12,7 +12,6 @@ import { DefaultTheme, } from '@formicoidea/labre-core/model';
12
12
  *
13
13
  * - events — envelope (message), clock (timer), solid disc (terminate);
14
14
  * - tasks — a person (user) or a gear (service) in the top-left corner;
15
- * - activity — the `+` box at the bottom edge (sub-process, call activity);
16
15
  * - gateway — the X (exclusive) or the `+` (parallel);
17
16
  * - data — folded page, cylinder, open bracket.
18
17
  *
@@ -34,6 +33,13 @@ import { DefaultTheme, } from '@formicoidea/labre-core/model';
34
33
  * two and the one that is legible zoomed out.
35
34
  * - The **timer** has no hour ticks and the **data store** no shelf lines: at
36
35
  * the sizes this canvas draws them, both read as noise around the shape.
36
+ * - The **collapsed `[+]`** of the sub-process and the call activity is not
37
+ * drawn at all. In bpmn.io that boxed plus is a BUTTON — it unfolds the
38
+ * process the box stands for. Labre has no such gesture: a process defined
39
+ * elsewhere is a linked document, which carries a symbol of its own. Drawn
40
+ * here the marker would promise an action nothing performs, so the two kinds
41
+ * are bare and the call activity is told apart by its thick border, a
42
+ * creation-time preset (`CALL_ACTIVITY_WIDTH` in `consts.ts`).
37
43
  *
38
44
  * Mirrors the EDGY node renderer.
39
45
  */
@@ -41,13 +47,19 @@ import { DefaultTheme, } from '@formicoidea/labre-core/model';
41
47
  * The artefacts BPMN draws BARE — a plain native shape with nothing on it.
42
48
  * Everything else in the union is decorated here.
43
49
  *
44
- * `group` is bare for a different reason from the other three. They are
45
- * undecorated because the notation puts no marker on them; the group has a
46
- * distinctive look — a dashed, rounded, unfilled rectangle — and it is here
47
- * because that look is expressible as a native shape's own properties
48
- * (`strokeStyle: dash`, `radius`, `filled: false`). Drawing it by hand would
49
- * have meant re-implementing dashes the shape renderer already does, and losing
50
- * the editability that comes free with them.
50
+ * Three of them are here because the notation puts no marker on them at all.
51
+ * The other three are here for reasons of their own:
52
+ *
53
+ * - `group` has a distinctive look — a dashed, rounded, unfilled rectangle —
54
+ * and it is bare because that look is expressible as a native shape's own
55
+ * properties (`strokeStyle: dash`, `radius`, `filled: false`). Drawing it by
56
+ * hand would have meant re-implementing dashes the shape renderer already
57
+ * does, and losing the editability that comes free with them.
58
+ * - `subProcess` and `callActivity` do carry a marker in the notation — the
59
+ * collapsed `[+]` — and Labre drops it on purpose, because here it opens
60
+ * nothing (see the note on simplifications above). What tells the call
61
+ * activity from the sub-process is its thick border, a creation-time preset
62
+ * rather than anything drawn here.
51
63
  *
52
64
  * Written as the short list rather than the long one, so that the glyph kinds
53
65
  * are DERIVED from the model's union instead of restated beside it: a kind
@@ -61,6 +73,8 @@ const UNDECORATED_KINDS = {
61
73
  startEvent: true,
62
74
  endEvent: true,
63
75
  task: true,
76
+ subProcess: true,
77
+ callActivity: true,
64
78
  group: true,
65
79
  };
66
80
  const isUndecorated = (kind) => Object.hasOwn(UNDECORATED_KINDS, kind);
@@ -204,30 +218,6 @@ export const bpmnNode = (model, ctx, matrix, renderer, rc, bound) => {
204
218
  ctx.stroke();
205
219
  return;
206
220
  }
207
- if (kind === 'subProcess' || kind === 'callActivity') {
208
- // The collapsed marker: a small boxed `+` on the bottom edge, saying
209
- // "there is a whole process folded up in here". The call activity carries
210
- // the SAME marker — what tells the two apart is its thick border, which is
211
- // a creation-time preset rather than anything drawn here.
212
- const side = unit * 0.2;
213
- const bx = cx;
214
- const by = h - unit * 0.1 - side / 2;
215
- const half = side / 2;
216
- const arm = side * 0.3;
217
- ctx.lineWidth = Math.max(1, unit * 0.028);
218
- ctx.beginPath();
219
- ctx.moveTo(bx - half, by - half);
220
- ctx.lineTo(bx + half, by - half);
221
- ctx.lineTo(bx + half, by + half);
222
- ctx.lineTo(bx - half, by + half);
223
- ctx.lineTo(bx - half, by - half);
224
- ctx.moveTo(bx - arm, by);
225
- ctx.lineTo(bx + arm, by);
226
- ctx.moveTo(bx, by - arm);
227
- ctx.lineTo(bx, by + arm);
228
- ctx.stroke();
229
- return;
230
- }
231
221
  // ── Data and artifacts: the glyph IS the body ────────────────────────
232
222
  // The native rect under these three is unfilled and unstroked, so both the
233
223
  // fill and the outline are drawn here — off the model's own colours, which
package/dist/presets.js CHANGED
@@ -29,9 +29,10 @@ export const NODE_PRESETS = {
29
29
  stroke: EVENT_END,
30
30
  width: END_WIDTH,
31
31
  },
32
- // Activities: the same rounded rectangle, and a marker tells them apart —
33
- // except the call activity, whose thick border IS the distinction (it carries
34
- // the same `+` as the sub-process).
32
+ // Activities: the same rounded rectangle, and a corner marker tells the two
33
+ // typed tasks apart. The sub-process wears nothing (its collapsed `+` opens
34
+ // nothing here, so it is not drawn) and the call activity's thick border IS
35
+ // its distinction.
35
36
  task: {
36
37
  shapeType: 'rect',
37
38
  stroke: NEUTRAL_STROKE,
@@ -1,2 +1,57 @@
1
1
  import { type TemplateCategory } from '@formicoidea/labre-core/gfx/template';
2
+ import { type ChromeWording } from '@formicoidea/labre-core/shared/services';
3
+ /**
4
+ * The seeds the two worked scenes write — declared once so the scene builder
5
+ * and the manifest ({@link ../translations.ts}) read the very same fallback,
6
+ * never a restated literal. `com.labre.bpmn.example.<scene>.<slug>`, per
7
+ * `docs/adr/0023`.
8
+ */
9
+ export declare const SIMPLE_PROCESS_SEED: {
10
+ readonly poolName: {
11
+ readonly key: "com.labre.bpmn.example.simple-process.pool-name";
12
+ readonly fallback: "Process";
13
+ };
14
+ readonly submitRequest: {
15
+ readonly key: "com.labre.bpmn.example.simple-process.submit-request";
16
+ readonly fallback: "Submit request";
17
+ };
18
+ readonly fulfil: {
19
+ readonly key: "com.labre.bpmn.example.simple-process.fulfil";
20
+ readonly fallback: "Fulfil";
21
+ };
22
+ readonly reject: {
23
+ readonly key: "com.labre.bpmn.example.simple-process.reject";
24
+ readonly fallback: "Reject";
25
+ };
26
+ };
27
+ /**
28
+ * The two worked scenes' and the free-standing "Sequence flow" swatch's own
29
+ * tile names — a `nameKey` per hand-composed card, since none of the three
30
+ * derives from a command (`Template.nameKey`; `resolveTemplateName` checks it
31
+ * BEFORE `commandId`). `com.labre.bpmn.template.<slug>`, distinct from the
32
+ * `com.labre.bpmn.example.<scene>.*` seeds the two scenes WRITE — a tile name
33
+ * is chrome (re-rendered every time the panel opens), a seed is content
34
+ * (translated once, at insertion).
35
+ */
36
+ export declare const BPMN_TEMPLATE_NAME_SIMPLE_PROCESS: ChromeWording;
37
+ export declare const BPMN_TEMPLATE_NAME_MESSAGE_EXCHANGE: ChromeWording;
38
+ export declare const BPMN_TEMPLATE_NAME_SEQUENCE_FLOW: ChromeWording;
39
+ export declare const MESSAGE_EXCHANGE_SEED: {
40
+ readonly customer: {
41
+ readonly key: "com.labre.bpmn.example.message-exchange.customer";
42
+ readonly fallback: "Customer";
43
+ };
44
+ readonly supplier: {
45
+ readonly key: "com.labre.bpmn.example.message-exchange.supplier";
46
+ readonly fallback: "Supplier";
47
+ };
48
+ readonly placeOrder: {
49
+ readonly key: "com.labre.bpmn.example.message-exchange.place-order";
50
+ readonly fallback: "Place order";
51
+ };
52
+ readonly confirmOrder: {
53
+ readonly key: "com.labre.bpmn.example.message-exchange.confirm-order";
54
+ readonly fallback: "Confirm order";
55
+ };
56
+ };
2
57
  export declare const bpmnTemplateCategory: TemplateCategory;
@@ -1,9 +1,81 @@
1
1
  import { makeTemplateSnapshot, surfaceText, templateFromCommand, } from '@formicoidea/labre-core/gfx/template';
2
2
  import { ConnectorMode, PointStyle, StrokeStyle, } from '@formicoidea/labre-core/model';
3
+ import { translateKey, } from '@formicoidea/labre-core/shared/services';
3
4
  import { bpmnCommands } from '../commands.js';
4
5
  import { EVENT_END, EVENT_START, GROUP_STROKE, MESSAGE_STROKE, MESSAGE_WIDTH, NEUTRAL_STROKE, NODE_SIZE, POOL_BAND_FILL, POOL_FRAME_COLOR, SEQUENCE_STROKE, SEQUENCE_WIDTH, } from '../consts.js';
5
6
  import { bpmnNodeProps, bpmnPoolProps } from '../presets.js';
6
7
  import { BPMN_ROLE } from '../roles.js';
8
+ /**
9
+ * The seeds the two worked scenes write — declared once so the scene builder
10
+ * and the manifest ({@link ../translations.ts}) read the very same fallback,
11
+ * never a restated literal. `com.labre.bpmn.example.<scene>.<slug>`, per
12
+ * `docs/adr/0023`.
13
+ */
14
+ export const SIMPLE_PROCESS_SEED = {
15
+ poolName: {
16
+ key: 'com.labre.bpmn.example.simple-process.pool-name',
17
+ fallback: 'Process',
18
+ },
19
+ submitRequest: {
20
+ key: 'com.labre.bpmn.example.simple-process.submit-request',
21
+ fallback: 'Submit request',
22
+ },
23
+ fulfil: {
24
+ key: 'com.labre.bpmn.example.simple-process.fulfil',
25
+ fallback: 'Fulfil',
26
+ },
27
+ reject: {
28
+ key: 'com.labre.bpmn.example.simple-process.reject',
29
+ fallback: 'Reject',
30
+ },
31
+ };
32
+ /**
33
+ * The two worked scenes' and the free-standing "Sequence flow" swatch's own
34
+ * tile names — a `nameKey` per hand-composed card, since none of the three
35
+ * derives from a command (`Template.nameKey`; `resolveTemplateName` checks it
36
+ * BEFORE `commandId`). `com.labre.bpmn.template.<slug>`, distinct from the
37
+ * `com.labre.bpmn.example.<scene>.*` seeds the two scenes WRITE — a tile name
38
+ * is chrome (re-rendered every time the panel opens), a seed is content
39
+ * (translated once, at insertion).
40
+ */
41
+ export const BPMN_TEMPLATE_NAME_SIMPLE_PROCESS = [
42
+ 'com.labre.bpmn.template.simple-process',
43
+ 'Simple process',
44
+ ];
45
+ export const BPMN_TEMPLATE_NAME_MESSAGE_EXCHANGE = [
46
+ 'com.labre.bpmn.template.message-exchange',
47
+ 'Message exchange',
48
+ ];
49
+ export const BPMN_TEMPLATE_NAME_SEQUENCE_FLOW = [
50
+ 'com.labre.bpmn.template.sequence-flow',
51
+ 'Sequence flow',
52
+ ];
53
+ export const MESSAGE_EXCHANGE_SEED = {
54
+ customer: {
55
+ key: 'com.labre.bpmn.example.message-exchange.customer',
56
+ fallback: 'Customer',
57
+ },
58
+ supplier: {
59
+ key: 'com.labre.bpmn.example.message-exchange.supplier',
60
+ fallback: 'Supplier',
61
+ },
62
+ placeOrder: {
63
+ key: 'com.labre.bpmn.example.message-exchange.place-order',
64
+ fallback: 'Place order',
65
+ },
66
+ confirmOrder: {
67
+ key: 'com.labre.bpmn.example.message-exchange.confirm-order',
68
+ fallback: 'Confirm order',
69
+ },
70
+ };
71
+ /**
72
+ * One seed's resolved text — the host's catalogue when `std` is a real
73
+ * inserting editor, the English fallback when building the module's own
74
+ * `content` (no editor exists yet at that point).
75
+ */
76
+ function seedText(std, def) {
77
+ return std ? translateKey(std, def.key, def.fallback) : def.fallback;
78
+ }
7
79
  /**
8
80
  * The BPMN palette — DERIVED from the toolbox, one template per artefact
9
81
  * command.
@@ -139,8 +211,6 @@ const taskPreview = (glyph = '', width = 2.4) => `<svg ${A} fill="none"><rect x=
139
211
  const gatewayPreview = (marker) => `<svg ${A} fill="none"><path d="M67 16 L92 40 L67 64 L42 40 Z" stroke="${NEUTRAL_STROKE}" stroke-width="2.4" stroke-linejoin="round"/><path d="${marker}" stroke="${NEUTRAL_STROKE}" stroke-width="2.2" stroke-linecap="round"/></svg>`;
140
212
  const ENVELOPE = `<rect x="59" y="35" width="16" height="11" stroke="${NEUTRAL_STROKE}" stroke-width="1.4"/><path d="M59 35 L67 42 L75 35" stroke="${NEUTRAL_STROKE}" stroke-width="1.4" stroke-linejoin="round"/>`;
141
213
  const CLOCK = `<circle cx="67" cy="40" r="9" stroke="${NEUTRAL_STROKE}" stroke-width="1.4"/><path d="M67 40 V33 M67 40 L72 43" stroke="${NEUTRAL_STROKE}" stroke-width="1.4" stroke-linecap="round"/>`;
142
- /** The collapsed `[+]`, on the activity's bottom edge — sub-process and call alike. */
143
- const COLLAPSED = `<rect x="61" y="44" width="12" height="12" stroke="${NEUTRAL_STROKE}" stroke-width="1.4"/><path d="M67 47 V53 M64 50 H70" stroke="${NEUTRAL_STROKE}" stroke-width="1.4" stroke-linecap="round"/>`;
144
214
  const previews = {
145
215
  process: `<svg ${A} fill="none"><circle cx="16" cy="40" r="8" stroke="${EVENT_START}" stroke-width="2"/><rect x="34" y="31" width="26" height="18" rx="3" stroke="${NEUTRAL_STROKE}" stroke-width="1.6"/><path d="M78 31 L88 40 L78 49 L68 40 Z" stroke="${NEUTRAL_STROKE}" stroke-width="1.4"/><path d="M73 37 L83 43 M83 37 L73 43" stroke="${NEUTRAL_STROKE}" stroke-width="1.2"/><circle cx="118" cy="40" r="8" stroke="${EVENT_END}" stroke-width="3"/><path d="M24 40 H34 M60 40 H68 M88 40 H110" stroke="${SEQUENCE_STROKE}" stroke-width="1.2"/></svg>`,
146
216
  startEvent: eventPreview(EVENT_START, 3),
@@ -152,10 +222,13 @@ const previews = {
152
222
  task: taskPreview(),
153
223
  taskUser: taskPreview(`<circle cx="41" cy="31" r="2.6" stroke="${NEUTRAL_STROKE}" stroke-width="1.2"/><path d="M37.4 38 a3.6 3.6 0 0 1 7.2 0" stroke="${NEUTRAL_STROKE}" stroke-width="1.2"/>`),
154
224
  taskService: taskPreview(`<circle cx="41" cy="32" r="3.4" stroke="${NEUTRAL_STROKE}" stroke-width="1.2"/><circle cx="41" cy="32" r="1" fill="${NEUTRAL_STROKE}"/><path d="M41 27.4 V29 M41 35 V36.6 M36.4 32 H38 M44 32 H45.6" stroke="${NEUTRAL_STROKE}" stroke-width="1.2" stroke-linecap="round"/>`),
155
- subProcess: taskPreview(COLLAPSED),
225
+ // Neither carries the notation's collapsed `[+]`: it opens nothing in Labre
226
+ // and the board does not paint it, so a card that did would be advertising a
227
+ // drawing the command never makes.
228
+ subProcess: taskPreview(),
156
229
  // The thick border IS the distinction: this box stands for a process defined
157
230
  // elsewhere (`presets.ts`, `CALL_ACTIVITY_WIDTH`).
158
- callActivity: taskPreview(COLLAPSED, 4.5),
231
+ callActivity: taskPreview('', 4.5),
159
232
  gateway: gatewayPreview('M58 31 L76 49 M76 31 L58 49'),
160
233
  gatewayParallel: gatewayPreview('M67 30 V50 M57 40 H77'),
161
234
  dataObject: `<svg ${A} fill="none"><path d="M52 14 H76 L88 26 V66 H52 Z" stroke="${NEUTRAL_STROKE}" stroke-width="2.2" stroke-linejoin="round"/><path d="M76 14 V26 H88" stroke="${NEUTRAL_STROKE}" stroke-width="1.8" stroke-linejoin="round"/></svg>`,
@@ -172,28 +245,39 @@ const previews = {
172
245
  messageExchange: `<svg ${A} fill="none"><rect x="14" y="8" width="107" height="27" rx="3" stroke="${POOL_FRAME_COLOR}" stroke-width="1.6"/><rect x="14" y="8" width="11" height="27" fill="${POOL_BAND_FILL}"/><path d="M25 8 V35" stroke="${POOL_FRAME_COLOR}" stroke-width="1.4"/><rect x="14" y="45" width="107" height="27" rx="3" stroke="${POOL_FRAME_COLOR}" stroke-width="1.6"/><rect x="14" y="45" width="11" height="27" fill="${POOL_BAND_FILL}"/><path d="M25 45 V72" stroke="${POOL_FRAME_COLOR}" stroke-width="1.4"/><circle cx="33" cy="21.5" r="4.5" stroke="${EVENT_START}" stroke-width="1.6"/><rect x="46" y="14" width="26" height="15" rx="3" stroke="${NEUTRAL_STROKE}" stroke-width="1.6"/><circle cx="88" cy="21.5" r="4.5" stroke="${EVENT_END}" stroke-width="2.6"/><rect x="46" y="51" width="26" height="15" rx="3" stroke="${NEUTRAL_STROKE}" stroke-width="1.6"/><path d="M41 21.5 H46 M72 21.5 H83" stroke="${SEQUENCE_STROKE}" stroke-width="1.4"/><circle cx="59" cy="31.5" r="2.2" fill="${MESSAGE_STROKE}"/><path d="M59 34 V48" stroke="${MESSAGE_STROKE}" stroke-width="1.4" stroke-dasharray="3 2.4"/><path d="M56 46 L59 50 L62 46" stroke="${MESSAGE_STROKE}" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"/></svg>`,
173
246
  };
174
247
  /* ── The two worked scenes ────────────────────────────────────────────────── */
175
- /** A hand-composed card — what is left once the artefacts are derived. */
176
- const scene = (name, preview, elements) => ({
248
+ /**
249
+ * A hand-composed card — what is left once the artefacts are derived.
250
+ *
251
+ * `build` takes the OPTIONAL inserting editor: called with none, at module
252
+ * load, for the English `content`; called again with the real `std` as
253
+ * {@link Template.localize}, so the two builds read the very same layout and
254
+ * differ only in the words a seed resolves to (`docs/adr/0023`).
255
+ */
256
+ const scene = (name, preview, build, nameKey) => ({
177
257
  name,
178
258
  type: 'template',
179
259
  preview,
180
- content: makeTemplateSnapshot(elements, name),
260
+ nameKey,
261
+ content: makeTemplateSnapshot(build(), name),
262
+ localize: std => makeTemplateSnapshot(build(std), name),
181
263
  });
182
- const process = {
183
- pool: pool(0, 0, 640, 200, 'Process'),
184
- start: node('startEvent', 40, 72),
185
- task1: node('task', 116, 64, 'Submit request'),
186
- gw: node('gatewayExclusive', 272, 64),
187
- task2: node('task', 376, 20, 'Fulfil'),
188
- task3: node('task', 376, 124, 'Reject'),
189
- end: node('endEvent', 556, 72),
190
- c1: seq('start', 'task1'),
191
- c2: seq('task1', 'gw'),
192
- c3: seq('gw', 'task2'),
193
- c4: seq('gw', 'task3'),
194
- c5: seq('task2', 'end'),
195
- c6: seq('task3', 'end'),
196
- };
264
+ function process(std) {
265
+ return {
266
+ pool: pool(0, 0, 640, 200, seedText(std, SIMPLE_PROCESS_SEED.poolName)),
267
+ start: node('startEvent', 40, 72),
268
+ task1: node('task', 116, 64, seedText(std, SIMPLE_PROCESS_SEED.submitRequest)),
269
+ gw: node('gatewayExclusive', 272, 64),
270
+ task2: node('task', 376, 20, seedText(std, SIMPLE_PROCESS_SEED.fulfil)),
271
+ task3: node('task', 376, 124, seedText(std, SIMPLE_PROCESS_SEED.reject)),
272
+ end: node('endEvent', 556, 72),
273
+ c1: seq('start', 'task1'),
274
+ c2: seq('task1', 'gw'),
275
+ c3: seq('gw', 'task2'),
276
+ c4: seq('gw', 'task3'),
277
+ c5: seq('task2', 'end'),
278
+ c6: seq('task3', 'end'),
279
+ };
280
+ }
197
281
  /**
198
282
  * Two participants, and the one arrow that is allowed to cross between them.
199
283
  *
@@ -216,30 +300,34 @@ const process = {
216
300
  * until this slice ran the rules over the shipped cards. The customer's process
217
301
  * now closes where it should: order placed, then done.
218
302
  */
219
- const messageExchange = {
220
- customer: pool(0, 0, 640, 200, 'Customer'),
221
- supplier: pool(0, 240, 640, 200, 'Supplier'),
222
- start: node('startEvent', 40, 72),
223
- ask: node('taskUser', 140, 64, 'Place order'),
224
- done: node('endEvent', 320, 72),
225
- answer: node('taskService', 140, 304, 'Confirm order'),
226
- // Inside the first participant: what happens, and in what order.
227
- inside: seq('start', 'ask'),
228
- closes: seq('ask', 'done'),
229
- // Across the two: who told whom.
230
- across: msg('ask', 'answer'),
231
- };
303
+ function messageExchange(std) {
304
+ return {
305
+ customer: pool(0, 0, 640, 200, seedText(std, MESSAGE_EXCHANGE_SEED.customer)),
306
+ supplier: pool(0, 240, 640, 200, seedText(std, MESSAGE_EXCHANGE_SEED.supplier)),
307
+ start: node('startEvent', 40, 72),
308
+ ask: node('taskUser', 140, 64, seedText(std, MESSAGE_EXCHANGE_SEED.placeOrder)),
309
+ done: node('endEvent', 320, 72),
310
+ answer: node('taskService', 140, 304, seedText(std, MESSAGE_EXCHANGE_SEED.confirmOrder)),
311
+ // Inside the first participant: what happens, and in what order.
312
+ inside: seq('start', 'ask'),
313
+ closes: seq('ask', 'done'),
314
+ // Across the two: who told whom.
315
+ across: msg('ask', 'answer'),
316
+ };
317
+ }
232
318
  export const bpmnTemplateCategory = {
233
319
  name: 'BPMN',
320
+ // Reuses the senior button's own key — see `TemplateCategory.nameKey`.
321
+ nameKey: 'com.labre.framework.bpmn',
234
322
  templates: [
235
- scene('Simple process', previews.process, process),
236
- scene('Message exchange', previews.messageExchange, messageExchange),
323
+ scene('Simple process', previews.process, process, BPMN_TEMPLATE_NAME_SIMPLE_PROCESS[0]),
324
+ scene('Message exchange', previews.messageExchange, messageExchange, BPMN_TEMPLATE_NAME_MESSAGE_EXCHANGE[0]),
237
325
  /* ── The core: a drawable process, from the first click ─────────────── */
238
326
  templateFromCommand(byId('bpmn.addStartEvent'), previews.startEvent),
239
327
  templateFromCommand(byId('bpmn.addEndEvent'), previews.endEvent),
240
328
  templateFromCommand(byId('bpmn.addTask'), previews.task),
241
329
  templateFromCommand(byId('bpmn.addExclusiveGateway'), previews.gateway),
242
- scene('Sequence flow', previews.sequence, { a: freeSeq() }),
330
+ scene('Sequence flow', previews.sequence, () => ({ a: freeSeq() }), BPMN_TEMPLATE_NAME_SEQUENCE_FLOW[0]),
243
331
  templateFromCommand(byId('bpmn.addPool'), previews.pool),
244
332
  /* ── Activities ─────────────────────────────────────────────────────── */
245
333
  templateFromCommand(byId('bpmn.addUserTask'), previews.taskUser),
@@ -10,6 +10,8 @@ export declare class EdgelessBpmnSeniorButton extends EdgelessBpmnSeniorButton_b
10
10
  enableActiveBackground: boolean;
11
11
  type: typeof EmptyTool;
12
12
  private _toggleMenu;
13
+ /** Lit while one of THIS framework's artefacts is armed. See the Wardley one. */
14
+ private get _armed();
13
15
  render(): import("lit-html").TemplateResult<1>;
14
16
  }
15
17
  export {};
@@ -1,7 +1,7 @@
1
1
  import { DefaultTool } from '@formicoidea/labre-core/blocks/surface';
2
2
  import { EmptyTool } from '@formicoidea/labre-core/gfx/pointer';
3
3
  import { translateKey } from '@formicoidea/labre-core/shared/services';
4
- import { EdgelessToolbarToolMixin } from '@formicoidea/labre-core/widgets/edgeless-toolbar';
4
+ import { armedArtefact, EdgelessToolbarToolMixin, } from '@formicoidea/labre-core/widgets/edgeless-toolbar';
5
5
  import { SignalWatcher } from '@formicoidea/labre-core/global/lit';
6
6
  import { css, html, LitElement } from 'lit';
7
7
  import { bpmnToolbarIcon } from './icons.js';
@@ -72,6 +72,10 @@ export class EdgelessBpmnSeniorButton extends EdgelessToolbarToolMixin(SignalWat
72
72
  const menu = this.createPopper('edgeless-bpmn-menu', this);
73
73
  menu.element.edgeless = this.edgeless;
74
74
  }
75
+ /** Lit while one of THIS framework's artefacts is armed. See the Wardley one. */
76
+ get _armed() {
77
+ return armedArtefact(this.gfx)?.owner === 'bpmn';
78
+ }
75
79
  render() {
76
80
  return html `<edgeless-toolbar-button
77
81
  class="bpmn-button"
@@ -79,7 +83,7 @@ export class EdgelessBpmnSeniorButton extends EdgelessToolbarToolMixin(SignalWat
79
83
  ? ''
80
84
  : translateKey(this.edgeless.std, 'com.labre.framework.bpmn', 'BPMN')}
81
85
  .tooltipOffset=${4}
82
- .active=${!!this.popper}
86
+ .active=${!!this.popper || this._armed}
83
87
  @click=${this._toggleMenu}
84
88
  >
85
89
  <div class="bpmn-root">
@@ -1,4 +1,4 @@
1
- import { ActionPlacement, type ChromeWording, type ToolbarContext } from '@formicoidea/labre-core/shared/services';
1
+ import { type ChromeWording, type ToolbarContext, type ToolbarModuleConfig } from '@formicoidea/labre-core/shared/services';
2
2
  import { type TemplateResult } from 'lit';
3
3
  export declare const bpmnPoolToolbarConfig: {
4
4
  readonly actions: [{
@@ -24,16 +24,27 @@ export declare const bpmnPoolToolbarConfig: {
24
24
  tooltip: string;
25
25
  run: (runCtx: ToolbarContext) => void;
26
26
  };
27
- }, {
28
- id: string;
29
- placement: ActionPlacement;
30
- when: (ctx: ToolbarContext) => boolean;
31
- generate: (ctx: ToolbarContext) => {
32
- icon: TemplateResult;
33
- label: string;
34
- run: (runCtx: ToolbarContext) => void;
35
- };
36
- }];
27
+ }, import("@formicoidea/labre-core/shared/services").ToolbarActionGenerator];
37
28
  readonly when: (ctx: ToolbarContext) => boolean;
38
29
  };
39
30
  export declare const bpmnPoolToolbarExtension: import("@formicoidea/labre-core/store/index.js").ExtensionType;
31
+ /**
32
+ * The pool's FLAG-GATED row: the legend and the validation dropdown, in one
33
+ * module because a flavour may carry exactly one (a second on the same key
34
+ * throws `DuplicateServiceDefinitionError` before the editor finishes setting
35
+ * up — the same merge C4's board makes for the same reason).
36
+ *
37
+ * `c.legend` and not `b.legend`: the toolbar sorts lexicographically by id
38
+ * after partitioning by placement, and `'b.add-lane' < 'b.legend' <
39
+ * 'b.remove-lane'` would wedge the button between the two lane gestures. `c.`
40
+ * puts it where it belongs — resize · add lane · remove lane · **Legend** ·
41
+ * Validation, with Export XML in the "⋮".
42
+ *
43
+ * Gated, because GENERATING a legend is tooling (ADR 0026, amending 0009). What
44
+ * the gesture already wrote is document content and keeps painting with the
45
+ * flag off: the box is plain shapes and text plus `bpmnNode`s the always-on
46
+ * renderer paints, and the pool keeps its resize handle and its lanes either
47
+ * way.
48
+ */
49
+ export declare const bpmnPoolToolingToolbarConfig: ToolbarModuleConfig;
50
+ export declare const bpmnPoolToolingToolbarExtension: import("@formicoidea/labre-core/store/index.js").ExtensionType;
@@ -1,6 +1,6 @@
1
- import { EdgelessCRUDIdentifier } from '@formicoidea/labre-core/blocks/surface';
1
+ import { EdgelessCRUDIdentifier, legendToolbarAction, validationToolbarConfig, } from '@formicoidea/labre-core/blocks/surface';
2
2
  import { BpmnPoolElementModel } from '@formicoidea/labre-core/model';
3
- import { ActionPlacement, BOARD_RESIZE_TOGGLE, ToolbarModuleExtension, translateKey, } from '@formicoidea/labre-core/shared/services';
3
+ import { BOARD_RESIZE_TOGGLE, commandMoreAction, ToolbarModuleExtension, translateKey, } from '@formicoidea/labre-core/shared/services';
4
4
  import { BlockFlavourIdentifier, getRegisteredCommands, runCommand, } from '@formicoidea/labre-core/std';
5
5
  import { html } from 'lit';
6
6
  import { bpmnExportXmlIcon, bpmnLaneAddIcon, bpmnLaneRemoveIcon, } from './icons.js';
@@ -84,43 +84,6 @@ function commandAction(id, commandId, labelKey, labelFallback, icon) {
84
84
  },
85
85
  };
86
86
  }
87
- /**
88
- * The same thing, for the "⋮" menu instead of the row.
89
- *
90
- * Two differences, and the widget imposes both: a menu line is drawn from
91
- * `label` (a tooltip on a line that is already words would be a second copy of
92
- * them), and `placement: ActionPlacement.More` is what partitions it out of the
93
- * row in the first place — `renderToolbar` splits on exactly that flag, and the
94
- * entries it collects come from every module contributing to the element,
95
- * so a framework can add to the "⋮" without owning it.
96
- */
97
- function commandMoreAction(id, commandId, labelKey, labelFallback, icon) {
98
- return {
99
- id,
100
- placement: ActionPlacement.More,
101
- when: (ctx) => {
102
- const command = findCommand(ctx, commandId);
103
- return command !== undefined && (command.when?.(ctx.std) ?? true);
104
- },
105
- generate: (ctx) => ({
106
- icon,
107
- label: translateKey(ctx.std, labelKey, labelFallback),
108
- run: (runCtx) => {
109
- const command = findCommand(runCtx, commandId);
110
- if (!command)
111
- return;
112
- // The same `source` the row's own entries report: the "⋮" is a
113
- // degradation of the row, not a surface of its own, and the
114
- // `ElementCreationSource` union deliberately names places rather than
115
- // widths (`docs/adr/0008`).
116
- runCommand(runCtx.std, command, {
117
- surface: 'contextual-toolbar',
118
- source: 'toolbar:general',
119
- });
120
- },
121
- }),
122
- };
123
- }
124
87
  const findCommand = (ctx, id) => getRegisteredCommands(ctx.std).find(candidate => candidate.id === id);
125
88
  export const bpmnPoolToolbarConfig = {
126
89
  actions: [
@@ -141,3 +104,40 @@ export const bpmnPoolToolbarExtension = ToolbarModuleExtension({
141
104
  id: BlockFlavourIdentifier('affine:surface:bpmnPool'),
142
105
  config: bpmnPoolToolbarConfig,
143
106
  });
107
+ /**
108
+ * The pool's FLAG-GATED row: the legend and the validation dropdown, in one
109
+ * module because a flavour may carry exactly one (a second on the same key
110
+ * throws `DuplicateServiceDefinitionError` before the editor finishes setting
111
+ * up — the same merge C4's board makes for the same reason).
112
+ *
113
+ * `c.legend` and not `b.legend`: the toolbar sorts lexicographically by id
114
+ * after partitioning by placement, and `'b.add-lane' < 'b.legend' <
115
+ * 'b.remove-lane'` would wedge the button between the two lane gestures. `c.`
116
+ * puts it where it belongs — resize · add lane · remove lane · **Legend** ·
117
+ * Validation, with Export XML in the "⋮".
118
+ *
119
+ * Gated, because GENERATING a legend is tooling (ADR 0026, amending 0009). What
120
+ * the gesture already wrote is document content and keeps painting with the
121
+ * flag off: the box is plain shapes and text plus `bpmnNode`s the always-on
122
+ * renderer paints, and the pool keeps its resize handle and its lanes either
123
+ * way.
124
+ */
125
+ export const bpmnPoolToolingToolbarConfig = {
126
+ actions: [
127
+ legendToolbarAction({
128
+ id: 'c.legend',
129
+ Model: BpmnPoolElementModel,
130
+ owner: 'bpmn',
131
+ framework: 'bpmn',
132
+ }),
133
+ // The generic dropdown, not a BPMN variant of it: the config names no
134
+ // framework — it reads the registered rules and profiles — so this is the
135
+ // very same object wardley, c4 and the context map register.
136
+ ...validationToolbarConfig.actions,
137
+ ],
138
+ when: bpmnPoolToolbarConfig.when,
139
+ };
140
+ export const bpmnPoolToolingToolbarExtension = ToolbarModuleExtension({
141
+ id: BlockFlavourIdentifier('custom:affine:surface:bpmnPool'),
142
+ config: bpmnPoolToolingToolbarConfig,
143
+ });
@@ -38,9 +38,16 @@ export declare const bpmnTaskIcon: import("lit-html").TemplateResult<2>;
38
38
  export declare const bpmnTaskUserIcon: import("lit-html").TemplateResult<2>;
39
39
  /** Service task — the rectangle with a gear in its top-left corner. */
40
40
  export declare const bpmnTaskServiceIcon: import("lit-html").TemplateResult<2>;
41
- /** Sub-process — the rectangle with the collapsed [+] marker on its bottom edge. */
41
+ /**
42
+ * Sub-process — the plain rectangle, like the task it is drawn as.
43
+ *
44
+ * The notation puts a collapsed [+] on its bottom edge and the canvas does not
45
+ * paint one (see `node/node-renderer.ts`): that box is a button everywhere else
46
+ * and opens nothing here. A palette glyph says what lands on the board, so it
47
+ * has none either, and the menu label is what tells this entry from the task.
48
+ */
42
49
  export declare const bpmnSubProcessIcon: import("lit-html").TemplateResult<2>;
43
- /** Call activity — the same [+] marker, on the notation's THICKEST border. */
50
+ /** Call activity — the same bare rectangle, on the notation's THICKEST border. */
44
51
  export declare const bpmnCallActivityIcon: import("lit-html").TemplateResult<2>;
45
52
  /** Exclusive gateway — diamond with an X. */
46
53
  export declare const bpmnGatewayIcon: import("lit-html").TemplateResult<2>;
@@ -87,17 +87,20 @@ export const bpmnTaskServiceIcon = svg `<svg width="24" height="24" viewBox="0 0
87
87
  <circle cx="7.6" cy="11" r="0.5" stroke="currentColor" stroke-width="0.8"/>
88
88
  <path d="M7.6 8.9 V9.3 M7.6 12.7 V13.1 M5.5 11 H5.9 M9.3 11 H9.7 M6.1 9.5 L6.4 9.8 M8.8 12.2 L9.1 12.5 M9.1 9.5 L8.8 9.8 M6.4 12.2 L6.1 12.5" stroke="currentColor" stroke-width="1" stroke-linecap="round"/>
89
89
  </svg>`;
90
- /** Sub-process — the rectangle with the collapsed [+] marker on its bottom edge. */
90
+ /**
91
+ * Sub-process — the plain rectangle, like the task it is drawn as.
92
+ *
93
+ * The notation puts a collapsed [+] on its bottom edge and the canvas does not
94
+ * paint one (see `node/node-renderer.ts`): that box is a button everywhere else
95
+ * and opens nothing here. A palette glyph says what lands on the board, so it
96
+ * has none either, and the menu label is what tells this entry from the task.
97
+ */
91
98
  export const bpmnSubProcessIcon = svg `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
92
99
  <rect x="3.5" y="6.5" width="17" height="11" rx="2.5" stroke="currentColor" stroke-width="1.6"/>
93
- <rect x="9.6" y="12.6" width="4.8" height="4.8" stroke="currentColor" stroke-width="1.1"/>
94
- <path d="M12 13.8 V16.2 M10.8 15 H13.2" stroke="currentColor" stroke-width="1.1" stroke-linecap="round"/>
95
100
  </svg>`;
96
- /** Call activity — the same [+] marker, on the notation's THICKEST border. */
101
+ /** Call activity — the same bare rectangle, on the notation's THICKEST border. */
97
102
  export const bpmnCallActivityIcon = svg `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
98
103
  <rect x="4" y="7" width="16" height="10" rx="2.5" stroke="currentColor" stroke-width="3"/>
99
- <rect x="9.6" y="12.2" width="4.8" height="4.8" stroke="currentColor" stroke-width="1.1"/>
100
- <path d="M12 13.4 V15.8 M10.8 14.6 H13.2" stroke="currentColor" stroke-width="1.1" stroke-linecap="round"/>
101
104
  </svg>`;
102
105
  /** Exclusive gateway — diamond with an X. */
103
106
  export const bpmnGatewayIcon = svg `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
@@ -0,0 +1,23 @@
1
+ import type { FrameworkPalette, NamedPalette } from '@formicoidea/labre-core/components/color-picker';
2
+ import type { ChromeWording } from '@formicoidea/labre-core/shared/services';
3
+ /**
4
+ * The two BPMN swatches' own names, resolved by `resolvePaletteLabel`
5
+ * (`@labre/affine-components/color-picker`) through each swatch's own
6
+ * {@link NamedPalette.labelWording} — the mechanism `WARDLEY_PALETTE_WORDINGS`
7
+ * established, reused here without either package naming the other.
8
+ *
9
+ * Exported so `../translations.ts` can list them under source `chrome`: a
10
+ * swatch's name is toolbar chrome, never seeded into a document.
11
+ */
12
+ export declare const BPMN_PALETTE_WORDING_START: ChromeWording;
13
+ export declare const BPMN_PALETTE_WORDING_END: ChromeWording;
14
+ export declare const BPMN_PALETTE_WORDINGS: readonly ChromeWording[];
15
+ /** The two notation hues, then the neutrals every drawing needs. */
16
+ export declare const BPMN_PALETTE_LIST: NamedPalette[];
17
+ /**
18
+ * BPMN's page of the colour pickers' carousel (`docs/adr/0027`). Registered
19
+ * from the FLAG-GATED view extension, because offering hues is TOOLING: a
20
+ * process drawn while the flag was on keeps every colour it was painted with
21
+ * when the flag goes off (`docs/adr/0009`).
22
+ */
23
+ export declare const BPMN_FRAMEWORK_PALETTE: FrameworkPalette;