sad-mcp 2.3.5 → 2.3.7

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,7 @@ export declare const L: {
12
12
  readonly POOL_VGAP: 16;
13
13
  readonly LANE_MIN_H: 140;
14
14
  readonly LANE_CORRIDOR_H: 56;
15
- readonly EMPTY_POOL_H: 60;
15
+ readonly EMPTY_POOL_H: 96;
16
16
  readonly CANVAS_TOP: 72;
17
17
  readonly CANVAS_LEFT_PAD: 20;
18
18
  readonly CANVAS_RIGHT_PAD: 40;
@@ -31,7 +31,7 @@ export const L = {
31
31
  POOL_VGAP: 16,
32
32
  LANE_MIN_H: 140,
33
33
  LANE_CORRIDOR_H: 56, // bottom band for data stores/objects
34
- EMPTY_POOL_H: 60,
34
+ EMPTY_POOL_H: 96, // tall enough for a wrapped 2-line pool label
35
35
  CANVAS_TOP: 72, // room for header bar above diagram
36
36
  CANVAS_LEFT_PAD: 20,
37
37
  CANVAS_RIGHT_PAD: 40,
@@ -389,10 +389,10 @@ export function layoutModel(m) {
389
389
  // ── Pool ordering ──────────────────────────────────────────────────────
390
390
  //
391
391
  // Heuristic:
392
- // - Identify which external pool initiates the process by sending a
393
- // message to the organization pool's start event.
394
- // - Put that pool first, then the organization pool(s), then the rest.
395
- // - Preserve relative order within each group from the input array.
392
+ // - If an external pool initiates the process by messaging the
393
+ // organization's start event, promote it to the top.
394
+ // - Otherwise preserve the modeler's input order (the natural expectation
395
+ // when the start event is a plain origin with no incoming message).
396
396
  function orderPools(m) {
397
397
  const initiators = new Set();
398
398
  const orgStartEvents = new Set();
@@ -409,16 +409,9 @@ function orderPools(m) {
409
409
  initiators.add(mf.fromPool);
410
410
  }
411
411
  }
412
- const primary = [];
413
- const org = [];
414
- const supporting = [];
415
- for (const p of m.pools) {
416
- if (initiators.has(p.id))
417
- primary.push(p);
418
- else if (p.type === "organization")
419
- org.push(p);
420
- else
421
- supporting.push(p);
422
- }
423
- return [...primary, ...org, ...supporting];
412
+ if (initiators.size === 0)
413
+ return [...m.pools]; // respect input order
414
+ const primary = m.pools.filter((p) => initiators.has(p.id));
415
+ const rest = m.pools.filter((p) => !initiators.has(p.id));
416
+ return [...primary, ...rest];
424
417
  }
package/dist/bpmn/svg.js CHANGED
@@ -80,6 +80,24 @@ export function renderPool(pool) {
80
80
  const contentW = pool.w - headerW;
81
81
  const labelCx = headerX + headerW / 2;
82
82
  const labelCy = pool.y + pool.h / 2;
83
+ // Rotated pool labels run vertically, so their available length is the pool
84
+ // HEIGHT. Short (external-empty) pools clip long names — wrap to fit, and
85
+ // render the lines side by side across the header width.
86
+ const maxChars = Math.max(4, Math.floor((pool.h - 14) / 7));
87
+ const nameLines = wrapLabel(pool.name, maxChars, 3);
88
+ const lines = nameLines.length ? nameLines : [pool.name];
89
+ const nameFont = lines.length >= 3 ? 10 : lines.length === 2 ? 11 : 12;
90
+ const nameGap = nameFont + 1;
91
+ const nameSvg = lines
92
+ .map((ln, i) => {
93
+ const lx = labelCx + (i - (lines.length - 1) / 2) * nameGap;
94
+ return `<text x="${lx}" y="${labelCy}" text-anchor="middle"
95
+ dominant-baseline="middle"
96
+ transform="rotate(-90, ${lx}, ${labelCy})"
97
+ fill="#ffffff" font-size="${nameFont}" font-weight="600"
98
+ direction="rtl">${escapeXml(ln)}</text>`;
99
+ })
100
+ .join("\n ");
83
101
  return `
84
102
  <!-- pool: ${escapeXml(pool.poolId)} -->
85
103
  <g class="bpmn-pool" data-pool="${escapeXml(pool.poolId)}">
@@ -87,11 +105,7 @@ export function renderPool(pool) {
87
105
  fill="${bodyFill}" stroke="${bodyStroke}" stroke-width="1.5"/>
88
106
  <rect x="${headerX}" y="${pool.y}" width="${headerW}" height="${pool.h}"
89
107
  fill="${headerFill}" stroke="${bodyStroke}" stroke-width="1.5"/>
90
- <text x="${labelCx}" y="${labelCy}" text-anchor="middle"
91
- dominant-baseline="middle"
92
- transform="rotate(-90, ${labelCx}, ${labelCy})"
93
- fill="#ffffff" font-size="12" font-weight="600"
94
- direction="rtl">${escapeXml(pool.name)}</text>
108
+ ${nameSvg}
95
109
  <line x1="${contentX}" y1="${pool.y}" x2="${contentX}" y2="${pool.y + pool.h}"
96
110
  stroke="${bodyStroke}" stroke-width="1"/>
97
111
  </g>`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sad-mcp",
3
- "version": "2.3.5",
3
+ "version": "2.3.7",
4
4
  "description": "MCP server for Software Analysis and Design course materials at BGU",
5
5
  "type": "module",
6
6
  "bin": {
@@ -459,6 +459,7 @@ When splitting (§5), generate one `<svg id="svg-part-N">` per part. Split `CLAS
459
459
  ## 10. Output
460
460
 
461
461
  - Save as `[system_name]_class_diagram.html`
462
+ - **Visual verification (required) — a loop, not a one-shot.** Structural validity ≠ visual validity. If a browser is available, render the HTML to an image, inspect it, and **fix → re-render → repeat until visually clean**. Scan every pass for: box overlaps; boxes off-canvas/clipped; a **multiplicity or role label landing inside a box** or on a line; **any line crossing through a foreign box**; the association-class tether hitting the wrong association; enum boxes overlapping. Do not report the diagram as done until a rendered pass is clean.
462
463
  - After saving, briefly state: number of classes, enumerations, relationships, whether split (and why), whether VP export is included
463
464
 
464
465
  ---
@@ -44,7 +44,7 @@ Two compartments separated by a horizontal line:
44
44
  - `exit /` actions on leaving
45
45
  - `do /` ongoing activity while in state
46
46
 
47
- Multiple activity lines in one compartment are fine.
47
+ Multiple activity lines in one compartment are fine — but that means different *kinds* of lines (`entry /`, `exit /`, `do /`). Each kind appears **at most once per state**: a state has a single entry behavior, so multiple entry actions are written in the one `entry /` clause separated by `;`, never as a repeated `entry /` line.
48
48
 
49
49
  ## Layout Rules (from lectures)
50
50
 
@@ -51,6 +51,7 @@ The shared files prepended to this prompt define the layout recipes, model shape
51
51
  2. **Activity compartment** — always present; minimum content is `entry / setStatus('SCREAMING_SNAKE_CASE')`
52
52
  - `setStatus()` is required in every state including composite states and archive states — it represents the object's tracked status in the implementation
53
53
  - Additional compartment lines: `entry / action`, `exit / action`, `do / activity`
54
+ - **At most one `entry /` clause per state** (same for `exit /`). UML allows a single entry behavior; multiple entry actions go in that one clause, separated by `;` (wrap to an indented continuation line if too wide) — never write a second `entry /` line
54
55
  - Never produce a state with only a name and no compartment
55
56
 
56
57
  ### Transitions
@@ -257,6 +258,7 @@ Include at the bottom of every SVG:
257
258
  ## 11. Output
258
259
 
259
260
  - Save as `[entity_name]_state_diagram.html`
261
+ - **Visual verification (required) — a loop, not a one-shot.** Structural validity ≠ visual validity. If a browser is available, render the HTML, inspect it, and **fix → re-render → repeat until visually clean**. Scan for: state-box overlaps; transition **labels/guards landing on a box or on another line**; arrows crossing through a foreign state; the initial/final markers misplaced; self-transitions unreadable. Confirm the lifecycle reads as **non-linear** (branches + returns), not a straight line. Don't report done until a rendered pass is clean.
260
262
  - Briefly state: entity modeled, state count, transition count, composite states, whether split
261
263
 
262
264
  ---
@@ -907,6 +907,9 @@ The validator runs locally (no API cost) and catches what the §4 and CR gates c
907
907
  If the response is `✗ Model invalid`, fix every listed error and call the validator again. **Do not write the HTML file until the validator returns `✓ Model valid`.**
908
908
 
909
909
  After the validator passes, save the HTML as `[system_name]_use_case_diagram.html`.
910
+
911
+ **Visual verification (required) — a loop, not a one-shot.** The validator passing is necessary but NOT sufficient: it does not catch layout problems. If a browser is available, render the HTML and inspect it, then **fix → re-render → repeat until visually clean**. Check **both tabs** (toggle which `.diagram-part` is `hidden`) and open at least one UC modal to confirm the wireframe renders. Scan every pass for: ellipse **overlaps**; actors too close (min ~200px); actor figure detached from its own line; a UC crowded into a **center column** (usually a recipient-only human actor on a system-initiated UC — remove it per §4.1); wide ellipses colliding with the boundary title bar; timing/label text on top of a line. Do not report the diagram as done until a rendered pass of each tab is clean.
912
+
910
913
  Briefly report: actor count, use case count, include/extend count, whether split, and any warnings the validator surfaced.
911
914
 
912
915
  ---