@likec4/diagram 1.5.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.
Files changed (34) hide show
  1. package/LICENSE +1 -1
  2. package/dist/LikeC4Diagram.d.ts +1 -1
  3. package/dist/LikeC4Diagram.props.d.ts +18 -3
  4. package/dist/StaticLikeC4Diagram.d.ts +1 -1
  5. package/dist/index.mjs +3097 -3090
  6. package/dist/state/DiagramContext.d.ts +18 -9
  7. package/dist/state/diagramStore.d.ts +12 -2
  8. package/dist/style.css +149 -85
  9. package/dist/xyflow/nodes/element/ElementShapeSvg.d.ts +5 -0
  10. package/dist/xyflow/nodes/element/element.css.d.ts +4 -2
  11. package/package.json +13 -13
  12. package/src/LikeC4Diagram.props.ts +21 -3
  13. package/src/LikeC4Diagram.tsx +4 -1
  14. package/src/StaticLikeC4Diagram.tsx +1 -0
  15. package/src/state/DiagramContext.tsx +1 -1
  16. package/src/state/diagramStore.ts +8 -1
  17. package/src/ui/BackwardForwardPanel.tsx +1 -1
  18. package/src/xyflow/FitviewOnDiagramChange.tsx +1 -1
  19. package/src/xyflow/SelectEdgesOnNodeFocus.tsx +1 -1
  20. package/src/xyflow/SyncWithDiagram.tsx +1 -1
  21. package/src/xyflow/XYFlow.tsx +2 -1
  22. package/src/xyflow/XYFlowEvents.tsx +1 -1
  23. package/src/xyflow/XYFlowInner.tsx +1 -1
  24. package/src/xyflow/edges/RelationshipEdge.tsx +3 -3
  25. package/src/xyflow/edges/edges.css.ts +6 -6
  26. package/src/xyflow/hooks/useLayoutConstraints.ts +1 -1
  27. package/src/xyflow/nodes/compound/CompoundNode.tsx +1 -1
  28. package/src/xyflow/nodes/element/ElementLink.tsx +2 -1
  29. package/src/xyflow/nodes/element/ElementNode.tsx +70 -22
  30. package/src/xyflow/nodes/element/ElementShapeSvg.tsx +2 -2
  31. package/src/xyflow/nodes/element/element.css.ts +126 -54
  32. package/src/xyflow/nodes/shared/NavigateToBtn.tsx +1 -1
  33. package/dist/xyflow/nodes/element/ElementIcon.d.ts +0 -7
  34. package/src/xyflow/nodes/element/ElementIcon.tsx +0 -33
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@likec4/diagram",
3
- "version": "1.5.0",
3
+ "version": "1.6.0",
4
4
  "license": "MIT",
5
5
  "bugs": "https://github.com/likec4/likec4/issues",
6
6
  "homepage": "https://likec4.dev",
@@ -48,31 +48,31 @@
48
48
  "clean": "run -T rimraf -v -g 'dist/*' .tsbuildinfo"
49
49
  },
50
50
  "dependencies": {
51
- "@likec4/core": "1.5.0",
51
+ "@likec4/core": "1.6.0",
52
52
  "@react-hookz/web": "^24.0.4",
53
- "@tabler/icons-react": "^3.9.0",
53
+ "@tabler/icons-react": "^3.11.0",
54
54
  "@vanilla-extract/css": "^1.15.3",
55
55
  "@vanilla-extract/css-utils": "^0.1.4",
56
56
  "@vanilla-extract/dynamic": "^2.1.1",
57
57
  "d3-shape": "^3.2.0",
58
58
  "esm-env": "^1.0.0",
59
59
  "fast-equals": "^5.0.1",
60
- "framer-motion": "^11.2.13",
60
+ "framer-motion": "^11.3.8",
61
61
  "remeda": "^2.3.0",
62
62
  "zustand": "^4.5.4"
63
63
  },
64
64
  "peerDependencies": {
65
- "@mantine/core": "^7.11.1",
66
- "@mantine/hooks": "^7.11.1",
65
+ "@mantine/core": "^7.11.2",
66
+ "@mantine/hooks": "^7.11.2",
67
67
  "react": "^18.3.1",
68
68
  "react-dom": "^18.3.1"
69
69
  },
70
70
  "devDependencies": {
71
- "@likec4/tsconfig": "1.5.0",
71
+ "@likec4/tsconfig": "1.6.0",
72
72
  "@lume/kiwi": "^0.4.3",
73
- "@mantine/core": "7.11.1",
74
- "@mantine/hooks": "7.11.1",
75
- "@mantine/vanilla-extract": "7.11.1",
73
+ "@mantine/core": "7.11.2",
74
+ "@mantine/hooks": "7.11.2",
75
+ "@mantine/vanilla-extract": "7.11.2",
76
76
  "@total-typescript/ts-reset": "^0.5.1",
77
77
  "@types/bezier-js": "^4.1.3",
78
78
  "@types/d3-shape": "^3.1.6",
@@ -80,8 +80,8 @@
80
80
  "@types/react-dom": "^18.3.0",
81
81
  "@vanilla-extract/esbuild-plugin": "^2.3.8",
82
82
  "@vitejs/plugin-react": "^4.3.1",
83
- "@xyflow/react": "12.0.1",
84
- "@xyflow/system": "0.0.35",
83
+ "@xyflow/react": "12.0.3",
84
+ "@xyflow/system": "0.0.37",
85
85
  "bezier-js": "^6.1.4",
86
86
  "clsx": "^2.1.1",
87
87
  "execa": "^9.3.0",
@@ -91,7 +91,7 @@
91
91
  "tilg": "^0.1.1",
92
92
  "type-fest": "^4.21.0",
93
93
  "typescript": "^5.5.3",
94
- "vite": "5.3.3",
94
+ "vite": "5.3.4",
95
95
  "vite-plugin-dts": "^3.9.1"
96
96
  }
97
97
  }
@@ -6,17 +6,29 @@ import type {
6
6
  EdgeId,
7
7
  ElementShape,
8
8
  Fqn,
9
+ IconUrl,
9
10
  NonEmptyArray,
10
11
  ThemeColor,
11
12
  ViewID
12
13
  } from '@likec4/core'
13
14
  import type { XYPosition } from '@xyflow/system'
14
- import type { MouseEvent as ReactMouseEvent } from 'react'
15
- import type { SetRequired, Simplify } from 'type-fest'
15
+ import type { MouseEvent as ReactMouseEvent, ReactNode } from 'react'
16
16
  import type { XYFlowEdge, XYFlowNode } from './xyflow/types'
17
17
  import type { XYBackground } from './xyflow/XYFlowBackground'
18
18
 
19
- export type DiagramNodeWithNavigate = Simplify<SetRequired<DiagramNode, 'navigateTo'>>
19
+ export type DiagramNodeWithNavigate = Omit<DiagramNode, 'navigateTo'> & {
20
+ navigateTo: ViewID
21
+ }
22
+
23
+ type ElementIconNodeProps = {
24
+ id: string
25
+ title: string
26
+ icon?: string | undefined
27
+ }
28
+
29
+ export type ElementIconRenderer = (props: {
30
+ node: ElementIconNodeProps
31
+ }) => ReactNode
20
32
 
21
33
  export type OnNavigateTo = {
22
34
  (
@@ -177,6 +189,12 @@ export interface LikeC4DiagramProperties {
177
189
  * @default false
178
190
  */
179
191
  experimentalEdgeEditing?: boolean | undefined
192
+
193
+ /**
194
+ * Render custom icon for a node
195
+ * By default, if icon is http:// or https://, it will be rendered as an image
196
+ */
197
+ renderIcon?: ElementIconRenderer | undefined
180
198
  }
181
199
 
182
200
  export interface LikeC4DiagramEventHandlers {
@@ -49,7 +49,8 @@ export function LikeC4Diagram({
49
49
  onEdgeContextMenu,
50
50
  onNavigateTo,
51
51
  onNodeClick,
52
- onNodeContextMenu
52
+ onNodeContextMenu,
53
+ renderIcon
53
54
  }: LikeC4DiagramProps) {
54
55
  DEV && useTilg()
55
56
  const initialRef = useRef<{
@@ -70,6 +71,7 @@ export function LikeC4Diagram({
70
71
  initialHeight: initialHeight ?? view.height
71
72
  }
72
73
  }
74
+
73
75
  return (
74
76
  <EnsureMantine>
75
77
  <XYFlowProvider
@@ -89,6 +91,7 @@ export function LikeC4Diagram({
89
91
  nodesDraggable={nodesDraggable}
90
92
  nodesSelectable={nodesSelectable}
91
93
  experimentalEdgeEditing={experimentalEdgeEditing}
94
+ renderIcon={renderIcon ?? null}
92
95
  onCanvasClick={onCanvasClick}
93
96
  onCanvasContextMenu={onCanvasContextMenu}
94
97
  onEdgeClick={onEdgeClick}
@@ -11,6 +11,7 @@ export type StaticLikeC4DiagramProps = Pick<
11
11
  | 'background'
12
12
  | 'initialWidth'
13
13
  | 'initialHeight'
14
+ | 'renderIcon'
14
15
  >
15
16
 
16
17
  export function StaticLikeC4Diagram({
@@ -2,7 +2,7 @@ import { shallowEqual } from 'fast-equals'
2
2
  import { createContext, type PropsWithChildren, useEffect, useRef } from 'react'
3
3
  import { hasSubObject, isNonNullish, pickBy } from 'remeda'
4
4
  import { useUpdateEffect } from '../hooks'
5
- import { useXYFlow, useXYStoreApi } from '../xyflow/hooks'
5
+ import { useXYFlow, useXYStoreApi } from '../xyflow/hooks/useXYFlow'
6
6
  import { createDiagramStore, type DiagramInitialState } from './diagramStore'
7
7
 
8
8
  export type DiagramZustandStore = ReturnType<typeof createDiagramStore>
@@ -10,7 +10,12 @@ import type { Exact, Except, RequiredKeysOf, Simplify } from 'type-fest'
10
10
  import { devtools, subscribeWithSelector } from 'zustand/middleware'
11
11
  import { shallow } from 'zustand/shallow'
12
12
  import { createWithEqualityFn } from 'zustand/traditional'
13
- import type { Changes, DiagramNodeWithNavigate, LikeC4DiagramEventHandlers } from '../LikeC4Diagram.props'
13
+ import type {
14
+ Changes,
15
+ DiagramNodeWithNavigate,
16
+ ElementIconRenderer,
17
+ LikeC4DiagramEventHandlers
18
+ } from '../LikeC4Diagram.props'
14
19
  import { MinZoom } from '../xyflow/const'
15
20
  import type { XYStoreApi } from '../xyflow/hooks'
16
21
  import type { XYFlowEdge, XYFlowInstance, XYFlowNode } from '../xyflow/types'
@@ -29,6 +34,7 @@ export type DiagramStore = {
29
34
  nodesDraggable: boolean
30
35
  nodesSelectable: boolean
31
36
  experimentalEdgeEditing: boolean
37
+ renderIcon: ElementIconRenderer | null
32
38
  // Diagram Container, see DiagramContainer.tsx
33
39
  getContainer: () => HTMLDivElement | null
34
40
 
@@ -76,6 +82,7 @@ export type DiagramInitialState = // Required properties
76
82
  & Pick<
77
83
  DiagramStore,
78
84
  | 'view'
85
+ | 'renderIcon'
79
86
  | 'readonly'
80
87
  | 'showElementLinks'
81
88
  | 'fitViewEnabled'
@@ -2,7 +2,7 @@ import { ActionIcon, Group } from '@mantine/core'
2
2
  import { IconChevronLeft, IconChevronRight } from '@tabler/icons-react'
3
3
  import clsx from 'clsx'
4
4
  import { AnimatePresence, m } from 'framer-motion'
5
- import { useNavigationHistory } from '../state/hooks'
5
+ import { useNavigationHistory } from '../state/useNavigationHistory'
6
6
  import * as css from './BackwardForwardPanel.css'
7
7
 
8
8
  export function BackwardForwardPanel() {
@@ -3,7 +3,7 @@ import { useDebouncedEffect } from '@react-hookz/web'
3
3
  import { shallowEqual } from 'fast-equals'
4
4
  import { useEffect, useRef } from 'react'
5
5
  import { type DiagramState, type DiagramStoreApi, useDiagramState, useDiagramStoreApi } from '../state/hooks'
6
- import { useXYStore, useXYStoreApi, type XYStoreApi } from './hooks'
6
+ import { useXYStore, useXYStoreApi, type XYStoreApi } from './hooks/useXYFlow'
7
7
  import type { XYFlowState } from './types'
8
8
  import { toDomPrecision } from './utils'
9
9
 
@@ -2,7 +2,7 @@ import { nonNullable } from '@likec4/core'
2
2
  import type { EdgeChange, NodeChange } from '@xyflow/react'
3
3
  import { getBoundsOfRects, getViewportForBounds, nodeToRect } from '@xyflow/system'
4
4
  import { useEffect } from 'react'
5
- import { useDiagramStoreApi } from '../state/hooks'
5
+ import { useDiagramStoreApi } from '../state/useDiagramStore'
6
6
  import { MinZoom } from './const'
7
7
  import type { XYFlowEdge, XYFlowNode } from './types'
8
8
 
@@ -2,7 +2,7 @@ import { deepEqual as eq } from 'fast-equals'
2
2
 
3
3
  import { useEffect } from 'react'
4
4
  import { omit } from 'remeda'
5
- import { useDiagramState, useDiagramStoreApi } from '../state/hooks'
5
+ import { useDiagramState, useDiagramStoreApi } from '../state/useDiagramStore'
6
6
  import { diagramViewToXYFlowData } from './diagram-to-xyflow'
7
7
 
8
8
  /**
@@ -3,7 +3,8 @@ import { DEV } from 'esm-env'
3
3
  import { deepEqual as eq } from 'fast-equals'
4
4
  import { type CSSProperties, type PropsWithChildren, useCallback } from 'react'
5
5
  import useTilg from 'tilg'
6
- import { type DiagramState, useDiagramState, useDiagramStoreApi } from '../state/hooks'
6
+ import type { DiagramState } from '../state/diagramStore'
7
+ import { useDiagramState, useDiagramStoreApi } from '../state/useDiagramStore'
7
8
  import { MinZoom } from './const'
8
9
  import { RelationshipEdge } from './edges/RelationshipEdge'
9
10
  import { useLayoutConstraints } from './hooks/useLayoutConstraints'
@@ -1,7 +1,7 @@
1
1
  import type { ReactFlowProps } from '@xyflow/react'
2
2
  import { useMemo, useRef } from 'react'
3
3
  import type { Simplify } from 'type-fest'
4
- import { useDiagramStoreApi } from '../state/hooks'
4
+ import { useDiagramStoreApi } from '../state/useDiagramStore'
5
5
  import type { XYFlowEdge, XYFlowNode } from './types'
6
6
 
7
7
  export type XYFlowEventHandlers = Simplify<
@@ -3,7 +3,7 @@ import { shallowEqual } from 'fast-equals'
3
3
  import { memo, type PropsWithChildren } from 'react'
4
4
  import { isNonNull } from 'remeda'
5
5
  import type { LikeC4DiagramProperties } from '../LikeC4Diagram.props'
6
- import { useDiagramState } from '../state/hooks'
6
+ import { useDiagramState } from '../state/useDiagramStore'
7
7
  import { BackwardForwardPanel } from '../ui/BackwardForwardPanel'
8
8
  import DiagramTitlePanel from '../ui/DiagramTitlePanel'
9
9
  import { DynamicViewWalkthrough } from '../ui/DynamicViewWalkthrough'
@@ -16,10 +16,10 @@ import { curveCatmullRomOpen, line as d3line } from 'd3-shape'
16
16
  import { deepEqual, deepEqual as eq } from 'fast-equals'
17
17
  import { memo, type PointerEvent as ReactPointerEvent, useRef, useState } from 'react'
18
18
  import { first, hasAtLeast, isArray, isTruthy, last } from 'remeda'
19
- import { useDiagramState, useDiagramStoreApi } from '../../state/hooks'
19
+ import { useDiagramState, useDiagramStoreApi } from '../../state/useDiagramStore'
20
20
  import { ZIndexes } from '../const'
21
21
  import { EdgeMarkers } from '../EdgeMarkers'
22
- import { useXYStoreApi } from '../hooks'
22
+ import { useXYStoreApi } from '../hooks/useXYFlow'
23
23
  import { type RelationshipData, type XYFlowEdge } from '../types'
24
24
  import { bezierControlPoints } from '../utils'
25
25
  import * as edgesCss from './edges.css'
@@ -375,7 +375,7 @@ export const RelationshipEdge = /* @__PURE__ */ memo<EdgeProps<XYFlowEdge>>(func
375
375
  selected: selected ?? false,
376
376
  stepNum: data.stepNum,
377
377
  label: data.label,
378
- zIndex: edgeLookup.get(id)!.zIndex ?? ZIndexes.Edge,
378
+ zIndex: (edgeLookup.get(id)!.zIndex ?? ZIndexes.Edge) + 1,
379
379
  isHovered,
380
380
  isActive,
381
381
  isStepEdge
@@ -53,7 +53,7 @@ globalStyle(`:where(${isSelected}) ${container}[data-edge-hovered='true']`, {
53
53
  // })
54
54
 
55
55
  globalStyle(`.react-flow__edges > svg`, {
56
- mixBlendMode: 'screen'
56
+ mixBlendMode: 'plus-lighter'
57
57
  })
58
58
 
59
59
  // globalStyle(`:where([data-mantine-color-scheme="light"]) .react-flow__edges > svg`, {
@@ -63,7 +63,7 @@ globalStyle(`.react-flow__edges > svg`, {
63
63
  export const dimmed = style({})
64
64
 
65
65
  globalStyle(`.react-flow__edges > svg:has(${dimmed})`, {
66
- opacity: 0.8,
66
+ opacity: 0.6,
67
67
  transition: 'opacity 600ms ease-in-out, filter 600ms ease-in-out',
68
68
  transitionDelay: '200ms',
69
69
  filter: `grayscale(0.85) ${fallbackVar(vars.safariAnimationHook, 'blur(1px)')}`,
@@ -117,11 +117,11 @@ export const controlPoint = style({
117
117
  })
118
118
 
119
119
  export const controlDragging = style({
120
- cursor: 'grabbing'
120
+ cursor: 'grab'
121
121
  })
122
122
 
123
123
  globalStyle(`${controlDragging} *`, {
124
- cursor: 'grabbing'
124
+ cursor: 'grab'
125
125
  })
126
126
 
127
127
  const strokeKeyframes = keyframes({
@@ -174,7 +174,7 @@ export const edgeLabel = style({
174
174
  cursor: 'pointer',
175
175
  width: 'fit-content',
176
176
  transformOrigin: '50% 50%',
177
- mixBlendMode: 'screen',
177
+ mixBlendMode: 'plus-lighter',
178
178
  color: xyvars.edge.labelColor,
179
179
  backgroundColor: xyvars.edge.labelBgColor,
180
180
  borderRadius: 3,
@@ -188,7 +188,7 @@ export const edgeLabel = style({
188
188
  transform: `${varTranslate} scale(1.12)`
189
189
  },
190
190
  [`&:is(${dimmed})`]: {
191
- opacity: 0.6,
191
+ opacity: 0.3,
192
192
  transition: 'opacity 600ms ease-in-out, filter 600ms ease-in-out',
193
193
  transitionDelay: '200ms',
194
194
  filter: `grayscale(0.85) ${fallbackVar(vars.safariAnimationHook, 'blur(1px)')}`,
@@ -4,7 +4,7 @@ import type { InternalNode, ReactFlowProps, XYPosition } from '@xyflow/react'
4
4
  import { getNodeDimensions } from '@xyflow/system'
5
5
  import { useMemo, useRef } from 'react'
6
6
  import { isNullish } from 'remeda'
7
- import { useDiagramStoreApi } from '../../state/hooks'
7
+ import { useDiagramStoreApi } from '../../state/useDiagramStore'
8
8
  import type { XYFlowInstance, XYFlowNode } from '../types'
9
9
  import { isSamePoint } from '../utils'
10
10
  import { useXYStoreApi, type XYStoreApi } from './useXYFlow'
@@ -5,7 +5,7 @@ import clsx from 'clsx'
5
5
  import { deepEqual as eq } from 'fast-equals'
6
6
  import { memo } from 'react'
7
7
  import { clamp } from 'remeda'
8
- import { useDiagramState } from '../../../state/hooks'
8
+ import { useDiagramState } from '../../../state/useDiagramStore'
9
9
  import type { CompoundXYFlowNode } from '../../types'
10
10
  import { NavigateToBtn } from '../shared/NavigateToBtn'
11
11
  import * as css from './CompoundNode.css'
@@ -16,7 +16,8 @@ import { IconLink } from '@tabler/icons-react'
16
16
  import clsx from 'clsx'
17
17
  import { useId } from 'react'
18
18
  import { clamp } from 'remeda'
19
- import { type DiagramState, useDiagramState } from '../../../state/hooks'
19
+ import { type DiagramState } from '../../../state/diagramStore'
20
+ import { useDiagramState } from '../../../state/useDiagramStore'
20
21
  import type { XYFlowNode } from '../../types'
21
22
  import { elementLink, trigger } from './ElementLink.css'
22
23
 
@@ -1,19 +1,23 @@
1
- import { Text } from '@mantine/core'
1
+ import type { DiagramNode } from '@likec4/core/types'
2
+ import { Text as MantineText } from '@mantine/core'
2
3
  import { Handle, type NodeProps, Position } from '@xyflow/react'
3
4
  import clsx from 'clsx'
4
5
  import { deepEqual as eq } from 'fast-equals'
5
6
  import { m, type Variants } from 'framer-motion'
6
7
  import { memo } from 'react'
7
- import { isNumber } from 'remeda'
8
- import { useDiagramState } from '../../../state/hooks'
8
+ import { isNumber, isTruthy } from 'remeda'
9
+ import { useDiagramState } from '../../../state/useDiagramStore'
9
10
  import type { ElementXYFlowNode } from '../../types'
10
11
  import { toDomPrecision } from '../../utils'
11
12
  import { NavigateToBtn } from '../shared/NavigateToBtn'
12
13
  import * as css from './element.css'
13
- import { ElementIcon } from './ElementIcon'
14
14
  import { ElementLink } from './ElementLink'
15
15
  import { ElementShapeSvg, SelectedIndicator } from './ElementShapeSvg'
16
16
 
17
+ const Text = MantineText.withProps({
18
+ component: 'div'
19
+ })
20
+
17
21
  type ElementNodeProps = NodeProps<ElementXYFlowNode>
18
22
 
19
23
  const selectedScale = 1.015
@@ -143,6 +147,22 @@ export const ElementNodeMemo = memo<ElementNodeProps>(function ElementNode({
143
147
  whileTap: dragging ? animate : 'tap'
144
148
  })}
145
149
  >
150
+ {
151
+ /* <svg
152
+ className={clsx(
153
+ css.cssShapeSvgMultiple
154
+ )}
155
+ viewBox={`0 0 ${w} ${h}`}
156
+ width={w}
157
+ height={h}
158
+ >
159
+ <ElementShapeSvg
160
+ shape={element.shape}
161
+ w={w}
162
+ h={h}
163
+ />
164
+ </svg> */
165
+ }
146
166
  <svg
147
167
  className={clsx(
148
168
  css.cssShapeSvg
@@ -164,27 +184,32 @@ export const ElementNodeMemo = memo<ElementNodeProps>(function ElementNode({
164
184
  h={h}
165
185
  />
166
186
  </svg>
167
- <div className={clsx(css.element, 'likec4-element')}>
168
- <ElementIcon node={element} />
169
- <Text
170
- component="div"
171
- className={clsx(css.title, 'likec4-element-title')}>
172
- {element.title}
173
- </Text>
174
- {element.technology && (
175
- <Text
176
- component="div"
177
- className={clsx(css.technology, 'likec4-element-technology')}>
178
- {element.technology}
179
- </Text>
187
+ <div
188
+ className={clsx(
189
+ css.elementDataContainer,
190
+ isTruthy(element.icon) && css.hasIcon,
191
+ 'likec4-element'
180
192
  )}
181
- {element.description && (
193
+ >
194
+ {isTruthy(element.icon) && <ElementIcon node={element} />}
195
+ <div className={clsx(css.elementTextData, 'likec4-element-main-props')}>
182
196
  <Text
183
- component="div"
184
- className={clsx(css.description, 'likec4-element-description')}>
185
- {element.description}
197
+ className={clsx(css.title, 'likec4-element-title')}>
198
+ {element.title}
186
199
  </Text>
187
- )}
200
+ {element.technology && (
201
+ <Text
202
+ className={clsx(css.technology, 'likec4-element-technology')}>
203
+ {element.technology}
204
+ </Text>
205
+ )}
206
+ {element.description && (
207
+ <Text
208
+ className={clsx(css.description, 'likec4-element-description')}>
209
+ {element.description}
210
+ </Text>
211
+ )}
212
+ </div>
188
213
  </div>
189
214
  {isHovercards && element.links && <ElementLink element={element} />}
190
215
  {isNavigable && <NavigateToBtn xynodeId={id} className={css.cssNavigateBtn} />}
@@ -197,3 +222,26 @@ export const ElementNodeMemo = memo<ElementNodeProps>(function ElementNode({
197
222
  </>
198
223
  )
199
224
  }, isEqualProps)
225
+
226
+ const ElementIcon = ({ node }: { node: DiagramNode }) => {
227
+ const RenderIcon = useDiagramState(s => s.renderIcon)
228
+ if (!node.icon) {
229
+ return null
230
+ }
231
+ if (node.icon.startsWith('http://') || node.icon.startsWith('https://')) {
232
+ return (
233
+ <div className={clsx(css.elementIcon, 'likec4-element-icon')}>
234
+ <img src={node.icon} alt={node.title} />
235
+ </div>
236
+ )
237
+ }
238
+ const icon = RenderIcon ? <RenderIcon node={node} /> : null
239
+ if (!icon) {
240
+ return null
241
+ }
242
+ return (
243
+ <div className={clsx(css.elementIcon, 'likec4-element-icon')}>
244
+ {icon}
245
+ </div>
246
+ )
247
+ }
@@ -2,7 +2,7 @@ import { type ElementShape, nonexhaustive } from '@likec4/core'
2
2
  import { toDomPrecision } from '../../utils'
3
3
  import { fillElementFill, fillMixStroke } from './element.css'
4
4
 
5
- function cylinderSVGPath(diameter: number, height: number, tilt = 0.0725) {
5
+ export function cylinderSVGPath(diameter: number, height: number, tilt = 0.065) {
6
6
  const radius = Math.round(diameter / 2)
7
7
  // const tiltAdjustedHeight = height * Math.cos((tilt * Math.PI) / 2)
8
8
  const rx = radius
@@ -25,7 +25,7 @@ function cylinderSVGPath(diameter: number, height: number, tilt = 0.0725) {
25
25
  }
26
26
  }
27
27
 
28
- function queueSVGPath(width: number, height: number, tilt = 0.2) {
28
+ function queueSVGPath(width: number, height: number, tilt = 0.185) {
29
29
  const diameter = height
30
30
  const radius = Math.round(diameter / 2)
31
31
  const ry = radius