likec4 0.44.1 → 0.45.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 (32) hide show
  1. package/dist/@likec4/diagrams/components/primitives/fullscreen/FullscreenDiagram.js +8 -7
  2. package/dist/@likec4/diagrams/diagram/Diagram.js +16 -5
  3. package/dist/@likec4/diagrams/diagram/Edges.js +32 -16
  4. package/dist/@likec4/diagrams/diagram/Nodes.js +75 -77
  5. package/dist/@likec4/diagrams/diagram/icons/ZoomIn.js +2 -3
  6. package/dist/@likec4/diagrams/diagram/shapes/Browser.js +43 -9
  7. package/dist/@likec4/diagrams/diagram/shapes/Compound.js +8 -10
  8. package/dist/@likec4/diagrams/diagram/shapes/Cylinder.js +3 -1
  9. package/dist/@likec4/diagrams/diagram/shapes/Edge.js +7 -7
  10. package/dist/@likec4/diagrams/diagram/shapes/Mobile.js +19 -4
  11. package/dist/@likec4/diagrams/diagram/shapes/NodeIcon.js +47 -9
  12. package/dist/@likec4/diagrams/diagram/shapes/NodeLabel.js +31 -54
  13. package/dist/@likec4/diagrams/diagram/shapes/Person.js +3 -1
  14. package/dist/@likec4/diagrams/diagram/shapes/Queue.js +3 -1
  15. package/dist/@likec4/diagrams/diagram/shapes/Rectangle.js +3 -1
  16. package/dist/@likec4/diagrams/diagram/shapes/index.js +1 -1
  17. package/dist/@likec4/diagrams/diagram/shapes/utils.js +1 -1
  18. package/dist/@likec4/diagrams/diagram/state/atoms.js +6 -0
  19. package/dist/@likec4/diagrams/diagram/state/hooks.js +10 -1
  20. package/dist/@likec4/diagrams/hooks/useImageLoader.js +7 -1
  21. package/dist/__app__/likec4.css +4 -0
  22. package/dist/__app__/src/App.jsx +25 -5
  23. package/dist/__app__/src/components/DiagramNotFound.jsx +10 -4
  24. package/dist/__app__/src/pages/embed.page.jsx +12 -0
  25. package/dist/__app__/src/pages/export.page.jsx +9 -6
  26. package/dist/__app__/src/pages/index.js +1 -0
  27. package/dist/__app__/src/pages/view.page.jsx +8 -8
  28. package/dist/__app__/src/router.js +2 -2
  29. package/dist/__app__/tailwind.config.cjs +1 -3
  30. package/dist/__app__/tsconfig.json +1 -2
  31. package/dist/cli/index.js +145 -145
  32. package/package.json +6 -7
@@ -12,23 +12,24 @@ const StyleOverlay = {
12
12
  left: 0,
13
13
  right: 0,
14
14
  bottom: 0,
15
- backgroundColor: "var(--likec4-browser-overlay-bg, rgba(18,18,18,0.8))",
15
+ backgroundColor: "var(--likec4-browser-overlay-bg, rgba(18,18,18,0.9))",
16
16
  backdropFilter: "var(--likec4-browser-backdrop, blur(4px))",
17
17
  zIndex: "var(--likec4-overlay-z-index, 100)",
18
- display: "flex",
19
- placeContent: "strech",
20
- placeItems: "strech",
21
- touchAction: "pan-x pan-y pinch-zoom",
22
18
  boxSizing: "border-box",
23
19
  margin: 0,
24
20
  padding: 0,
25
21
  border: "0 solid transparent"
26
22
  };
27
23
  const StyleContainer = {
24
+ position: "absolute",
25
+ top: 0,
26
+ left: 0,
27
+ right: 0,
28
+ bottom: 0,
28
29
  margin: 0,
29
30
  padding: 0,
30
- flex: "1 1 100%",
31
- overflow: "hidden"
31
+ overflow: "hidden",
32
+ touchAction: "pan-x pan-y pinch-zoom"
32
33
  };
33
34
  export function FullscreenDiagram({
34
35
  diagram,
@@ -8,7 +8,7 @@ import { AnimatedStage, Layer } from "../konva.js";
8
8
  import { Edges } from "./Edges.js";
9
9
  import { createUseGesture, dragAction, pinchAction } from "@use-gesture/react";
10
10
  import { Nodes } from "./Nodes.js";
11
- import { DiagramGesture } from "./state/index.js";
11
+ import { DiagramGesture, useResetHoveredStates } from "./state/index.js";
12
12
  const useGesture = createUseGesture([dragAction, pinchAction]);
13
13
  const useSyncedRef = (value) => {
14
14
  const ref = useRef(value);
@@ -42,6 +42,8 @@ export const Diagram = /* @__PURE__ */ forwardRef(
42
42
  onStageContextMenu,
43
43
  width: _width,
44
44
  height: _height,
45
+ minZoom = 0.2,
46
+ maxZoom = 1.1,
45
47
  ...props
46
48
  }, ref) => {
47
49
  const immediate = !animate;
@@ -62,7 +64,7 @@ export const Diagram = /* @__PURE__ */ forwardRef(
62
64
  const viewRect = {
63
65
  width: Math.min(container?.clientWidth ?? width, width) - paddingLeft - paddingRight,
64
66
  height: Math.min(container?.clientHeight ?? height, height) - paddingTop - paddingBottom
65
- }, viewScale = Math.min(viewRect.width / centerTo.width, viewRect.height / centerTo.height), scale = clamp(0.1, 1.1, viewScale), centeringAjustment = {
67
+ }, viewScale = Math.min(viewRect.width / centerTo.width, viewRect.height / centerTo.height), scale = clamp(minZoom, maxZoom, viewScale), centeringAjustment = {
66
68
  x: ((width - centerTo.width) * scale + viewRect.width) / 2,
67
69
  y: ((height - centerTo.height) * scale + viewRect.height) / 2
68
70
  }, finalPosition = {
@@ -147,6 +149,10 @@ export const Diagram = /* @__PURE__ */ forwardRef(
147
149
  }),
148
150
  [refs, id, stageRef]
149
151
  );
152
+ const resetHoveredStates = useResetHoveredStates();
153
+ useUpdateEffect(() => {
154
+ resetHoveredStates();
155
+ }, [id]);
150
156
  useUpdateEffect(() => {
151
157
  refs.current.centerAndFit(80, 650);
152
158
  }, [id, height, width]);
@@ -213,21 +219,26 @@ export const Diagram = /* @__PURE__ */ forwardRef(
213
219
  {
214
220
  target: containerRef,
215
221
  drag: {
222
+ target: containerRef,
216
223
  enabled: pannable,
217
224
  threshold: 4,
218
225
  from: () => [stageProps.x.get(), stageProps.y.get()],
219
226
  pointer: {
220
227
  buttons: -1,
221
- keys: false
228
+ keys: false,
229
+ touch: true,
230
+ capture: true
222
231
  }
223
232
  },
224
233
  pinch: {
234
+ target: containerRef,
225
235
  pointer: {
226
236
  touch: true
227
237
  },
228
238
  enabled: zoomable,
229
- scaleBounds: { min: 0.1, max: 1.4 },
230
- rubberband: 0.05,
239
+ // eventOptions
240
+ scaleBounds: { min: minZoom, max: maxZoom + 0.4 },
241
+ rubberband: 0.04,
231
242
  pinchOnWheel: true
232
243
  }
233
244
  }
@@ -1,11 +1,12 @@
1
1
  import { jsx } from "react/jsx-runtime";
2
2
  import { DefaultRelationshipColor } from "@likec4/core";
3
+ import { isEqualSimple } from "@react-hookz/deep-equal/esnext";
3
4
  import { useTransition } from "@react-spring/konva";
4
5
  import { scale, toHex } from "khroma";
5
6
  import { memoize } from "rambdax";
6
- import { useCallback } from "react";
7
- import { Group } from "../konva.js";
8
- import { EdgeShape } from "./shapes/Edge.js";
7
+ import { memo, useCallback } from "react";
8
+ import { AnimatedGroup } from "../konva.js";
9
+ import { Edge } from "./shapes/Edge.js";
9
10
  import { mouseDefault, mousePointer } from "./shapes/utils.js";
10
11
  import { DiagramGesture, useHoveredEdgeId, useSetHoveredEdge } from "./state/index.js";
11
12
  const edgeColors = memoize((colors, isHovered) => {
@@ -13,13 +14,13 @@ const edgeColors = memoize((colors, isHovered) => {
13
14
  return {
14
15
  lineColor: toHex(
15
16
  scale(colors.lineColor, {
16
- l: 25,
17
- s: -5
17
+ l: 30
18
18
  })
19
19
  ),
20
20
  labelColor: toHex(
21
21
  scale(colors.labelColor, {
22
- l: 40
22
+ l: 40,
23
+ s: 5
23
24
  })
24
25
  ),
25
26
  labelBgColor: toHex(
@@ -34,7 +35,6 @@ const edgeColors = memoize((colors, isHovered) => {
34
35
  });
35
36
  export function Edges({ animate, theme, diagram, onEdgeClick }) {
36
37
  const hoveredEdgeId = useHoveredEdgeId();
37
- const setHoveredEdge = useSetHoveredEdge();
38
38
  const edgeSprings = useCallback(
39
39
  (edge, isHovered = false) => {
40
40
  return {
@@ -83,9 +83,25 @@ export function Edges({ animate, theme, diagram, onEdgeClick }) {
83
83
  // to avoid any issues with diagram-to-diagram transitions
84
84
  keys: (e) => e.id + diagram.id
85
85
  });
86
- return edgeTransitions((springs, edge, { key }) => /* @__PURE__ */ jsx(
87
- Group,
86
+ return edgeTransitions((_, edge, { key, ctrl }) => /* @__PURE__ */ jsx(
87
+ EdgeShape,
88
+ {
89
+ animate,
90
+ edge,
91
+ isHovered: hoveredEdgeId === edge.id,
92
+ theme,
93
+ ctrl,
94
+ onEdgeClick
95
+ },
96
+ key
97
+ ));
98
+ }
99
+ const EdgeShape = memo(({ animate, edge, ctrl, theme, isHovered, onEdgeClick }) => {
100
+ const setHoveredEdge = useSetHoveredEdge();
101
+ return /* @__PURE__ */ jsx(
102
+ AnimatedGroup,
88
103
  {
104
+ opacity: ctrl.springs.opacity,
89
105
  onPointerClick: (e) => {
90
106
  if (!onEdgeClick || DiagramGesture.isDragging || e.evt.button !== 0) {
91
107
  return;
@@ -104,16 +120,16 @@ export function Edges({ animate, theme, diagram, onEdgeClick }) {
104
120
  mouseDefault(e);
105
121
  },
106
122
  children: /* @__PURE__ */ jsx(
107
- EdgeShape,
123
+ Edge,
108
124
  {
109
125
  animate,
110
126
  edge,
111
- isHovered: hoveredEdgeId === edge.id,
127
+ isHovered,
112
128
  theme,
113
- springs
129
+ springs: ctrl.springs
114
130
  }
115
131
  )
116
- },
117
- key
118
- ));
119
- }
132
+ }
133
+ );
134
+ }, isEqualSimple);
135
+ EdgeShape.displayName = "EdgeShape";
@@ -1,7 +1,8 @@
1
1
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
2
2
  import { nonexhaustive } from "@likec4/core";
3
+ import { isEqualSimple } from "@react-hookz/deep-equal/esnext";
3
4
  import { useTransition } from "@react-spring/konva";
4
- import { useRef } from "react";
5
+ import { memo, useRef } from "react";
5
6
  import { AnimatedGroup } from "../konva.js";
6
7
  import { Portal } from "../konva-portal.js";
7
8
  import { ZoomInIcon } from "./icons/index.js";
@@ -115,7 +116,7 @@ export function Nodes({ animate, theme, diagram, onNodeClick }) {
115
116
  keys: keyOf
116
117
  });
117
118
  return nodeTransitions((_, node, { key, ctrl, expired }) => /* @__PURE__ */ jsx(
118
- NodeSnape,
119
+ NodeShape,
119
120
  {
120
121
  animate,
121
122
  node,
@@ -128,81 +129,78 @@ export function Nodes({ animate, theme, diagram, onNodeClick }) {
128
129
  key
129
130
  ));
130
131
  }
131
- function NodeSnape({
132
- animate,
133
- node,
134
- ctrl,
135
- theme,
136
- isHovered,
137
- expired,
138
- onNodeClick
139
- }) {
140
- const setHoveredNode = useSetHoveredNode();
141
- const _isCompound = isCompound(node);
142
- const isNavigatable = !!node.navigateTo && !!onNodeClick;
143
- const Shape = nodeShape(node);
144
- const springs = ctrl.springs;
145
- return /* @__PURE__ */ jsx(Portal, { selector: ".top", enabled: isHovered && !_isCompound, children: /* @__PURE__ */ jsxs(
146
- AnimatedGroup,
147
- {
148
- name: node.id,
149
- visible: expired !== true,
150
- ...animate && {
151
- onPointerEnter: (e) => {
152
- setHoveredNode(node);
153
- if (isNavigatable) {
154
- mousePointer(e);
132
+ const NodeShape = memo(
133
+ ({ animate, node, ctrl, theme, isHovered, expired, onNodeClick }) => {
134
+ const setHoveredNode = useSetHoveredNode();
135
+ const _isCompound = isCompound(node);
136
+ const isNavigatable = !!node.navigateTo && !!onNodeClick;
137
+ const Shape = nodeShape(node);
138
+ const springs = ctrl.springs;
139
+ let zoomInIconY;
140
+ switch (node.shape) {
141
+ case "browser":
142
+ case "mobile":
143
+ zoomInIconY = node.size.height - 20;
144
+ break;
145
+ default:
146
+ zoomInIconY = node.size.height - 16;
147
+ }
148
+ return /* @__PURE__ */ jsx(Portal, { selector: ".top", enabled: isHovered && !_isCompound, children: /* @__PURE__ */ jsxs(
149
+ AnimatedGroup,
150
+ {
151
+ name: node.id,
152
+ visible: expired !== true,
153
+ ...animate && {
154
+ onPointerEnter: (e) => {
155
+ setHoveredNode(node);
156
+ if (isNavigatable) {
157
+ mousePointer(e);
158
+ }
159
+ },
160
+ onPointerLeave: (e) => {
161
+ setHoveredNode(null);
162
+ mouseDefault(e);
155
163
  }
156
164
  },
157
- onPointerLeave: (e) => {
158
- setHoveredNode(null);
159
- mouseDefault(e);
160
- }
161
- },
162
- ...onNodeClick && {
163
- onPointerClick: (e) => {
164
- if (DiagramGesture.isDragging || e.evt.button !== 0) {
165
- return;
166
- }
167
- e.cancelBubble = true;
168
- onNodeClick(node, e);
169
- }
170
- },
171
- x: springs.x,
172
- y: springs.y,
173
- offsetX: springs.offsetX,
174
- offsetY: springs.offsetY,
175
- width: springs.width,
176
- height: springs.height,
177
- scaleX: springs.scaleX,
178
- scaleY: springs.scaleY,
179
- opacity: springs.opacity,
180
- children: [
181
- _isCompound && /* @__PURE__ */ jsxs(Fragment, { children: [
182
- /* @__PURE__ */ jsx(
183
- CompoundShape,
184
- {
185
- node,
186
- theme,
187
- springs,
188
- labelOffsetX: isNavigatable ? -12 : 4
165
+ ...onNodeClick && {
166
+ onPointerClick: (e) => {
167
+ if (DiagramGesture.isDragging || e.evt.button !== 0) {
168
+ return;
189
169
  }
190
- ),
191
- isNavigatable && /* @__PURE__ */ jsx(ZoomInIcon, { fill: "#BABABA", opacity: 0.9, size: 16, x: 16, y: 17 })
192
- ] }),
193
- !_isCompound && /* @__PURE__ */ jsxs(Fragment, { children: [
194
- /* @__PURE__ */ jsx(Shape, { node, theme, springs, isHovered }),
195
- isNavigatable && /* @__PURE__ */ jsx(
196
- ZoomInIcon,
197
- {
198
- fill: "#BABABA",
199
- size: 16,
200
- x: node.size.width / 2,
201
- y: node.size.height - 20
202
- }
203
- )
204
- ] })
205
- ]
206
- }
207
- ) });
208
- }
170
+ e.cancelBubble = true;
171
+ onNodeClick(node, e);
172
+ }
173
+ },
174
+ x: springs.x,
175
+ y: springs.y,
176
+ offsetX: springs.offsetX,
177
+ offsetY: springs.offsetY,
178
+ width: springs.width,
179
+ height: springs.height,
180
+ scaleX: springs.scaleX,
181
+ scaleY: springs.scaleY,
182
+ opacity: springs.opacity,
183
+ children: [
184
+ _isCompound && /* @__PURE__ */ jsxs(Fragment, { children: [
185
+ /* @__PURE__ */ jsx(
186
+ CompoundShape,
187
+ {
188
+ node,
189
+ theme,
190
+ springs,
191
+ labelOffsetX: isNavigatable ? -18 : 0
192
+ }
193
+ ),
194
+ isNavigatable && /* @__PURE__ */ jsx(ZoomInIcon, { opacity: 0.9, size: 18, x: 18, y: 22 })
195
+ ] }),
196
+ !_isCompound && /* @__PURE__ */ jsxs(Fragment, { children: [
197
+ /* @__PURE__ */ jsx(Shape, { node, theme, springs, isHovered }),
198
+ isNavigatable && /* @__PURE__ */ jsx(ZoomInIcon, { size: 16, x: node.size.width / 2, y: zoomInIconY })
199
+ ] })
200
+ ]
201
+ }
202
+ ) });
203
+ },
204
+ isEqualSimple
205
+ );
206
+ NodeShape.displayName = "NodeShape";
@@ -1,6 +1,6 @@
1
1
  import { jsx } from "react/jsx-runtime";
2
2
  import { Path } from "../../konva.js";
3
- export const ZoomInIcon = ({ fill, opacity = 1, size = 20, x, y }) => {
3
+ export const ZoomInIcon = ({ fill = "#BABABA", opacity = 1, size = 20, x, y }) => {
4
4
  const originalSize = 15;
5
5
  const scale = size / originalSize;
6
6
  const offsetIcon = originalSize / 2;
@@ -20,8 +20,7 @@ export const ZoomInIcon = ({ fill, opacity = 1, size = 20, x, y }) => {
20
20
  width: originalSize,
21
21
  height: originalSize,
22
22
  opacity,
23
- globalCompositeOperation: "luminosity",
24
- hitStrokeWidth: 5
23
+ globalCompositeOperation: "luminosity"
25
24
  }
26
25
  );
27
26
  };
@@ -1,9 +1,9 @@
1
1
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
2
+ import { AnimatedCircle, AnimatedRect } from "../../konva.js";
2
3
  import { useShadowSprings } from "../springs.js";
3
- import { AnimatedRect, Circle } from "../../konva.js";
4
+ import { NodeIcon } from "./NodeIcon.js";
4
5
  import { NodeLabels } from "./NodeLabel.js";
5
6
  export function BrowserShape({ node, theme, springs, isHovered }) {
6
- const colors = theme.elements[node.color];
7
7
  return /* @__PURE__ */ jsxs(Fragment, { children: [
8
8
  /* @__PURE__ */ jsx(
9
9
  AnimatedRect,
@@ -13,12 +13,43 @@ export function BrowserShape({ node, theme, springs, isHovered }) {
13
13
  strokeEnabled: false,
14
14
  width: springs.width,
15
15
  height: springs.height,
16
- fill: springs.stroke
16
+ fill: springs.stroke,
17
+ perfectDrawEnabled: false
18
+ }
19
+ ),
20
+ /* @__PURE__ */ jsx(
21
+ AnimatedCircle,
22
+ {
23
+ x: 16,
24
+ y: 15,
25
+ radius: 7,
26
+ fill: springs.fill,
27
+ listening: false,
28
+ perfectDrawEnabled: false
29
+ }
30
+ ),
31
+ /* @__PURE__ */ jsx(
32
+ AnimatedCircle,
33
+ {
34
+ x: 36,
35
+ y: 15,
36
+ radius: 7,
37
+ fill: springs.fill,
38
+ listening: false,
39
+ perfectDrawEnabled: false
40
+ }
41
+ ),
42
+ /* @__PURE__ */ jsx(
43
+ AnimatedCircle,
44
+ {
45
+ x: 56,
46
+ y: 15,
47
+ radius: 7,
48
+ fill: springs.fill,
49
+ listening: false,
50
+ perfectDrawEnabled: false
17
51
  }
18
52
  ),
19
- /* @__PURE__ */ jsx(Circle, { x: 16, y: 15, radius: 7, fill: colors.fill, listening: false }),
20
- /* @__PURE__ */ jsx(Circle, { x: 36, y: 15, radius: 7, fill: colors.fill, listening: false }),
21
- /* @__PURE__ */ jsx(Circle, { x: 56, y: 15, radius: 7, fill: colors.fill, listening: false }),
22
53
  /* @__PURE__ */ jsx(
23
54
  AnimatedRect,
24
55
  {
@@ -28,7 +59,8 @@ export function BrowserShape({ node, theme, springs, isHovered }) {
28
59
  width: springs.width.to((w) => w - 80),
29
60
  height: 16,
30
61
  fill: springs.fill,
31
- listening: false
62
+ listening: false,
63
+ perfectDrawEnabled: false
32
64
  }
33
65
  ),
34
66
  /* @__PURE__ */ jsx(
@@ -40,9 +72,11 @@ export function BrowserShape({ node, theme, springs, isHovered }) {
40
72
  width: springs.width.to((w) => w - 18),
41
73
  height: springs.height.to((h) => h - 40),
42
74
  fill: springs.fill,
43
- listening: false
75
+ listening: false,
76
+ perfectDrawEnabled: false
44
77
  }
45
78
  ),
46
- /* @__PURE__ */ jsx(NodeLabels, { node, theme, offsetY: -8 })
79
+ /* @__PURE__ */ jsx(NodeLabels, { node, theme, offsetY: -6 }),
80
+ /* @__PURE__ */ jsx(NodeIcon, { node, paddingY: 42 })
47
81
  ] });
48
82
  }
@@ -1,6 +1,6 @@
1
1
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
2
- import { AnimatedRect, AnimatedText } from "../../konva.js";
3
- export function CompoundShape({ node, theme, springs, labelOffsetX = 4 }) {
2
+ import { AnimatedRect, Text } from "../../konva.js";
3
+ export function CompoundShape({ node, theme, springs, labelOffsetX = 0 }) {
4
4
  const { labels } = node;
5
5
  return /* @__PURE__ */ jsxs(Fragment, { children: [
6
6
  /* @__PURE__ */ jsx(
@@ -21,25 +21,23 @@ export function CompoundShape({ node, theme, springs, labelOffsetX = 4 }) {
21
21
  }
22
22
  ),
23
23
  labels.map(({ pt: [x, y], ...label }, i) => /* @__PURE__ */ jsx(
24
- AnimatedText,
24
+ Text,
25
25
  {
26
- x,
27
- y: y - 4,
26
+ x: x - 6,
27
+ y: y - 8,
28
28
  offsetX: labelOffsetX,
29
- offsetY: label.fontSize / 2,
30
- width: springs.width.to((v) => v - x - 4),
29
+ width: node.size.width - x - 8 - 6 + labelOffsetX,
31
30
  fill: "#BABABA",
32
31
  fontFamily: theme.font,
33
32
  fontSize: label.fontSize,
34
33
  fontStyle: label.fontStyle ?? "normal",
35
- letterSpacing: 0.8,
34
+ letterSpacing: 0.75,
36
35
  align: label.align,
37
36
  text: label.text,
38
37
  wrap: "none",
39
38
  ellipsis: true,
40
39
  perfectDrawEnabled: false,
41
- padding: 6,
42
- hitStrokeWidth: 3,
40
+ padding: 8,
43
41
  globalCompositeOperation: "luminosity"
44
42
  },
45
43
  i
@@ -3,6 +3,7 @@ import { useSpring } from "@react-spring/konva";
3
3
  import { AnimatedEllipse, AnimatedPath } from "../../konva.js";
4
4
  import { NodeLabels } from "./NodeLabel.js";
5
5
  import { useShadowSprings } from "../springs.js";
6
+ import { NodeIcon } from "./NodeIcon.js";
6
7
  function cylinderSVGPath(diameter, height, tilt = 0.0825) {
7
8
  const radius = Math.round(diameter / 2);
8
9
  const rx = radius;
@@ -51,6 +52,7 @@ export function CylinderShape({ node, theme, springs, isHovered }) {
51
52
  fill: springs.stroke
52
53
  }
53
54
  ),
54
- /* @__PURE__ */ jsx(NodeLabels, { node, offsetY: -ry * (node.icon ? 1.5 : 0.5), theme })
55
+ /* @__PURE__ */ jsx(NodeLabels, { node, offsetY: -4 - ry * (node.icon ? 1 : 0), theme }),
56
+ /* @__PURE__ */ jsx(NodeIcon, { node, paddingY: ry + 4, offsetY: -ry - 8 })
55
57
  ] });
56
58
  }
@@ -16,7 +16,6 @@ function EdgeArrow({
16
16
  return /* @__PURE__ */ jsx(
17
17
  AnimatedLine,
18
18
  {
19
- opacity: springs.opacity,
20
19
  points: points.flat(),
21
20
  closed: true,
22
21
  fill: isOutline ? void 0 : springs.lineColor,
@@ -25,6 +24,7 @@ function EdgeArrow({
25
24
  hitStrokeWidth: 5,
26
25
  lineCap: "round",
27
26
  lineJoin: "miter",
27
+ perfectDrawEnabled: false,
28
28
  globalCompositeOperation
29
29
  }
30
30
  );
@@ -42,7 +42,7 @@ function EdgeLabelBg({
42
42
  y: labelBBox.y - padding,
43
43
  width: labelBBox.width + padding * 2,
44
44
  height: labelBBox.height + padding * 2,
45
- opacity: isHovered ? 0.25 : 0.1
45
+ opacity: isHovered ? 0.5 : 0.12
46
46
  },
47
47
  immediate: !animate
48
48
  });
@@ -50,6 +50,7 @@ function EdgeLabelBg({
50
50
  AnimatedRect,
51
51
  {
52
52
  ...props,
53
+ perfectDrawEnabled: false,
53
54
  fill: springs.labelBgColor,
54
55
  cornerRadius: 2,
55
56
  globalCompositeOperation: "darken",
@@ -57,7 +58,7 @@ function EdgeLabelBg({
57
58
  }
58
59
  );
59
60
  }
60
- export function EdgeShape({ animate = true, edge, theme, isHovered, springs }) {
61
+ export function Edge({ animate = true, edge, theme, isHovered, springs }) {
61
62
  const {
62
63
  points,
63
64
  head,
@@ -82,7 +83,6 @@ export function EdgeShape({ animate = true, edge, theme, isHovered, springs }) {
82
83
  /* @__PURE__ */ jsx(
83
84
  AnimatedLine,
84
85
  {
85
- opacity: springs.opacity,
86
86
  bezier: true,
87
87
  dashEnabled: isDashed,
88
88
  dashOffset: 1,
@@ -93,6 +93,7 @@ export function EdgeShape({ animate = true, edge, theme, isHovered, springs }) {
93
93
  hitStrokeWidth: 20,
94
94
  lineCap: "round",
95
95
  lineJoin: "round",
96
+ perfectDrawEnabled: false,
96
97
  globalCompositeOperation
97
98
  }
98
99
  ),
@@ -129,14 +130,13 @@ export function EdgeShape({ animate = true, edge, theme, isHovered, springs }) {
129
130
  AnimatedText,
130
131
  {
131
132
  x: label.pt[0],
132
- y: label.pt[1],
133
- offsetY: label.fontSize / 2,
134
- opacity: springs.opacity,
133
+ y: label.pt[1] - label.fontSize,
135
134
  fill: springs.labelColor,
136
135
  fontFamily: theme.font,
137
136
  fontSize: label.fontSize,
138
137
  fontStyle: label.fontStyle ?? "normal",
139
138
  text: label.text,
139
+ perfectDrawEnabled: false,
140
140
  listening: false,
141
141
  globalCompositeOperation,
142
142
  shadowEnabled: springs.opacity.to((o) => o > 0.5),
@@ -1,9 +1,11 @@
1
1
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
2
- import { AnimatedRect, Circle } from "../../konva.js";
2
+ import { AnimatedCircle, AnimatedRect } from "../../konva.js";
3
3
  import { NodeLabels } from "./NodeLabel.js";
4
4
  import { useShadowSprings } from "../springs.js";
5
+ import { NodeIcon } from "./NodeIcon.js";
5
6
  export function MobileShape({ node, theme, springs, isHovered }) {
6
7
  const colors = theme.elements[node.color];
8
+ const maxWidth = node.size.width - 40 - 30;
7
9
  return /* @__PURE__ */ jsxs(Fragment, { children: [
8
10
  /* @__PURE__ */ jsx(
9
11
  AnimatedRect,
@@ -12,10 +14,21 @@ export function MobileShape({ node, theme, springs, isHovered }) {
12
14
  cornerRadius: 6,
13
15
  width: springs.width,
14
16
  height: springs.height,
15
- fill: springs.stroke
17
+ fill: springs.stroke,
18
+ perfectDrawEnabled: false
19
+ }
20
+ ),
21
+ /* @__PURE__ */ jsx(
22
+ AnimatedCircle,
23
+ {
24
+ x: 16,
25
+ y: node.size.height / 2,
26
+ radius: 10,
27
+ fill: springs.fill,
28
+ listening: false,
29
+ perfectDrawEnabled: false
16
30
  }
17
31
  ),
18
- /* @__PURE__ */ jsx(Circle, { x: 16, y: node.size.height / 2, radius: 10, fill: colors.fill, listening: false }),
19
32
  /* @__PURE__ */ jsx(
20
33
  AnimatedRect,
21
34
  {
@@ -25,9 +38,11 @@ export function MobileShape({ node, theme, springs, isHovered }) {
25
38
  width: springs.width.to((w) => w - 43),
26
39
  height: springs.height.to((h) => h - 24),
27
40
  fill: springs.fill,
41
+ perfectDrawEnabled: false,
28
42
  listening: false
29
43
  }
30
44
  ),
31
- /* @__PURE__ */ jsx(NodeLabels, { node, theme, offsetX: -6 })
45
+ /* @__PURE__ */ jsx(NodeLabels, { node, theme, offsetX: -10, maxWidth }),
46
+ /* @__PURE__ */ jsx(NodeIcon, { node, offsetX: -10, maxWidth })
32
47
  ] });
33
48
  }