@likec4/core 1.4.0 → 1.6.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2023-2024 Denis Davydkov
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # `@likec4/core`
2
2
 
3
- [docs](https://docs.likec4.dev/)
3
+ [docs](https://likec4.dev/)
4
4
 
5
5
  Package contains:
6
6
 
@@ -57,7 +57,7 @@ export declare const ElementColors: {
57
57
  readonly fill: "#A35829";
58
58
  readonly stroke: "#7E451D";
59
59
  readonly hiContrast: "#FFE0C2";
60
- readonly loContrast: "#FFA057";
60
+ readonly loContrast: "#f9b27c";
61
61
  };
62
62
  readonly indigo: {
63
63
  readonly fill: "#6366f1";
@@ -60,7 +60,7 @@ export const ElementColors = {
60
60
  fill: '#A35829',
61
61
  stroke: '#7E451D',
62
62
  hiContrast: '#FFE0C2',
63
- loContrast: '#FFA057'
63
+ loContrast: '#f9b27c'
64
64
  },
65
65
  indigo: {
66
66
  // fill: colors.indigo[500],
@@ -60,7 +60,7 @@ export declare const defaultTheme: {
60
60
  readonly fill: "#A35829";
61
61
  readonly stroke: "#7E451D";
62
62
  readonly hiContrast: "#FFE0C2";
63
- readonly loContrast: "#FFA057";
63
+ readonly loContrast: "#f9b27c";
64
64
  };
65
65
  readonly indigo: {
66
66
  readonly fill: "#6366f1";
@@ -126,7 +126,7 @@ export declare const defaultTheme: {
126
126
  labelColor: "#cbd5e1";
127
127
  };
128
128
  };
129
- font: "Helvetica";
129
+ font: "Arial";
130
130
  shadow: "#0a0a0a";
131
131
  };
132
132
  export { ElementColors, RelationshipColors };
@@ -3,7 +3,7 @@ import { RelationshipColors } from './relationships';
3
3
  export const defaultTheme = {
4
4
  elements: ElementColors,
5
5
  relationships: RelationshipColors,
6
- font: 'Helvetica',
6
+ font: 'Arial',
7
7
  shadow: '#0a0a0a'
8
8
  };
9
9
  export { ElementColors, RelationshipColors };
@@ -1,5 +1,6 @@
1
1
  import type { IconUrl } from './_common';
2
2
  import type { BorderStyle, ElementKind, ElementShape, Fqn, Tag } from './element';
3
+ import type { RelationshipArrowType, RelationshipLineType } from './relation';
3
4
  import type { ThemeColor } from './theme';
4
5
  import type { ViewID } from './view';
5
6
  interface BaseExpr {
@@ -16,6 +17,7 @@ interface BaseExpr {
16
17
  inout?: never;
17
18
  incoming?: never;
18
19
  outgoing?: never;
20
+ customRelation?: never;
19
21
  }
20
22
  export interface ElementRefExpr extends Omit<BaseExpr, 'element' | 'isDescedants'> {
21
23
  element: Fqn;
@@ -57,6 +59,7 @@ export interface ElementTagExpr extends Omit<BaseExpr, 'elementTag' | 'isEqual'>
57
59
  export declare function isElementTagExpr(expr: Expression): expr is ElementTagExpr;
58
60
  export type ElementExpression = ElementRefExpr | WildcardExpr | ElementKindExpr | ElementTagExpr | ExpandedElementExpr;
59
61
  export declare function isElement(expr: Expression): expr is ElementExpression;
62
+ export type ElementPredicateExpression = ElementExpression | CustomElementExpr;
60
63
  export interface RelationExpr extends Omit<BaseExpr, 'source' | 'target'> {
61
64
  source: ElementExpression;
62
65
  target: ElementExpression;
@@ -75,8 +78,20 @@ export interface OutgoingExpr extends Omit<BaseExpr, 'outgoing'> {
75
78
  outgoing: ElementExpression;
76
79
  }
77
80
  export declare function isOutgoing(expr: Expression): expr is OutgoingExpr;
78
- export type AnyRelationExpression = RelationExpr | InOutExpr | IncomingExpr | OutgoingExpr;
79
- export declare function isAnyRelation(expr: Expression): expr is AnyRelationExpression;
80
- export type Expression = ElementExpression | CustomElementExpr | AnyRelationExpression;
81
+ export interface CustomRelationExpr extends Omit<BaseExpr, 'customRelation'> {
82
+ customRelation: {
83
+ relation: RelationExpr;
84
+ title?: string;
85
+ color?: ThemeColor;
86
+ line?: RelationshipLineType;
87
+ head?: RelationshipArrowType;
88
+ tail?: RelationshipArrowType;
89
+ };
90
+ }
91
+ export declare function isCustomRelationExpr(expr: Expression): expr is CustomRelationExpr;
92
+ export type RelationExpression = RelationExpr | InOutExpr | IncomingExpr | OutgoingExpr;
93
+ export type RelationPredicateExpression = RelationExpression | CustomRelationExpr;
94
+ export declare function isAnyRelation(expr: Expression): expr is RelationPredicateExpression;
95
+ export type Expression = ElementPredicateExpression | RelationPredicateExpression;
81
96
  export {};
82
97
  //# sourceMappingURL=expression.d.ts.map
@@ -35,8 +35,11 @@ export function isIncoming(expr) {
35
35
  export function isOutgoing(expr) {
36
36
  return 'outgoing' in expr;
37
37
  }
38
+ export function isCustomRelationExpr(expr) {
39
+ return 'customRelation' in expr;
40
+ }
38
41
  export function isAnyRelation(expr) {
39
- return isRelation(expr) || isInOut(expr) || isIncoming(expr) || isOutgoing(expr);
42
+ return isRelation(expr) || isInOut(expr) || isIncoming(expr) || isOutgoing(expr) || isCustomRelationExpr(expr);
40
43
  }
41
44
  // export const Expr = {
42
45
  // isElementRef,
@@ -1,10 +1,10 @@
1
1
  import type { Element, Fqn } from './element';
2
2
  import type { Relation, RelationID } from './relation';
3
- import type { ComputedView, View, ViewID } from './view';
3
+ import type { ComputedView, LikeC4View, ViewID } from './view';
4
4
  export interface LikeC4Model {
5
5
  elements: Record<Fqn, Element>;
6
6
  relations: Record<RelationID, Relation>;
7
- views: Record<ViewID, View>;
7
+ views: Record<ViewID, LikeC4View>;
8
8
  }
9
9
  export interface LikeC4ComputedModel {
10
10
  elements: Record<Fqn, Element>;
@@ -5,7 +5,7 @@ import type { ThemeColor } from './theme';
5
5
  export type RelationID = Opaque<string, 'RelationID'>;
6
6
  export type RelationshipKind = Opaque<string, 'RelationshipKind'>;
7
7
  export type RelationshipLineType = 'dashed' | 'solid' | 'dotted';
8
- export type RelationshipArrowType = 'none' | 'normal' | 'onormal' | 'diamond' | 'odiamond' | 'crow' | 'open' | 'vee';
8
+ export type RelationshipArrowType = 'none' | 'normal' | 'onormal' | 'dot' | 'odot' | 'diamond' | 'odiamond' | 'crow' | 'open' | 'vee';
9
9
  export declare const DefaultLineStyle = "dashed";
10
10
  export declare const DefaultArrowType = "normal";
11
11
  export declare const DefaultRelationshipColor = "gray";
@@ -19,7 +19,7 @@ export type RelationshipThemeColors = {
19
19
  [key in ThemeColor]: RelationshipThemeColorValues;
20
20
  };
21
21
  export interface LikeC4Theme {
22
- font: 'Helvetica';
22
+ font: 'Arial';
23
23
  shadow: ColorLiteral;
24
24
  relationships: RelationshipThemeColors;
25
25
  elements: ElementThemeColors;
@@ -1,18 +1,18 @@
1
1
  import type { IconUrl, NonEmptyArray, Point, XYPosition } from './_common';
2
2
  import type { ElementKind, ElementShape, ElementStyle, Fqn, Tag } from './element';
3
- import type { CustomElementExpr, ElementExpression, Expression } from './expression';
3
+ import type { ElementExpression, ElementPredicateExpression, Expression } from './expression';
4
4
  import type { Opaque } from './opaque';
5
5
  import type { RelationID, RelationshipArrowType, RelationshipLineType } from './relation';
6
- import type { ColorLiteral, ThemeColor } from './theme';
6
+ import type { ThemeColor } from './theme';
7
7
  export type ViewID = Opaque<string, 'ViewID'>;
8
- export type ViewRuleExpression = {
8
+ export type ViewRulePredicate = {
9
9
  include: Expression[];
10
10
  exclude?: never;
11
11
  } | {
12
12
  include?: never;
13
13
  exclude: Expression[];
14
14
  };
15
- export declare function isViewRuleExpression(rule: ViewRule): rule is ViewRuleExpression;
15
+ export declare function isViewRulePredicate(rule: ViewRule): rule is ViewRulePredicate;
16
16
  export interface ViewRuleStyle {
17
17
  targets: ElementExpression[];
18
18
  style: ElementStyle & {
@@ -27,8 +27,8 @@ export interface ViewRuleAutoLayout {
27
27
  autoLayout: AutoLayoutDirection;
28
28
  }
29
29
  export declare function isViewRuleAutoLayout(rule: ViewRule): rule is ViewRuleAutoLayout;
30
- export type ViewRule = ViewRuleExpression | ViewRuleStyle | ViewRuleAutoLayout;
31
- export interface BasicView<ViewType extends 'element' | 'dynamic' = 'element' | 'dynamic'> {
30
+ export type ViewRule = ViewRulePredicate | ViewRuleStyle | ViewRuleAutoLayout;
31
+ export interface BasicView<ViewType extends 'element' | 'dynamic'> {
32
32
  readonly __?: ViewType;
33
33
  readonly id: ViewID;
34
34
  readonly title: string | null;
@@ -59,13 +59,13 @@ export interface BasicElementView extends BasicView<'element'> {
59
59
  readonly viewOf?: Fqn;
60
60
  readonly rules: ViewRule[];
61
61
  }
62
- export interface StrictElementView extends BasicElementView {
62
+ export interface ScopedElementView extends BasicElementView {
63
63
  readonly viewOf: Fqn;
64
64
  }
65
65
  export interface ExtendsElementView extends BasicElementView {
66
66
  readonly extends: ViewID;
67
67
  }
68
- export type ElementView = StrictElementView | ExtendsElementView | BasicElementView;
68
+ export type ElementView = ScopedElementView | ExtendsElementView | BasicElementView;
69
69
  export interface DynamicViewStep {
70
70
  readonly source: Fqn;
71
71
  readonly target: Fqn;
@@ -73,7 +73,7 @@ export interface DynamicViewStep {
73
73
  readonly isBackward?: boolean;
74
74
  }
75
75
  export type DynamicViewIncludeRule = {
76
- include: (ElementExpression | CustomElementExpr)[];
76
+ include: ElementPredicateExpression[];
77
77
  };
78
78
  export declare function isDynamicViewIncludeRule(rule: DynamicViewRule): rule is DynamicViewIncludeRule;
79
79
  export type DynamicViewRule = DynamicViewIncludeRule | ViewRuleStyle | ViewRuleAutoLayout;
@@ -82,11 +82,11 @@ export interface DynamicView extends BasicView<'dynamic'> {
82
82
  readonly steps: DynamicViewStep[];
83
83
  readonly rules: DynamicViewRule[];
84
84
  }
85
- export type View = ElementView | DynamicView;
86
- export declare function isDynamicView(view: View): view is DynamicView;
87
- export declare function isElementView(view: View): view is ElementView;
88
- export declare function isExtendsElementView(view: View): view is ExtendsElementView;
89
- export declare function isStrictElementView(view: View): view is StrictElementView;
85
+ export type LikeC4View = ElementView | DynamicView;
86
+ export declare function isDynamicView(view: LikeC4View): view is DynamicView;
87
+ export declare function isElementView(view: LikeC4View): view is ElementView;
88
+ export declare function isExtendsElementView(view: LikeC4View): view is ExtendsElementView;
89
+ export declare function isScopedElementView(view: LikeC4View): view is ScopedElementView;
90
90
  export type NodeId = Fqn;
91
91
  export type EdgeId = Opaque<string, 'EdgeId'>;
92
92
  export type StepEdgeIdLiteral = `step-${number}`;
@@ -119,6 +119,10 @@ export interface ComputedNode {
119
119
  navigateTo?: ViewID;
120
120
  level: number;
121
121
  depth?: number;
122
+ /**
123
+ * If this node was customized in the view
124
+ */
125
+ isCustomized?: boolean;
122
126
  }
123
127
  export interface ComputedEdge {
124
128
  id: EdgeId;
@@ -131,6 +135,10 @@ export interface ComputedEdge {
131
135
  line?: RelationshipLineType;
132
136
  head?: RelationshipArrowType;
133
137
  tail?: RelationshipArrowType;
138
+ /**
139
+ * If this edge is derived from custom relationship predicate
140
+ */
141
+ isCustomized?: boolean;
134
142
  /**
135
143
  * For layouting purposes
136
144
  * @default 'forward'
@@ -157,29 +165,15 @@ export type BBox = {
157
165
  width: number;
158
166
  height: number;
159
167
  };
160
- export interface DiagramLabel {
161
- align: 'left' | 'right' | 'center';
162
- fontStyle?: 'bold' | 'normal';
163
- color?: ColorLiteral;
164
- fontSize: number;
165
- pt: Point;
166
- width: number;
167
- text: string;
168
- }
169
168
  export interface DiagramNode extends ComputedNode {
170
169
  width: number;
171
170
  height: number;
172
171
  position: Point;
173
- labels: DiagramLabel[];
172
+ labelBBox: BBox;
174
173
  }
175
174
  export interface DiagramEdge extends ComputedEdge {
176
175
  points: NonEmptyArray<Point>;
177
- headArrow?: NonEmptyArray<Point>;
178
- headArrowPoint?: Point;
179
- tailArrow?: NonEmptyArray<Point>;
180
- tailArrowPoint?: Point;
181
- labels?: NonEmptyArray<DiagramLabel>;
182
- labelBBox?: BBox;
176
+ labelBBox?: BBox | null;
183
177
  }
184
178
  export interface DiagramView extends Omit<ComputedView, 'nodes' | 'edges'> {
185
179
  readonly nodes: DiagramNode[];
@@ -1,5 +1,5 @@
1
1
  import { isNullish } from 'remeda';
2
- export function isViewRuleExpression(rule) {
2
+ export function isViewRulePredicate(rule) {
3
3
  return (('include' in rule && Array.isArray(rule.include))
4
4
  || ('exclude' in rule && Array.isArray(rule.exclude)));
5
5
  }
@@ -21,7 +21,7 @@ export function isElementView(view) {
21
21
  export function isExtendsElementView(view) {
22
22
  return isElementView(view) && 'extends' in view;
23
23
  }
24
- export function isStrictElementView(view) {
24
+ export function isScopedElementView(view) {
25
25
  return isElementView(view) && 'viewOf' in view;
26
26
  }
27
27
  export function StepEdgeId(step) {
@@ -1,4 +1,5 @@
1
1
  import type { Element, Fqn } from '../types';
2
+ type Predicate<T> = (x: T) => boolean;
2
3
  export declare function nameFromFqn(fqn: Fqn): string;
3
4
  export declare function isAncestor<E extends {
4
5
  id: Fqn;
@@ -12,6 +13,9 @@ export declare function isDescendantOf<E extends {
12
13
  export declare function notDescendantOf(ancestors: Element[]): (e: Element) => boolean;
13
14
  export declare function commonAncestor(first: Fqn, second: Fqn): Fqn | null;
14
15
  export declare function parentFqn(fqn: Fqn): Fqn | null;
16
+ export declare function parentFqnPredicate<T extends {
17
+ parent: Fqn | null;
18
+ }>(parent: Fqn): Predicate<T>;
15
19
  /**
16
20
  * Get all ancestor elements (i.e. parent, parent’s parent, etc.)
17
21
  * (from closest to root)
@@ -31,4 +35,5 @@ export declare function compareFqnHierarchically<T extends string = string>(a: T
31
35
  export declare function compareByFqnHierarchically<T extends {
32
36
  id: string;
33
37
  }>(a: T, b: T): number;
38
+ export {};
34
39
  //# sourceMappingURL=fqn.d.ts.map
package/dist/utils/fqn.js CHANGED
@@ -52,6 +52,10 @@ export function parentFqn(fqn) {
52
52
  }
53
53
  return null;
54
54
  }
55
+ export function parentFqnPredicate(parent) {
56
+ const prefix = parent + '.';
57
+ return (e) => !!e.parent && (e.parent === parent || e.parent.startsWith(prefix));
58
+ }
55
59
  /**
56
60
  * Get all ancestor elements (i.e. parent, parent’s parent, etc.)
57
61
  * (from closest to root)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@likec4/core",
3
- "version": "1.4.0",
3
+ "version": "1.6.0",
4
4
  "license": "MIT",
5
5
  "homepage": "https://likec4.dev",
6
6
  "author": "Denis Davydkov <denis@davydkov.com>",
@@ -66,16 +66,16 @@
66
66
  }
67
67
  },
68
68
  "dependencies": {
69
- "remeda": "^1.61.0",
69
+ "remeda": "^2.3.0",
70
70
  "ts-custom-error": "^3.3.1",
71
- "type-fest": "^4.18.2"
71
+ "type-fest": "^4.21.0"
72
72
  },
73
73
  "devDependencies": {
74
- "@likec4/tsconfig": "1.4.0",
74
+ "@likec4/tsconfig": "1.6.0",
75
75
  "@total-typescript/ts-reset": "^0.5.1",
76
- "execa": "^9.1.0",
77
- "typescript": "^5.4.5",
78
- "vitest": "~1.5.3"
76
+ "execa": "^9.3.0",
77
+ "typescript": "^5.5.3",
78
+ "vitest": "~2.0.3"
79
79
  },
80
- "packageManager": "yarn@4.3.0"
80
+ "packageManager": "yarn@4.3.1"
81
81
  }
@@ -65,7 +65,7 @@ export const ElementColors = {
65
65
  fill: '#A35829',
66
66
  stroke: '#7E451D',
67
67
  hiContrast: '#FFE0C2',
68
- loContrast: '#FFA057'
68
+ loContrast: '#f9b27c'
69
69
  },
70
70
  indigo: {
71
71
  // fill: colors.indigo[500],
@@ -5,7 +5,7 @@ import { RelationshipColors } from './relationships'
5
5
  export const defaultTheme = {
6
6
  elements: ElementColors,
7
7
  relationships: RelationshipColors,
8
- font: 'Helvetica',
8
+ font: 'Arial',
9
9
  shadow: '#0a0a0a'
10
10
  } satisfies LikeC4Theme
11
11
 
@@ -1,5 +1,6 @@
1
1
  import type { IconUrl } from './_common'
2
2
  import type { BorderStyle, ElementKind, ElementShape, Fqn, Tag } from './element'
3
+ import type { RelationshipArrowType, RelationshipLineType } from './relation'
3
4
  import type { ThemeColor } from './theme'
4
5
  import type { ViewID } from './view'
5
6
 
@@ -17,6 +18,7 @@ interface BaseExpr {
17
18
  inout?: never
18
19
  incoming?: never
19
20
  outgoing?: never
21
+ customRelation?: never
20
22
  }
21
23
 
22
24
  export interface ElementRefExpr extends Omit<BaseExpr, 'element' | 'isDescedants'> {
@@ -89,6 +91,8 @@ export function isElement(expr: Expression): expr is ElementExpression {
89
91
  || isExpandedElementExpr(expr)
90
92
  }
91
93
 
94
+ export type ElementPredicateExpression = ElementExpression | CustomElementExpr
95
+
92
96
  export interface RelationExpr extends Omit<BaseExpr, 'source' | 'target'> {
93
97
  source: ElementExpression
94
98
  target: ElementExpression
@@ -118,13 +122,29 @@ export function isOutgoing(expr: Expression): expr is OutgoingExpr {
118
122
  return 'outgoing' in expr
119
123
  }
120
124
 
121
- export type AnyRelationExpression = RelationExpr | InOutExpr | IncomingExpr | OutgoingExpr
125
+ export interface CustomRelationExpr extends Omit<BaseExpr, 'customRelation'> {
126
+ customRelation: {
127
+ relation: RelationExpr
128
+ title?: string
129
+ color?: ThemeColor
130
+ line?: RelationshipLineType
131
+ head?: RelationshipArrowType
132
+ tail?: RelationshipArrowType
133
+ }
134
+ }
135
+ export function isCustomRelationExpr(expr: Expression): expr is CustomRelationExpr {
136
+ return 'customRelation' in expr
137
+ }
138
+
139
+ export type RelationExpression = RelationExpr | InOutExpr | IncomingExpr | OutgoingExpr
140
+
141
+ export type RelationPredicateExpression = RelationExpression | CustomRelationExpr
122
142
 
123
- export function isAnyRelation(expr: Expression): expr is AnyRelationExpression {
124
- return isRelation(expr) || isInOut(expr) || isIncoming(expr) || isOutgoing(expr)
143
+ export function isAnyRelation(expr: Expression): expr is RelationPredicateExpression {
144
+ return isRelation(expr) || isInOut(expr) || isIncoming(expr) || isOutgoing(expr) || isCustomRelationExpr(expr)
125
145
  }
126
146
 
127
- export type Expression = ElementExpression | CustomElementExpr | AnyRelationExpression
147
+ export type Expression = ElementPredicateExpression | RelationPredicateExpression
128
148
 
129
149
  // export const Expr = {
130
150
  // isElementRef,
@@ -1,11 +1,11 @@
1
1
  import type { Element, Fqn } from './element'
2
2
  import type { Relation, RelationID } from './relation'
3
- import type { ComputedView, View, ViewID } from './view'
3
+ import type { ComputedView, LikeC4View, ViewID } from './view'
4
4
 
5
5
  export interface LikeC4Model {
6
6
  elements: Record<Fqn, Element>
7
7
  relations: Record<RelationID, Relation>
8
- views: Record<ViewID, View>
8
+ views: Record<ViewID, LikeC4View>
9
9
  }
10
10
 
11
11
  export interface LikeC4ComputedModel {
@@ -13,6 +13,8 @@ export type RelationshipArrowType =
13
13
  | 'none'
14
14
  | 'normal'
15
15
  | 'onormal'
16
+ | 'dot'
17
+ | 'odot'
16
18
  | 'diamond'
17
19
  | 'odiamond'
18
20
  | 'crow'
@@ -39,7 +39,7 @@ export type RelationshipThemeColors = {
39
39
  }
40
40
 
41
41
  export interface LikeC4Theme {
42
- font: 'Helvetica' // for now only support Helvetica
42
+ font: 'Arial'
43
43
  shadow: ColorLiteral
44
44
  relationships: RelationshipThemeColors
45
45
  elements: ElementThemeColors
package/src/types/view.ts CHANGED
@@ -1,15 +1,15 @@
1
1
  import { isNullish } from 'remeda'
2
2
  import type { IconUrl, NonEmptyArray, Point, XYPosition } from './_common'
3
3
  import type { ElementKind, ElementShape, ElementStyle, Fqn, Tag } from './element'
4
- import type { CustomElementExpr, ElementExpression, Expression } from './expression'
4
+ import type { ElementExpression, ElementPredicateExpression, Expression } from './expression'
5
5
  import type { Opaque } from './opaque'
6
6
  import type { RelationID, RelationshipArrowType, RelationshipLineType } from './relation'
7
- import type { ColorLiteral, ThemeColor } from './theme'
7
+ import type { ThemeColor } from './theme'
8
8
 
9
9
  // Full-qualified-name
10
10
  export type ViewID = Opaque<string, 'ViewID'>
11
11
 
12
- export type ViewRuleExpression =
12
+ export type ViewRulePredicate =
13
13
  | {
14
14
  include: Expression[]
15
15
  exclude?: never
@@ -18,7 +18,7 @@ export type ViewRuleExpression =
18
18
  include?: never
19
19
  exclude: Expression[]
20
20
  }
21
- export function isViewRuleExpression(rule: ViewRule): rule is ViewRuleExpression {
21
+ export function isViewRulePredicate(rule: ViewRule): rule is ViewRulePredicate {
22
22
  return (
23
23
  ('include' in rule && Array.isArray(rule.include))
24
24
  || ('exclude' in rule && Array.isArray(rule.exclude))
@@ -45,9 +45,9 @@ export function isViewRuleAutoLayout(rule: ViewRule): rule is ViewRuleAutoLayout
45
45
  return 'autoLayout' in rule
46
46
  }
47
47
 
48
- export type ViewRule = ViewRuleExpression | ViewRuleStyle | ViewRuleAutoLayout
48
+ export type ViewRule = ViewRulePredicate | ViewRuleStyle | ViewRuleAutoLayout
49
49
 
50
- export interface BasicView<ViewType extends 'element' | 'dynamic' = 'element' | 'dynamic'> {
50
+ export interface BasicView<ViewType extends 'element' | 'dynamic'> {
51
51
  readonly __?: ViewType
52
52
  readonly id: ViewID
53
53
  readonly title: string | null
@@ -81,14 +81,14 @@ export interface BasicElementView extends BasicView<'element'> {
81
81
  readonly viewOf?: Fqn
82
82
  readonly rules: ViewRule[]
83
83
  }
84
- export interface StrictElementView extends BasicElementView {
84
+ export interface ScopedElementView extends BasicElementView {
85
85
  readonly viewOf: Fqn
86
86
  }
87
87
 
88
88
  export interface ExtendsElementView extends BasicElementView {
89
89
  readonly extends: ViewID
90
90
  }
91
- export type ElementView = StrictElementView | ExtendsElementView | BasicElementView
91
+ export type ElementView = ScopedElementView | ExtendsElementView | BasicElementView
92
92
 
93
93
  export interface DynamicViewStep {
94
94
  readonly source: Fqn
@@ -98,7 +98,7 @@ export interface DynamicViewStep {
98
98
  }
99
99
 
100
100
  export type DynamicViewIncludeRule = {
101
- include: (ElementExpression | CustomElementExpr)[]
101
+ include: ElementPredicateExpression[]
102
102
  }
103
103
 
104
104
  export function isDynamicViewIncludeRule(rule: DynamicViewRule): rule is DynamicViewIncludeRule {
@@ -114,27 +114,26 @@ export interface DynamicView extends BasicView<'dynamic'> {
114
114
  readonly rules: DynamicViewRule[]
115
115
  }
116
116
 
117
- export type View = ElementView | DynamicView
117
+ export type LikeC4View = ElementView | DynamicView
118
118
 
119
- export function isDynamicView(view: View): view is DynamicView {
119
+ export function isDynamicView(view: LikeC4View): view is DynamicView {
120
120
  return view.__ === 'dynamic'
121
121
  }
122
- export function isElementView(view: View): view is ElementView {
122
+ export function isElementView(view: LikeC4View): view is ElementView {
123
123
  return isNullish(view.__) || view.__ === 'element'
124
124
  }
125
125
 
126
- export function isExtendsElementView(view: View): view is ExtendsElementView {
126
+ export function isExtendsElementView(view: LikeC4View): view is ExtendsElementView {
127
127
  return isElementView(view) && 'extends' in view
128
128
  }
129
129
 
130
- export function isStrictElementView(view: View): view is StrictElementView {
130
+ export function isScopedElementView(view: LikeC4View): view is ScopedElementView {
131
131
  return isElementView(view) && 'viewOf' in view
132
132
  }
133
133
 
134
134
  export type NodeId = Fqn
135
135
 
136
136
  export type EdgeId = Opaque<string, 'EdgeId'>
137
-
138
137
  export type StepEdgeIdLiteral = `step-${number}`
139
138
  export type StepEdgeId = Opaque<StepEdgeIdLiteral, 'EdgeId'>
140
139
  export function StepEdgeId(step: number): StepEdgeId {
@@ -177,6 +176,10 @@ export interface ComputedNode {
177
176
  level: number
178
177
  // For compound nodes, the max depth of nested nodes
179
178
  depth?: number
179
+ /**
180
+ * If this node was customized in the view
181
+ */
182
+ isCustomized?: boolean
180
183
  }
181
184
 
182
185
  export interface ComputedEdge {
@@ -190,7 +193,10 @@ export interface ComputedEdge {
190
193
  line?: RelationshipLineType
191
194
  head?: RelationshipArrowType
192
195
  tail?: RelationshipArrowType
193
-
196
+ /**
197
+ * If this edge is derived from custom relationship predicate
198
+ */
199
+ isCustomized?: boolean
194
200
  /**
195
201
  * For layouting purposes
196
202
  * @default 'forward'
@@ -227,35 +233,17 @@ export type BBox = {
227
233
  height: number
228
234
  }
229
235
 
230
- export interface DiagramLabel {
231
- align: 'left' | 'right' | 'center'
232
- fontStyle?: 'bold' | 'normal'
233
- color?: ColorLiteral
234
- fontSize: number
235
- pt: Point
236
- width: number
237
- text: string
238
- }
239
-
240
236
  export interface DiagramNode extends ComputedNode {
241
237
  width: number
242
238
  height: number
243
- position: Point // Absolute position, top left
244
- // relative: Point // Top left, relative to parent
245
- labels: DiagramLabel[]
239
+ // Absolute position, top left
240
+ position: Point
241
+ labelBBox: BBox
246
242
  }
247
243
 
248
244
  export interface DiagramEdge extends ComputedEdge {
249
245
  points: NonEmptyArray<Point>
250
- // Polygons are used to draw arrows
251
- headArrow?: NonEmptyArray<Point>
252
- // Draw arrow from the last point of the edge to this point
253
- headArrowPoint?: Point
254
- tailArrow?: NonEmptyArray<Point>
255
- // Draw arrow from the first point of the edge to this point
256
- tailArrowPoint?: Point
257
- labels?: NonEmptyArray<DiagramLabel>
258
- labelBBox?: BBox
246
+ labelBBox?: BBox | null
259
247
  }
260
248
 
261
249
  export interface DiagramView extends Omit<ComputedView, 'nodes' | 'edges'> {
package/src/utils/fqn.ts CHANGED
@@ -1,6 +1,8 @@
1
1
  import type { Element, Fqn } from '../types'
2
2
  import { isString } from './guards'
3
3
 
4
+ type Predicate<T> = (x: T) => boolean
5
+
4
6
  export function nameFromFqn(fqn: Fqn) {
5
7
  const lastDot = fqn.lastIndexOf('.')
6
8
  if (lastDot > 0) {
@@ -64,6 +66,11 @@ export function parentFqn(fqn: Fqn): Fqn | null {
64
66
  return null
65
67
  }
66
68
 
69
+ export function parentFqnPredicate<T extends { parent: Fqn | null }>(parent: Fqn): Predicate<T> {
70
+ const prefix = parent + '.'
71
+ return (e: T) => !!e.parent && (e.parent === parent || e.parent.startsWith(prefix))
72
+ }
73
+
67
74
  /**
68
75
  * Get all ancestor elements (i.e. parent, parent’s parent, etc.)
69
76
  * (from closest to root)