@likec4/core 1.39.5 → 1.41.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 (65) hide show
  1. package/dist/builder/index.d.mts +75 -14
  2. package/dist/builder/index.mjs +111 -47
  3. package/dist/compute-view/index.d.mts +5 -5
  4. package/dist/compute-view/index.mjs +3 -3
  5. package/dist/compute-view/relationships-view/index.d.mts +5 -5
  6. package/dist/index.d.mts +5 -5
  7. package/dist/index.mjs +5 -5
  8. package/dist/model/connection/deployment/index.d.mts +7 -7
  9. package/dist/model/connection/deployment/index.mjs +1 -1
  10. package/dist/model/connection/index.d.mts +8 -8
  11. package/dist/model/connection/index.mjs +1 -1
  12. package/dist/model/connection/model/index.d.mts +7 -7
  13. package/dist/model/index.d.mts +10 -10
  14. package/dist/model/index.mjs +3 -3
  15. package/dist/shared/{core.CxGKeL-L.mjs → core.6_Vp2iiN.mjs} +29 -1
  16. package/dist/shared/{core.BqVo8LrH.mjs → core.Ae86Y3tB.mjs} +27 -8
  17. package/dist/shared/{core.DozSPE83.mjs → core.Asc11Mjy.mjs} +47 -11
  18. package/dist/shared/{core.PFqKFee4.d.mts → core.BXsxn-hf.d.mts} +44 -8
  19. package/dist/shared/core.BazD6cot.mjs +14 -0
  20. package/dist/shared/{core.B4scjmM6.d.mts → core.BhpgpOep.d.mts} +24 -4
  21. package/dist/shared/{core.CNhp8dSD.mjs → core.BvMRMo7Z.mjs} +69 -46
  22. package/dist/shared/{core.DmFVWyBc.d.mts → core.CUG6vpyL.d.mts} +2 -2
  23. package/dist/shared/{core.yHe79zEl.mjs → core.CXy1NLUG.mjs} +2 -2
  24. package/dist/shared/{core.B65NlqR_.d.mts → core.D-ZGWDDt.d.mts} +49 -39
  25. package/dist/shared/{core.CsbOakly.mjs → core.DA5vzwZ9.mjs} +30 -4
  26. package/dist/shared/{core.T-eLZTW_.mjs → core.DJp8fgok.mjs} +6 -2
  27. package/dist/shared/{core.1_FtXl46.d.mts → core.DgesAwC8.d.mts} +12 -3
  28. package/dist/shared/{core.1ypAWObi.d.mts → core.FUPplpae.d.mts} +1 -1
  29. package/dist/shared/{core.BZs_hKMJ.d.mts → core.hfOE3RfK.d.mts} +4 -4
  30. package/dist/shared/{core.Chtad_Hv.d.mts → core.xSQO0XYD.d.mts} +2 -2
  31. package/dist/shared/{core.Bq0r6_yG.d.mts → core.zxTBF_PD.d.mts} +3 -3
  32. package/dist/types/_aux.d.mts +2 -2
  33. package/dist/types/_aux.mjs +1 -5
  34. package/dist/types/expression-model.d.mts +3 -3
  35. package/dist/types/expression.d.mts +3 -3
  36. package/dist/types/fqnRef.d.mts +2 -2
  37. package/dist/types/index.d.mts +6 -6
  38. package/dist/types/index.mjs +5 -5
  39. package/dist/utils/index.d.mts +2 -2
  40. package/dist/utils/iterable/index.d.mts +1 -1
  41. package/package.json +2 -2
  42. package/src/builder/Builder.ts +119 -43
  43. package/src/builder/_types.ts +12 -8
  44. package/src/compute-view/deployment-view/utils.ts +47 -36
  45. package/src/compute-view/dynamic-view/compute.ts +1 -0
  46. package/src/compute-view/relationships-view/layout.ts +7 -6
  47. package/src/compute-view/utils/buildComputedNodes.ts +23 -10
  48. package/src/compute-view/utils/buildElementNotations.ts +2 -1
  49. package/src/model/DeploymentElementModel.ts +33 -3
  50. package/src/model/ElementModel.ts +25 -2
  51. package/src/model/RelationModel.ts +1 -1
  52. package/src/model/view/EdgeModel.ts +3 -2
  53. package/src/model/view/LikeC4ViewModel.ts +25 -7
  54. package/src/model/view/NodeModel.ts +1 -1
  55. package/src/types/RichText.ts +6 -4
  56. package/src/types/_aux.ts +26 -0
  57. package/src/types/_common.ts +24 -2
  58. package/src/types/geometry.ts +29 -1
  59. package/src/types/index.ts +2 -0
  60. package/src/types/model-deployment.ts +8 -18
  61. package/src/types/model-logical.ts +31 -16
  62. package/src/types/model-spec.ts +3 -0
  63. package/src/types/view-computed.ts +28 -13
  64. package/src/types/view-layouted.ts +7 -4
  65. package/src/types/view-parsed.dynamic.ts +11 -0
@@ -1,4 +1,4 @@
1
- import { isEmpty, isTruthy, unique } from 'remeda'
1
+ import { isEmpty, isShallowEqual, isTruthy, unique } from 'remeda'
2
2
  import type { SetRequired } from 'type-fest'
3
3
  import type { Any, AnyAux, Color, IteratorLike } from '../types'
4
4
  import {
@@ -12,6 +12,8 @@ import {
12
12
  DefaultElementShape,
13
13
  DefaultShapeSize,
14
14
  DefaultThemeColor,
15
+ preferDescription,
16
+ preferSummary,
15
17
  RichText,
16
18
  splitGlobalFqn,
17
19
  } from '../types'
@@ -95,8 +97,29 @@ export class ElementModel<A extends AnyAux = Any> implements WithTags<A>, WithMe
95
97
  return this.$element.title
96
98
  }
97
99
 
100
+ /**
101
+ * Returns true if the element has a summary and a description
102
+ * (if one is missing - it falls back to another)
103
+ */
104
+ get hasSummary(): boolean {
105
+ return !!this.$element.summary && !!this.$element.description &&
106
+ !isShallowEqual(this.$element.summary, this.$element.description)
107
+ }
108
+
109
+ /**
110
+ * Short description of the element.
111
+ * Falls back to description if summary is not provided.
112
+ */
113
+ get summary(): RichTextOrEmpty {
114
+ return RichText.memoize(this, 'summary', preferSummary(this.$element))
115
+ }
116
+
117
+ /**
118
+ * Long description of the element.
119
+ * Falls back to summary if description is not provided.
120
+ */
98
121
  get description(): RichTextOrEmpty {
99
- return RichText.memoize(this, this.$element.description)
122
+ return RichText.memoize(this, 'description', preferDescription(this.$element))
100
123
  }
101
124
 
102
125
  get technology(): string | null {
@@ -83,7 +83,7 @@ export class RelationshipModel<A extends AnyAux = AnyAux> implements AnyRelation
83
83
  }
84
84
 
85
85
  get description(): RichTextOrEmpty {
86
- return RichText.memoize(this, this.$relationship.description)
86
+ return RichText.memoize(this, 'description', this.$relationship.description)
87
87
  }
88
88
 
89
89
  get navigateTo(): LikeC4ViewModel<A> | null {
@@ -6,12 +6,13 @@ import {
6
6
  type DiagramEdge,
7
7
  type IteratorLike,
8
8
  type RelationshipLineType,
9
+ type RichTextOrEmpty,
9
10
  type scalar,
10
11
  type StepEdgeId,
11
12
  extractStep,
12
13
  isStepEdgeId,
14
+ RichText,
13
15
  } from '../../types'
14
- import { type RichTextOrEmpty, RichText } from '../../types'
15
16
  import type { DeploymentRelationModel } from '../DeploymentElementModel'
16
17
  import type { RelationshipModel } from '../RelationModel'
17
18
  import type { $View, WithTags } from '../types'
@@ -44,7 +45,7 @@ export class EdgeModel<A extends Any = Any, View extends $View<A> = $View<A>> im
44
45
  }
45
46
 
46
47
  get description(): RichTextOrEmpty {
47
- return RichText.memoize(this, this.#edge.description)
48
+ return RichText.memoize(this, 'description', this.#edge.description)
48
49
  }
49
50
 
50
51
  get technology(): string | null {
@@ -2,6 +2,7 @@ import { isTruthy } from 'remeda'
2
2
  import type {
3
3
  Any,
4
4
  AnyView,
5
+ DynamicViewDisplayVariant,
5
6
  IteratorLike,
6
7
  Link,
7
8
  scalar,
@@ -149,7 +150,7 @@ export class LikeC4ViewModel<A extends Any = Any, V extends $View<A> = $View<A>>
149
150
  }
150
151
 
151
152
  get description(): RichTextOrEmpty {
152
- return RichText.memoize(this, this.$view.description)
153
+ return RichText.memoize(this, 'description', this.$view.description)
153
154
  }
154
155
 
155
156
  get tags(): aux.Tags<A> {
@@ -168,6 +169,17 @@ export class LikeC4ViewModel<A extends Any = Any, V extends $View<A> = $View<A>>
168
169
  return null
169
170
  }
170
171
 
172
+ /**
173
+ * Available for dynamic views only
174
+ * throws error if view is not dynamic
175
+ */
176
+ get mode(): DynamicViewDisplayVariant {
177
+ if (this.isDynamicView()) {
178
+ return this.$view.variant ?? 'diagram'
179
+ }
180
+ throw new Error('View is not dynamic')
181
+ }
182
+
171
183
  /**
172
184
  * All tags from nodes and edges.
173
185
  */
@@ -223,17 +235,26 @@ export class LikeC4ViewModel<A extends Any = Any, V extends $View<A> = $View<A>>
223
235
  }
224
236
 
225
237
  /**
226
- * Find edge by id.
238
+ * Get edge by id, throws error if edge is not found.
239
+ * Use {@link findEdge} if you are not sure if the edge exists.
240
+ *
227
241
  * @param edge Edge or id
228
- * @returns EdgeModel
242
+ * @returns {@link EdgeModel}
229
243
  */
230
244
  public edge(edge: EdgeOrId): EdgeModel<A, V> {
231
245
  const edgeId = getId(edge)
232
246
  return nonNullable(this.#edges.get(edgeId), `Edge ${edgeId} not found in view ${this.$view.id}`)
233
247
  }
248
+
249
+ /**
250
+ * Find edge by id.
251
+ * @param edge Edge or id
252
+ * @returns {@link EdgeModel} or null if edge is not found
253
+ */
234
254
  public findEdge(edge: EdgeOrId): EdgeModel<A, V> | null {
235
255
  return this.#edges.get(getId(edge)) ?? null
236
256
  }
257
+
237
258
  /**
238
259
  * Iterate over all edges.
239
260
  */
@@ -336,8 +357,5 @@ export namespace LikeC4ViewModel {
336
357
  {
337
358
  }
338
359
 
339
- export interface DynamicView<A extends Any, V extends $View<A> = $View<A>>
340
- extends LikeC4ViewModel<A, ViewWithType<V, 'dynamic'>>
341
- {
342
- }
360
+ export type DynamicView<A extends Any, V extends $View<A> = $View<A>> = LikeC4ViewModel<A, ViewWithType<V, 'dynamic'>>
343
361
  }
@@ -49,7 +49,7 @@ export class NodeModel<A extends Any = Any, V extends $View<A> = $View<A>> imple
49
49
  }
50
50
 
51
51
  get description(): RichTextOrEmpty {
52
- return RichText.memoize(this, this.#node.description)
52
+ return RichText.memoize(this, 'description', this.#node.description)
53
53
  }
54
54
 
55
55
  get technology(): string | null {
@@ -1,7 +1,6 @@
1
1
  import { markdownToHtml, markdownToText, memoizeProp, nonexhaustive } from '../utils'
2
2
  import type { MarkdownOrString } from './scalar'
3
3
 
4
- const richtxt = Symbol.for('richtxt')
5
4
  const symb_text = Symbol.for('text')
6
5
  const symb_html = Symbol.for('html')
7
6
 
@@ -48,8 +47,8 @@ export class RichText {
48
47
  * return RichText.memoize(this, this.$element.description)
49
48
  * }
50
49
  */
51
- static memoize(obj: object, source: MarkdownOrString | null | undefined): RichTextOrEmpty {
52
- return memoizeProp(obj, richtxt, () => RichText.from(source))
50
+ static memoize(obj: object, tag: symbol | string, source: MarkdownOrString | null | undefined): RichTextOrEmpty {
51
+ return memoizeProp(obj, tag, () => RichText.from(source))
53
52
  }
54
53
 
55
54
  /**
@@ -63,7 +62,10 @@ export class RichText {
63
62
  return source as RichTextOrEmpty
64
63
  }
65
64
  if (typeof source === 'string') {
66
- return RichText.from({ txt: source })
65
+ if (source.trim() === emptyTxt) {
66
+ return RichText.EMPTY
67
+ }
68
+ return new RichText({ txt: source }) as RichTextOrEmpty
67
69
  }
68
70
  if ('isEmpty' in source && source.isEmpty) {
69
71
  return RichText.EMPTY
package/src/types/_aux.ts CHANGED
@@ -255,6 +255,28 @@ export type StrictDeploymentKind<A> = A extends infer T extends Any ? scalar.Dep
255
255
  : never
256
256
  export type StrictRelationKind<A> = A extends infer T extends Any ? scalar.RelationshipKind<RelationKind<T>> : never
257
257
 
258
+ export type WithDescriptionAndTech = {
259
+ // short summary (displayed in the diagram)
260
+ readonly summary?: scalar.MarkdownOrString | null
261
+ // long description (displayed in the details panel, or in the diagram if summary is not set)
262
+ readonly description?: scalar.MarkdownOrString | null
263
+ readonly technology?: string | null
264
+ }
265
+
266
+ /**
267
+ * Returns summary if it is not null, otherwise returns description
268
+ */
269
+ export function preferSummary(a: WithDescriptionAndTech): scalar.MarkdownOrString | null | undefined {
270
+ return a.summary ?? a.description
271
+ }
272
+
273
+ /**
274
+ * Returns description if it is not null, otherwise returns summary
275
+ */
276
+ export function preferDescription(a: WithDescriptionAndTech): scalar.MarkdownOrString | null | undefined {
277
+ return a.description ?? a.summary
278
+ }
279
+
258
280
  export type WithTags<A extends Any> = {
259
281
  readonly tags: Tags<A>
260
282
  }
@@ -275,6 +297,10 @@ export type WithMetadata<A extends Any> = {
275
297
  readonly metadata?: Metadata<A>
276
298
  }
277
299
 
300
+ export type WithNotation = {
301
+ readonly notation?: string | null
302
+ }
303
+
278
304
  /**
279
305
  * @see {@link LiteralUnion} from type-fest (https://github.com/sindresorhus/type-fest/blob/main/source/literal-union.d.ts)
280
306
  */
@@ -1,4 +1,11 @@
1
- import type { IsAny, Simplify, UnionToIntersection } from 'type-fest'
1
+ import { omitBy } from 'remeda'
2
+ import type {
3
+ Exact,
4
+ IsAny,
5
+ KeysOfUnion,
6
+ Simplify,
7
+ UnionToIntersection,
8
+ } from 'type-fest'
2
9
 
3
10
  export type NonEmptyArray<T> = [T, ...T[]]
4
11
 
@@ -62,7 +69,7 @@ export type NTuple<
62
69
  Result extends Array<unknown> = [],
63
70
  > = Result['length'] extends N ? Result : NTuple<T, N, [...Result, T]>
64
71
 
65
- export type IteratorLike<T> = IteratorObject<T, BuiltinIteratorReturn>
72
+ export type IteratorLike<T> = IteratorObject<T, BuiltinIteratorReturn, unknown>
66
73
 
67
74
  export type Predicate<T> = (x: T) => boolean
68
75
 
@@ -78,3 +85,18 @@ export interface Link {
78
85
  * Coalesce `V` to a string if it is `any`
79
86
  */
80
87
  export type Coalesce<V extends string, OrIfAny = string> = IsAny<V> extends true ? OrIfAny : V
88
+
89
+ type ExactObject<T, InputType = unknown> =
90
+ & {
91
+ [KeyType in keyof T]: undefined extends T[KeyType] ? T[KeyType] | undefined : T[KeyType]
92
+ }
93
+ & Record<Exclude<keyof InputType, KeysOfUnion<T>>, never>
94
+
95
+ /**
96
+ * Allows only exact properties of `U` to be present in `T` and omits undefined values
97
+ *
98
+ * See {@link Exact} for more details (this version is non-deep)
99
+ */
100
+ export function omitUndefined<Expected, T extends ExactObject<Expected, T>>(a: T): Expected {
101
+ return omitBy(a, v => v === undefined) as Expected
102
+ }
@@ -1,3 +1,4 @@
1
+ import { hasAtLeast } from 'remeda'
1
2
  import { invariant } from '../utils/invariant'
2
3
 
3
4
  export type Point = readonly [x: number, y: number]
@@ -34,7 +35,10 @@ export namespace BBox {
34
35
  }
35
36
 
36
37
  export function merge(...boxes: BBox[]): BBox {
37
- invariant(boxes.length > 0, 'No boxes provided')
38
+ invariant(hasAtLeast(boxes, 1), 'No boxes provided')
39
+ if (boxes.length === 1) {
40
+ return boxes[0]
41
+ }
38
42
  let minX = Infinity
39
43
  let minY = Infinity
40
44
  let maxX = -Infinity
@@ -61,6 +65,30 @@ export namespace BBox {
61
65
  y2: box.y + box.height,
62
66
  }
63
67
  }
68
+
69
+ export function expand(box: BBox, plus: number): BBox {
70
+ if (plus === 0) {
71
+ return box
72
+ }
73
+ return {
74
+ x: box.x - plus,
75
+ y: box.y - plus,
76
+ width: box.width + plus * 2,
77
+ height: box.height + plus * 2,
78
+ }
79
+ }
80
+
81
+ export function shrink(box: BBox, minus: number): BBox {
82
+ if (minus === 0) {
83
+ return box
84
+ }
85
+ return {
86
+ x: box.x + minus,
87
+ y: box.y + minus,
88
+ width: box.width - minus * 2,
89
+ height: box.height - minus * 2,
90
+ }
91
+ }
64
92
  }
65
93
 
66
94
  export interface RectBox {
@@ -13,6 +13,8 @@ export type {
13
13
  UnknownParsed,
14
14
  } from './_aux'
15
15
 
16
+ export { preferDescription, preferSummary } from './_aux'
17
+
16
18
  export * from './const'
17
19
  export * from './expression'
18
20
  export * from './expression-model'
@@ -1,11 +1,9 @@
1
1
  import { isTruthy } from 'remeda'
2
2
  import type * as aux from './_aux'
3
3
  import type { AnyAux, Unknown } from './_aux'
4
- import type { Link } from './_common'
5
4
  import type { FqnRef } from './fqnRef'
6
5
  import type { AbstractRelationship, ElementStyle } from './model-logical'
7
6
  import type { Icon } from './scalar'
8
- import type * as scalar from './scalar'
9
7
  import type { Color, ElementShape } from './styles'
10
8
 
11
9
  export interface DeploymentElementStyle extends ElementStyle {
@@ -17,30 +15,28 @@ export interface DeploymentElementStyle extends ElementStyle {
17
15
  // dprint-ignore
18
16
  export interface DeploymentNode<A extends AnyAux = Unknown>
19
17
  extends
18
+ aux.WithDescriptionAndTech,
20
19
  aux.WithOptionalTags<A>,
21
20
  aux.WithOptionalLinks,
22
- aux.WithMetadata<A>
21
+ aux.WithMetadata<A>,
22
+ aux.WithNotation
23
23
  {
24
24
  element?: never
25
25
  // Full-qualified-name for Deployment model
26
26
  readonly id: aux.StrictDeploymentFqn<A>
27
27
  readonly kind: aux.DeploymentKind<A>
28
28
  readonly title: string
29
- readonly description?: scalar.MarkdownOrString | null
30
- readonly technology?: string | null
31
- readonly tags?: aux.Tags<A> | null
32
- readonly links?: readonly Link[] | null
33
29
  readonly style: DeploymentElementStyle
34
- readonly notation?: string | null
35
- readonly metadata?: aux.Metadata<A>
36
30
  }
37
31
 
38
32
  // dprint-ignore
39
33
  export interface DeployedInstance<A extends AnyAux = Unknown>
40
34
  extends
35
+ aux.WithDescriptionAndTech,
41
36
  aux.WithOptionalTags<A>,
42
37
  aux.WithOptionalLinks,
43
- aux.WithMetadata<A>
38
+ aux.WithMetadata<A>,
39
+ aux.WithNotation
44
40
  {
45
41
  kind?: never
46
42
  /**
@@ -50,13 +46,7 @@ export interface DeployedInstance<A extends AnyAux = Unknown>
50
46
  readonly id: aux.StrictDeploymentFqn<A>
51
47
  readonly element: aux.StrictFqn<A>
52
48
  readonly title?: string
53
- readonly description?: scalar.MarkdownOrString | null
54
- readonly technology?: string | null
55
- readonly tags?: aux.Tags<A> | null
56
- readonly links?: readonly Link[] | null
57
49
  readonly style?: DeploymentElementStyle
58
- readonly notation?: string
59
- readonly metadata?: aux.Metadata<A>
60
50
  }
61
51
 
62
52
  export type DeploymentElement<A extends AnyAux = Unknown> = DeploymentNode<A> | DeployedInstance<A>
@@ -67,11 +57,11 @@ export type DeploymentElementRef<A extends AnyAux = Unknown> = {
67
57
  }
68
58
 
69
59
  export function isDeploymentNode<A extends AnyAux>(el: DeploymentElement<A>): el is DeploymentNode<A> {
70
- return 'kind' in el && !isTruthy(el.element)
60
+ return isTruthy(el.kind) && !isTruthy(el.element)
71
61
  }
72
62
 
73
63
  export function isDeployedInstance<A extends AnyAux>(el: DeploymentElement<A>): el is DeployedInstance<A> {
74
- return 'element' in el && isTruthy(el.element)
64
+ return isTruthy(el.element) && !isTruthy(el.kind)
75
65
  }
76
66
 
77
67
  /**
@@ -1,6 +1,5 @@
1
1
  import type * as aux from './_aux'
2
2
  import type { AnyAux } from './_aux'
3
- import type { Link } from './_common'
4
3
  import type { FqnRef } from './fqnRef'
5
4
  import type * as scalar from './scalar'
6
5
  import type {
@@ -17,9 +16,10 @@ import type {
17
16
 
18
17
  export const DefaultThemeColor: ThemeColor = 'primary'
19
18
  export const DefaultElementShape: ElementShape = 'rectangle'
20
- export const DefaultShapeSize: ShapeSize = 'md'
21
- export const DefaultPaddingSize: SpacingSize = 'md'
22
- export const DefaultTextSize: TextSize = 'md'
19
+ export const DefaultSize = 'md'
20
+ export const DefaultShapeSize: ShapeSize = DefaultSize
21
+ export const DefaultPaddingSize: SpacingSize = DefaultSize
22
+ export const DefaultTextSize: TextSize = DefaultSize
23
23
 
24
24
  export interface ElementStyle {
25
25
  readonly border?: BorderStyle
@@ -47,26 +47,45 @@ export interface ElementStyle {
47
47
  readonly textSize?: TextSize
48
48
  }
49
49
 
50
+ type WithSizes = Pick<ElementStyle, 'size' | 'padding' | 'textSize'>
51
+
52
+ /**
53
+ * Ensures that the sizes are set to default values if they are not set
54
+ */
55
+ export function ensureSizes<S extends WithSizes>({
56
+ size,
57
+ padding,
58
+ textSize,
59
+ ...rest
60
+ }: S): Omit<S, 'size' | 'padding' | 'textSize'> & Required<WithSizes> {
61
+ size ??= DefaultSize
62
+ textSize ??= size
63
+ padding ??= size
64
+
65
+ return {
66
+ ...rest,
67
+ size,
68
+ padding,
69
+ textSize,
70
+ }
71
+ }
72
+
50
73
  // dprint-ignore
51
74
  export interface Element<A extends AnyAux = AnyAux>
52
75
  extends
76
+ aux.WithDescriptionAndTech,
53
77
  aux.WithOptionalTags<A>,
54
78
  aux.WithOptionalLinks,
55
- aux.WithMetadata<A>
79
+ aux.WithMetadata<A>,
80
+ aux.WithNotation
56
81
  {
57
82
  readonly id: aux.StrictFqn<A>
58
83
  readonly kind: aux.ElementKind<A>
59
84
  readonly title: string
60
- readonly description?: scalar.MarkdownOrString | null
61
- readonly technology?: string | null
62
- readonly tags?: aux.Tags<A> | null
63
- readonly links?: readonly Link[] | null
64
85
  readonly icon?: scalar.Icon
65
86
  readonly shape?: ElementShape
66
87
  readonly color?: Color
67
88
  readonly style?: ElementStyle
68
- readonly notation?: string | null
69
- readonly metadata?: aux.Metadata<A>
70
89
  }
71
90
 
72
91
  export const DefaultLineStyle: RelationshipLineType = 'dashed'
@@ -76,24 +95,20 @@ export const DefaultRelationshipColor: ThemeColor = 'gray'
76
95
  // dprint-ignore
77
96
  export interface AbstractRelationship<A extends AnyAux>
78
97
  extends
98
+ aux.WithDescriptionAndTech,
79
99
  aux.WithOptionalTags<A>,
80
100
  aux.WithOptionalLinks,
81
101
  aux.WithMetadata<A>
82
102
  {
83
103
  readonly id: scalar.RelationId
84
104
  readonly title?: string | null
85
- readonly description?: scalar.MarkdownOrString | null
86
- readonly technology?: string | null
87
105
  readonly kind?: aux.RelationKind<A>
88
106
  readonly color?: Color
89
107
  readonly line?: RelationshipLineType
90
108
  readonly head?: RelationshipArrowType
91
109
  readonly tail?: RelationshipArrowType
92
- readonly tags?: aux.Tags<A> | null
93
- readonly links?: readonly Link[] | null
94
110
  // Link to dynamic view
95
111
  readonly navigateTo?: aux.ViewId<A>
96
- readonly metadata?: aux.Metadata<A>
97
112
  }
98
113
 
99
114
  /**
@@ -26,6 +26,9 @@ import type {
26
26
  export interface ElementSpecification {
27
27
  tags?: scalar.Tag[]
28
28
  title?: string
29
+ // short summary
30
+ summary?: scalar.MarkdownOrString
31
+ // long description
29
32
  description?: scalar.MarkdownOrString
30
33
  technology?: string
31
34
  notation?: string
@@ -1,5 +1,5 @@
1
1
  import type * as aux from './_aux'
2
- import type { Any } from './_aux'
2
+ import type { AnyAux } from './_aux'
3
3
  import type { _stage, _type } from './const'
4
4
  import type { ElementStyle } from './model-logical'
5
5
  import type * as scalar from './scalar'
@@ -19,27 +19,35 @@ import type {
19
19
  ViewWithHash,
20
20
  ViewWithNotation,
21
21
  } from './view-common'
22
+ import type { DynamicViewDisplayVariant } from './view-parsed.dynamic'
22
23
 
23
- export interface ComputedNode<A extends Any = Any> extends aux.WithOptionalLinks {
24
+ // dprint-ignore
25
+ export interface ComputedNode<A extends AnyAux = AnyAux>
26
+ extends
27
+ aux.WithTags<A>,
28
+ aux.WithOptionalLinks
29
+ {
24
30
  id: scalar.NodeId
25
31
  kind: aux.ElementKind<A> | aux.DeploymentKind<A> | '@group'
26
32
  parent: scalar.NodeId | null
27
33
  /**
28
34
  * Reference to model element
29
35
  */
30
- modelRef?: aux.Fqn<A> | undefined
36
+ modelRef?: aux.Fqn<A>
31
37
  /**
32
38
  * Reference to deployment element
33
39
  */
34
- deploymentRef?: aux.DeploymentFqn<A> | undefined
40
+ deploymentRef?: aux.DeploymentFqn<A>
35
41
  title: string
42
+ /**
43
+ * Description of the node
44
+ * either summary or description
45
+ */
36
46
  description?: scalar.MarkdownOrString | null
37
47
  technology?: string | null
38
- notation?: string
39
48
  children: scalar.NodeId[]
40
49
  inEdges: scalar.EdgeId[]
41
50
  outEdges: scalar.EdgeId[]
42
- tags: aux.Tags<A>
43
51
  shape: ElementShape
44
52
  color: Color
45
53
  icon?: Icon
@@ -47,21 +55,22 @@ export interface ComputedNode<A extends Any = Any> extends aux.WithOptionalLinks
47
55
  navigateTo?: aux.StrictViewId<A> | null
48
56
  level: number
49
57
  // For compound nodes, the max depth of nested nodes
50
- depth?: number
58
+ depth?: number | null
51
59
  /**
52
60
  * If this node was customized in the view
53
61
  */
54
62
  isCustomized?: boolean
63
+ notation?: string | null
55
64
  }
56
65
 
57
- export interface ComputedEdge<A extends Any = Any> extends aux.WithOptionalTags<A> {
66
+ export interface ComputedEdge<A extends AnyAux = AnyAux> extends aux.WithOptionalTags<A> {
58
67
  id: scalar.EdgeId
59
68
  parent: scalar.NodeId | null
60
69
  source: scalar.NodeId
61
70
  target: scalar.NodeId
62
71
  label: string | null
63
72
  description?: scalar.MarkdownOrString | null
64
- technology?: string
73
+ technology?: string | null
65
74
  relations: scalar.RelationId[]
66
75
  kind?: aux.RelationKind<A> | typeof scalar.StepEdgeKind
67
76
  notation?: string
@@ -84,7 +93,7 @@ export interface ComputedEdge<A extends Any = Any> extends aux.WithOptionalTags<
84
93
  dir?: 'forward' | 'back' | 'both'
85
94
  }
86
95
 
87
- interface BaseComputedViewProperties<A extends Any> extends BaseViewProperties<A>, ViewWithHash, ViewWithNotation {
96
+ interface BaseComputedViewProperties<A extends AnyAux> extends BaseViewProperties<A>, ViewWithHash, ViewWithNotation {
88
97
  readonly [_stage]: 'computed'
89
98
  readonly autoLayout: ViewAutoLayout
90
99
  readonly nodes: ComputedNode<A>[]
@@ -96,16 +105,22 @@ interface BaseComputedViewProperties<A extends Any> extends BaseViewProperties<A
96
105
  readonly manualLayout?: ViewManualLayout | undefined
97
106
  }
98
107
 
99
- export interface ComputedElementView<A extends Any = Any> extends BaseComputedViewProperties<A> {
108
+ export interface ComputedElementView<A extends AnyAux = AnyAux> extends BaseComputedViewProperties<A> {
100
109
  readonly [_type]: 'element'
101
110
  readonly viewOf?: aux.StrictFqn<A>
102
111
  readonly extends?: aux.StrictViewId<A>
103
112
  }
104
113
 
105
- export interface ComputedDeploymentView<A extends Any = Any> extends BaseComputedViewProperties<A> {
114
+ export interface ComputedDeploymentView<A extends AnyAux = AnyAux> extends BaseComputedViewProperties<A> {
106
115
  readonly [_type]: 'deployment'
107
116
  }
108
117
 
109
- export interface ComputedDynamicView<A extends Any = Any> extends BaseComputedViewProperties<A> {
118
+ export interface ComputedDynamicView<A extends AnyAux = AnyAux> extends BaseComputedViewProperties<A> {
110
119
  readonly [_type]: 'dynamic'
120
+ /**
121
+ * How to display the dynamic view
122
+ * - `diagram`: display as a regular likec4 view
123
+ * - `sequence`: display as a sequence diagram
124
+ */
125
+ readonly variant: DynamicViewDisplayVariant
111
126
  }
@@ -10,6 +10,7 @@ import type {
10
10
  ViewWithNotation,
11
11
  } from './view-common'
12
12
  import type { ComputedEdge, ComputedNode } from './view-computed'
13
+ import type { DynamicViewDisplayVariant } from './view-parsed.dynamic'
13
14
 
14
15
  export interface DiagramNode<A extends AnyAux = AnyAux> extends ComputedNode<A>, BBox {
15
16
  x: number
@@ -26,10 +27,6 @@ export interface DiagramNode<A extends AnyAux = AnyAux> extends ComputedNode<A>,
26
27
  * (Absolute coordinates)
27
28
  */
28
29
  labelBBox: BBox
29
-
30
- // description?: scalar.HtmlOrString | null
31
- // technology?: scalar.HtmlOrString | null
32
- // notation?: scalar.HtmlOrString | null
33
30
  }
34
31
 
35
32
  export interface DiagramEdge<A extends AnyAux = AnyAux> extends ComputedEdge<A> {
@@ -88,4 +85,10 @@ export interface LayoutedDeploymentView<A extends AnyAux = AnyAux> extends BaseL
88
85
 
89
86
  export interface LayoutedDynamicView<A extends AnyAux = AnyAux> extends BaseLayoutedViewProperties<A> {
90
87
  readonly [_type]: 'dynamic'
88
+ /**
89
+ * How to display the dynamic view
90
+ * - `diagram`: display as a regular likec4 view
91
+ * - `sequence`: display as a sequence diagram
92
+ */
93
+ readonly variant: DynamicViewDisplayVariant
91
94
  }
@@ -65,8 +65,19 @@ export type DynamicViewRule<A extends AnyAux = AnyAux> = ExclusiveUnion<{
65
65
  AutoLayout: ViewRuleAutoLayout
66
66
  }>
67
67
 
68
+ export type DynamicViewDisplayVariant = 'diagram' | 'sequence'
69
+
68
70
  export interface ParsedDynamicView<A extends AnyAux = AnyAux> extends BaseParsedViewProperties<A> {
69
71
  [_type]: 'dynamic'
72
+ /**
73
+ * How to display the dynamic view
74
+ * - `diagram`: display as a regular likec4 view
75
+ * - `sequence`: display as a sequence diagram
76
+ *
77
+ * @default 'diagram'
78
+ */
79
+ readonly variant?: DynamicViewDisplayVariant
80
+
70
81
  readonly steps: DynamicViewStepOrParallel<A>[]
71
82
  readonly rules: DynamicViewRule<A>[]
72
83
  }