@dxos/diagram 0.11.1 → 0.13.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.
Files changed (171) hide show
  1. package/dist/lib/chunk-parse.mjs +9396 -0
  2. package/dist/lib/chunk-parse.mjs.map +1 -0
  3. package/dist/lib/extension.mjs +204 -0
  4. package/dist/lib/extension.mjs.map +1 -0
  5. package/dist/lib/index.mjs +497 -2846
  6. package/dist/lib/index.mjs.map +1 -1
  7. package/dist/types/src/aesthetics.d.ts +12 -0
  8. package/dist/types/src/aesthetics.d.ts.map +1 -0
  9. package/dist/types/src/aesthetics.test.d.ts +2 -0
  10. package/dist/types/src/aesthetics.test.d.ts.map +1 -0
  11. package/dist/types/src/appeal.d.ts +55 -0
  12. package/dist/types/src/appeal.d.ts.map +1 -0
  13. package/dist/types/src/appeal.test.d.ts +2 -0
  14. package/dist/types/src/appeal.test.d.ts.map +1 -0
  15. package/dist/types/src/architecture.d.ts +86 -0
  16. package/dist/types/src/architecture.d.ts.map +1 -0
  17. package/dist/types/src/architecture.test.d.ts +2 -0
  18. package/dist/types/src/architecture.test.d.ts.map +1 -0
  19. package/dist/types/src/compact.d.ts +39 -0
  20. package/dist/types/src/compact.d.ts.map +1 -0
  21. package/dist/types/src/compact.test.d.ts +2 -0
  22. package/dist/types/src/compact.test.d.ts.map +1 -0
  23. package/dist/types/src/diagnostics.d.ts +70 -3
  24. package/dist/types/src/diagnostics.d.ts.map +1 -1
  25. package/dist/types/src/dsl/cli.d.ts +2 -0
  26. package/dist/types/src/dsl/cli.d.ts.map +1 -0
  27. package/dist/types/src/dsl/compile.d.ts +12 -0
  28. package/dist/types/src/dsl/compile.d.ts.map +1 -0
  29. package/dist/types/src/dsl/convert.d.ts +45 -0
  30. package/dist/types/src/dsl/convert.d.ts.map +1 -0
  31. package/dist/types/src/dsl/convert.test.d.ts +2 -0
  32. package/dist/types/src/dsl/convert.test.d.ts.map +1 -0
  33. package/dist/types/src/dsl/dsl.test.d.ts +2 -0
  34. package/dist/types/src/dsl/dsl.test.d.ts.map +1 -0
  35. package/dist/types/src/dsl/gen/diagram.d.ts +3 -0
  36. package/dist/types/src/dsl/gen/diagram.d.ts.map +1 -0
  37. package/dist/types/src/dsl/index.d.ts +7 -0
  38. package/dist/types/src/dsl/index.d.ts.map +1 -0
  39. package/dist/types/src/dsl/parse.d.ts +49 -0
  40. package/dist/types/src/dsl/parse.d.ts.map +1 -0
  41. package/dist/types/src/dsl/print.d.ts +19 -0
  42. package/dist/types/src/dsl/print.d.ts.map +1 -0
  43. package/dist/types/src/dsl/semantic.test.d.ts +2 -0
  44. package/dist/types/src/dsl/semantic.test.d.ts.map +1 -0
  45. package/dist/types/src/dsl/vocabulary.d.ts +61 -0
  46. package/dist/types/src/dsl/vocabulary.d.ts.map +1 -0
  47. package/dist/types/src/dx-svg.d.ts +12 -0
  48. package/dist/types/src/dx-svg.d.ts.map +1 -0
  49. package/dist/types/src/dx-svg.test.d.ts +2 -0
  50. package/dist/types/src/dx-svg.test.d.ts.map +1 -0
  51. package/dist/types/src/extension/complete.d.ts +3 -0
  52. package/dist/types/src/extension/complete.d.ts.map +1 -0
  53. package/dist/types/src/extension/extension.test.d.ts +2 -0
  54. package/dist/types/src/extension/extension.test.d.ts.map +1 -0
  55. package/dist/types/src/extension/highlight.d.ts +14 -0
  56. package/dist/types/src/extension/highlight.d.ts.map +1 -0
  57. package/dist/types/src/extension/index.d.ts +6 -0
  58. package/dist/types/src/extension/index.d.ts.map +1 -0
  59. package/dist/types/src/extension/language.d.ts +13 -0
  60. package/dist/types/src/extension/language.d.ts.map +1 -0
  61. package/dist/types/src/extension/lint.d.ts +21 -0
  62. package/dist/types/src/extension/lint.d.ts.map +1 -0
  63. package/dist/types/src/extension/syntax.d.ts +3 -0
  64. package/dist/types/src/extension/syntax.d.ts.map +1 -0
  65. package/dist/types/src/index.d.ts +14 -0
  66. package/dist/types/src/index.d.ts.map +1 -1
  67. package/dist/types/src/mermaid-engine.d.ts +40 -1
  68. package/dist/types/src/mermaid-engine.d.ts.map +1 -1
  69. package/dist/types/src/mermaid.d.ts +3 -3
  70. package/dist/types/src/mermaid.d.ts.map +1 -1
  71. package/dist/types/src/nudge.d.ts +30 -0
  72. package/dist/types/src/nudge.d.ts.map +1 -0
  73. package/dist/types/src/nudge.test.d.ts +2 -0
  74. package/dist/types/src/nudge.test.d.ts.map +1 -0
  75. package/dist/types/src/objective.d.ts +4 -0
  76. package/dist/types/src/objective.d.ts.map +1 -1
  77. package/dist/types/src/ortho-router.d.ts +13 -1
  78. package/dist/types/src/ortho-router.d.ts.map +1 -1
  79. package/dist/types/src/ports.d.ts +40 -0
  80. package/dist/types/src/ports.d.ts.map +1 -0
  81. package/dist/types/src/ports.test.d.ts +2 -0
  82. package/dist/types/src/ports.test.d.ts.map +1 -0
  83. package/dist/types/src/relations.test.d.ts +2 -0
  84. package/dist/types/src/relations.test.d.ts.map +1 -0
  85. package/dist/types/src/rules.d.ts +30 -0
  86. package/dist/types/src/rules.d.ts.map +1 -0
  87. package/dist/types/src/scene.d.ts +99 -65
  88. package/dist/types/src/scene.d.ts.map +1 -1
  89. package/dist/types/src/score.d.ts +54 -0
  90. package/dist/types/src/score.d.ts.map +1 -0
  91. package/dist/types/src/score.test.d.ts +2 -0
  92. package/dist/types/src/score.test.d.ts.map +1 -0
  93. package/dist/types/src/semantic-engine.d.ts +41 -0
  94. package/dist/types/src/semantic-engine.d.ts.map +1 -0
  95. package/dist/types/src/semantic-engine.test.d.ts +2 -0
  96. package/dist/types/src/semantic-engine.test.d.ts.map +1 -0
  97. package/dist/types/src/semantic-place.d.ts +81 -0
  98. package/dist/types/src/semantic-place.d.ts.map +1 -0
  99. package/dist/types/src/semantic-route.d.ts +133 -0
  100. package/dist/types/src/semantic-route.d.ts.map +1 -0
  101. package/dist/types/src/semantic.d.ts +164 -0
  102. package/dist/types/src/semantic.d.ts.map +1 -0
  103. package/dist/types/src/uml-grid.d.ts.map +1 -1
  104. package/dist/types/src/uml.d.ts +3 -5
  105. package/dist/types/src/uml.d.ts.map +1 -1
  106. package/dist/types/src/view.d.ts +53 -0
  107. package/dist/types/src/view.d.ts.map +1 -0
  108. package/dist/types/src/view.test.d.ts +2 -0
  109. package/dist/types/src/view.test.d.ts.map +1 -0
  110. package/dist/types/tsconfig.tsbuildinfo +1 -1
  111. package/package.json +23 -5
  112. package/rules/DIAGRAM.mdl +388 -0
  113. package/src/aesthetics.test.ts +89 -0
  114. package/src/aesthetics.ts +148 -0
  115. package/src/appeal.test.ts +144 -0
  116. package/src/appeal.ts +586 -0
  117. package/src/architecture.test.ts +94 -0
  118. package/src/architecture.ts +215 -0
  119. package/src/compact.test.ts +67 -0
  120. package/src/compact.ts +219 -0
  121. package/src/diagnostics.test.ts +207 -1
  122. package/src/diagnostics.ts +481 -37
  123. package/src/dsl/cli.ts +46 -0
  124. package/src/dsl/compile.ts +28 -0
  125. package/src/dsl/convert.test.ts +51 -0
  126. package/src/dsl/convert.ts +98 -0
  127. package/src/dsl/diagram.grammar +195 -0
  128. package/src/dsl/dsl.test.ts +286 -0
  129. package/src/dsl/gen/diagram.ts +22 -0
  130. package/src/dsl/index.ts +44 -0
  131. package/src/dsl/parse.ts +1054 -0
  132. package/src/dsl/print.ts +198 -0
  133. package/src/dsl/semantic.test.ts +200 -0
  134. package/src/dsl/vocabulary.ts +150 -0
  135. package/src/dx-svg.test.ts +44 -0
  136. package/src/dx-svg.ts +77 -0
  137. package/src/extension/complete.ts +105 -0
  138. package/src/extension/extension.test.ts +135 -0
  139. package/src/extension/highlight.ts +23 -0
  140. package/src/extension/index.ts +9 -0
  141. package/src/extension/language.ts +31 -0
  142. package/src/extension/lint.ts +60 -0
  143. package/src/extension/syntax.ts +62 -0
  144. package/src/index.ts +14 -0
  145. package/src/mermaid-engine.test.ts +6 -6
  146. package/src/mermaid-engine.ts +569 -119
  147. package/src/mermaid.test.ts +17 -1
  148. package/src/mermaid.ts +30 -18
  149. package/src/nudge.test.ts +155 -0
  150. package/src/nudge.ts +385 -0
  151. package/src/objective.test.ts +2 -0
  152. package/src/objective.ts +17 -1
  153. package/src/ortho-router.ts +240 -80
  154. package/src/ports.test.ts +157 -0
  155. package/src/ports.ts +186 -0
  156. package/src/relations.test.ts +98 -0
  157. package/src/rules.ts +117 -0
  158. package/src/scene.ts +64 -3
  159. package/src/score.test.ts +64 -0
  160. package/src/score.ts +130 -0
  161. package/src/semantic-engine.test.ts +635 -0
  162. package/src/semantic-engine.ts +888 -0
  163. package/src/semantic-place.ts +1508 -0
  164. package/src/semantic-route.ts +1136 -0
  165. package/src/semantic.ts +278 -0
  166. package/src/typings.d.ts +8 -0
  167. package/src/uml-grid.ts +6 -1
  168. package/src/uml.test.ts +5 -1
  169. package/src/uml.ts +24 -11
  170. package/src/view.test.ts +139 -0
  171. package/src/view.ts +376 -0
package/src/score.ts ADDED
@@ -0,0 +1,130 @@
1
+ //
2
+ // Copyright 2026 DXOS.org
3
+ //
4
+
5
+ //
6
+ // Normalized scoring: every heuristic — an objective's constraints and cost terms, or any other
7
+ // judge of a layout — reports one score in [0, 1] (0 bad, 1 good), so heterogeneous scorers read
8
+ // as one list and combine into one number. `Objective` stays the engines' selector; this is the
9
+ // view of the same measurements a person (or a later judge) compares layouts by.
10
+ //
11
+
12
+ import * as Effect from 'effect/Effect';
13
+
14
+ import type * as Objective from './objective.ts';
15
+
16
+ /** What produced a score; open so a host can add its own kinds of judge alongside the built-in ones. */
17
+ export type Kind = 'constraint' | 'cost' | (string & {});
18
+
19
+ export type Result = {
20
+ /** In [0, 1]: 0 is bad, 1 is good. Meaningless when `error` is set. */
21
+ score: number;
22
+ /** Why, in a line or two: the violations, or the raw measure behind a cost. */
23
+ detail?: string;
24
+ /** The scorer could not judge (e.g. its model was unreachable); left out of {@link overall}. */
25
+ error?: string;
26
+ };
27
+
28
+ export type Entry = {
29
+ id: string;
30
+ kind: Kind;
31
+ description: string;
32
+ };
33
+
34
+ /**
35
+ * One judge of a subject — by default the layout. An effect, so a judge that asks something outside the
36
+ * process can run late, be interrupted, and name what it needs in `R`.
37
+ */
38
+ export type Scorer<S = Objective.Layout, R = never> = Entry & {
39
+ evaluate: (subject: S) => Effect.Effect<Result, never, R>;
40
+ };
41
+
42
+ /** Several scores from one evaluation, in `entries` order: for a judge that answers many questions per call. */
43
+ export type Batch<S = Objective.Layout, R = never> = {
44
+ entries: readonly Entry[];
45
+ evaluate: (subject: S) => Effect.Effect<readonly Result[], never, R>;
46
+ };
47
+
48
+ export type Source<S = Objective.Layout, R = never> = Scorer<S, R> | Batch<S, R>;
49
+
50
+ export type Scored = Result & Entry;
51
+
52
+ /**
53
+ * Weighted cost at which a cost term scores 0.5: one crossing's worth (see `Objective.crossings`), so
54
+ * "half marks" means "as bad as one crossing" on every term.
55
+ */
56
+ export const HALF_COST = 3;
57
+
58
+ /** Maps a weighted cost in [0, ∞) onto (0, 1]: 1 at no cost, 0.5 at `half`, falling off hyperbolically. */
59
+ export const fromCost = (weighted: number, half: number = HALF_COST): number => 1 / (1 + Math.max(0, weighted) / half);
60
+
61
+ /** A hard requirement: 1 when it holds, 0 when anything violates it. */
62
+ export const constraintScorer = (constraint: Objective.Constraint): Scorer => ({
63
+ id: constraint.id,
64
+ kind: 'constraint',
65
+ description: constraint.description,
66
+ evaluate: (layout) =>
67
+ Effect.sync(() => {
68
+ const violations = constraint.violations(layout);
69
+ return { score: violations.length ? 0 : 1, ...(violations.length ? { detail: violations.join('\n') } : {}) };
70
+ }),
71
+ });
72
+
73
+ /** A soft preference: its weighted cost through {@link fromCost}. */
74
+ export const costScorer = (term: Objective.CostTerm, half: number = HALF_COST): Scorer => ({
75
+ id: term.id,
76
+ kind: 'cost',
77
+ description: term.description,
78
+ evaluate: (layout) =>
79
+ Effect.sync(() => {
80
+ const value = term.measure(layout);
81
+ const weighted = value * term.weight;
82
+ return { score: fromCost(weighted, half), detail: `${round(value)} × ${term.weight} = ${round(weighted)}` };
83
+ }),
84
+ });
85
+
86
+ /** Every constraint and cost term of an objective, constraints first. */
87
+ export const fromObjective = (objective: Objective.Objective, half: number = HALF_COST): Scorer[] => [
88
+ ...objective.constraints.map(constraintScorer),
89
+ ...objective.costs.map((term) => costScorer(term, half)),
90
+ ];
91
+
92
+ const isBatch = <S, R>(source: Source<S, R>): source is Batch<S, R> => 'entries' in source;
93
+
94
+ /** Runs every source against one subject concurrently; results keep the sources' order, batches flattened. */
95
+ export const evaluate = <S, R>(sources: readonly Source<S, R>[], subject: S): Effect.Effect<Scored[], never, R> =>
96
+ Effect.all(
97
+ sources.map((source) =>
98
+ isBatch(source)
99
+ ? source
100
+ .evaluate(subject)
101
+ .pipe(Effect.map((results) => source.entries.map((entry, index) => ({ ...entry, ...results[index] }))))
102
+ : source
103
+ .evaluate(subject)
104
+ .pipe(
105
+ Effect.map((result) => [
106
+ { id: source.id, kind: source.kind, description: source.description, ...result },
107
+ ]),
108
+ ),
109
+ ),
110
+ { concurrency: 'unbounded' },
111
+ ).pipe(Effect.map((groups) => groups.flat()));
112
+
113
+ /**
114
+ * One number for a set of scores: the worst constraint gates the mean of everything else, so a
115
+ * layout that breaks a constraint scores 0 however good it otherwise is — the same precedence
116
+ * `Objective.select` gives violations over cost — while every other kind contributes equally.
117
+ * `undefined` when nothing could be judged, so a set of failed scorers never reads as a perfect score.
118
+ */
119
+ export const overall = (all: readonly Pick<Scored, 'kind' | 'score' | 'error'>[]): number | undefined => {
120
+ const scores = all.filter(({ error }) => error === undefined);
121
+ if (scores.length === 0) {
122
+ return undefined;
123
+ }
124
+ const gate = Math.min(1, ...scores.filter(({ kind }) => kind === 'constraint').map(({ score }) => score));
125
+ const rest = scores.filter(({ kind }) => kind !== 'constraint');
126
+ const mean = rest.length ? rest.reduce((total, { score }) => total + score, 0) / rest.length : 1;
127
+ return gate * mean;
128
+ };
129
+
130
+ const round = (value: number) => (Number.isInteger(value) ? `${value}` : value.toFixed(2));