likec4 0.43.1 → 0.44.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/README.md +28 -12
- package/dist/@likec4/diagrams/diagram/Nodes.js +32 -4
- package/dist/@likec4/diagrams/diagram/icons/ZoomIn.js +27 -0
- package/dist/@likec4/diagrams/diagram/icons/index.js +1 -0
- package/dist/@likec4/diagrams/diagram/shapes/Compound.js +4 -4
- package/dist/__app__/src/components/view-page/DisplayModeSelector.jsx +50 -0
- package/dist/__app__/src/components/view-page/ViewActionsToolbar.jsx +5 -36
- package/package.json +6 -6
package/README.md
CHANGED
|
@@ -4,10 +4,10 @@
|
|
|
4
4
|
|
|
5
5
|
Features:
|
|
6
6
|
|
|
7
|
-
-
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
7
|
+
- Preview diagrams in a local web server (with lightning fast updates) ⚡️
|
|
8
|
+
- Build a static .website (deploy to github pages, netlify...) 🔗
|
|
9
|
+
- Export to PNG, Mermaid, Dot, D2 (if you something static) 🖼️
|
|
10
|
+
- Generate React components (for custom integrations ) 🛠️
|
|
11
11
|
|
|
12
12
|
## Install
|
|
13
13
|
|
|
@@ -33,10 +33,6 @@ You can reference it directly in the `package.json#scripts` object:
|
|
|
33
33
|
}
|
|
34
34
|
```
|
|
35
35
|
|
|
36
|
-
> **Template:**
|
|
37
|
-
> Check out the template repository [likec4/template](https://github.com/likec4/template)
|
|
38
|
-
> with pre-configured CI for building and deploying to github pages.
|
|
39
|
-
|
|
40
36
|
To use the binary, you can call it with [`npx`](https://docs.npmjs.com/cli/v10/commands/npx) while in the project directory:
|
|
41
37
|
|
|
42
38
|
```sh
|
|
@@ -88,14 +84,15 @@ Any changes in the sources trigger a super-fast hot update and you see changes i
|
|
|
88
84
|
|
|
89
85
|
### Build static website
|
|
90
86
|
|
|
91
|
-
|
|
92
|
-
Build a single-page application with all diagrams:
|
|
87
|
+
Build a single HTML with diagrams, ready to be embedded into your website:
|
|
93
88
|
|
|
94
89
|
```sh
|
|
95
90
|
likec4 build -o ./dist
|
|
96
91
|
```
|
|
97
92
|
|
|
98
|
-
|
|
93
|
+
Example [https://template.likec4.dev](https://template.likec4.dev/view/cloud)
|
|
94
|
+
|
|
95
|
+
When you deployed the website, you can use "Share" button to get a link to a specific diagram.
|
|
99
96
|
|
|
100
97
|
> **Tip:**
|
|
101
98
|
> [likec4/template](https://github.com/likec4/template) repository demonstrates how to deploy to github pages.
|
|
@@ -106,13 +103,16 @@ There is also a supplementary command to preview the build:
|
|
|
106
103
|
likec4 preview -o ./dist
|
|
107
104
|
```
|
|
108
105
|
|
|
106
|
+
For example, this command can be used on CI, to compare diagrams with ones from the previous/main build.
|
|
107
|
+
|
|
109
108
|
### Export to PNG
|
|
110
109
|
|
|
111
110
|
```sh
|
|
112
111
|
likec4 export png -o ./assets
|
|
113
112
|
```
|
|
114
113
|
|
|
115
|
-
This command starts
|
|
114
|
+
This command starts local web server and uses Playwright to take screenshots.
|
|
115
|
+
If you plan to use it on CI, refer to [Playwright documentation](https://playwright.dev/docs/ci) for details.
|
|
116
116
|
|
|
117
117
|
### Export to Mermaid, Dot, D2
|
|
118
118
|
|
|
@@ -131,11 +131,15 @@ likec4 codegen d2
|
|
|
131
131
|
likec4 codegen react --outfile ./src/likec4.generated.tsx
|
|
132
132
|
```
|
|
133
133
|
|
|
134
|
+
Check [documentation](https://likec4.dev/docs/tools/react/)
|
|
135
|
+
|
|
134
136
|
> Output file should have `.tsx` extension
|
|
135
137
|
> By default, it generates `likec4.generated.tsx` in current directory
|
|
136
138
|
|
|
137
139
|
### Generate structured data
|
|
138
140
|
|
|
141
|
+
Generate a TypeScript file with `LikeC4Views` object, which contains all diagrams and their metadata.
|
|
142
|
+
|
|
139
143
|
```sh
|
|
140
144
|
likec4 codegen views-data --outfile ./src/likec4.generated.ts
|
|
141
145
|
|
|
@@ -147,6 +151,18 @@ likec4 codegen ts ...
|
|
|
147
151
|
> Output file should have `.ts` extension
|
|
148
152
|
> By default, it generates `likec4.generated.ts` in current directory
|
|
149
153
|
|
|
154
|
+
## Development
|
|
155
|
+
|
|
156
|
+
In root workspace:
|
|
157
|
+
|
|
158
|
+
```sh
|
|
159
|
+
yarn install
|
|
160
|
+
yarn build
|
|
161
|
+
|
|
162
|
+
cd packages/likec4
|
|
163
|
+
yarn dev
|
|
164
|
+
```
|
|
165
|
+
|
|
150
166
|
## Support
|
|
151
167
|
|
|
152
168
|
If there's a problem you're encountering or something you need help with, don't hesitate to take advantage of my [_Priority Support_ service](https://github.com/sponsors/davydkov) where you can ask me questions in an exclusive forum. I'm well equppied to assist you with this project and would be happy to help you out! 🙂
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { jsx } from "react/jsx-runtime";
|
|
1
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { nonexhaustive } from "@likec4/core";
|
|
3
3
|
import { useTransition } from "@react-spring/konva";
|
|
4
4
|
import { useRef } from "react";
|
|
5
5
|
import { AnimatedGroup } from "../konva.js";
|
|
6
6
|
import { Portal } from "../konva-portal.js";
|
|
7
|
+
import { ZoomInIcon } from "./icons/index.js";
|
|
7
8
|
import { CylinderShape, MobileShape, PersonShape, QueueShape, RectangleShape } from "./shapes/index.js";
|
|
8
9
|
import { BrowserShape } from "./shapes/Browser.js";
|
|
9
10
|
import { CompoundShape } from "./shapes/Compound.js";
|
|
@@ -137,9 +138,11 @@ function NodeSnape({
|
|
|
137
138
|
onNodeClick
|
|
138
139
|
}) {
|
|
139
140
|
const setHoveredNode = useSetHoveredNode();
|
|
140
|
-
const
|
|
141
|
+
const _isCompound = isCompound(node);
|
|
142
|
+
const isNavigatable = !!node.navigateTo && !!onNodeClick;
|
|
143
|
+
const Shape = nodeShape(node);
|
|
141
144
|
const springs = ctrl.springs;
|
|
142
|
-
return /* @__PURE__ */ jsx(Portal, { selector: ".top", enabled: isHovered && !
|
|
145
|
+
return /* @__PURE__ */ jsx(Portal, { selector: ".top", enabled: isHovered && !_isCompound, children: /* @__PURE__ */ jsxs(
|
|
143
146
|
AnimatedGroup,
|
|
144
147
|
{
|
|
145
148
|
name: node.id,
|
|
@@ -172,7 +175,32 @@ function NodeSnape({
|
|
|
172
175
|
scaleX: springs.scaleX,
|
|
173
176
|
scaleY: springs.scaleY,
|
|
174
177
|
opacity: springs.opacity,
|
|
175
|
-
children:
|
|
178
|
+
children: [
|
|
179
|
+
_isCompound && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
180
|
+
/* @__PURE__ */ jsx(
|
|
181
|
+
CompoundShape,
|
|
182
|
+
{
|
|
183
|
+
node,
|
|
184
|
+
theme,
|
|
185
|
+
springs,
|
|
186
|
+
labelOffsetX: isNavigatable ? -12 : 4
|
|
187
|
+
}
|
|
188
|
+
),
|
|
189
|
+
isNavigatable && /* @__PURE__ */ jsx(ZoomInIcon, { fill: "#BABABA", opacity: 0.9, size: 16, x: 16, y: 18 })
|
|
190
|
+
] }),
|
|
191
|
+
!_isCompound && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
192
|
+
/* @__PURE__ */ jsx(Shape, { node, theme, springs, isHovered }),
|
|
193
|
+
isNavigatable && /* @__PURE__ */ jsx(
|
|
194
|
+
ZoomInIcon,
|
|
195
|
+
{
|
|
196
|
+
fill: "#BABABA",
|
|
197
|
+
size: 16,
|
|
198
|
+
x: node.size.width / 2,
|
|
199
|
+
y: node.size.height - 20
|
|
200
|
+
}
|
|
201
|
+
)
|
|
202
|
+
] })
|
|
203
|
+
]
|
|
176
204
|
}
|
|
177
205
|
) });
|
|
178
206
|
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Path } from "../../konva.js";
|
|
3
|
+
export const ZoomInIcon = ({ fill, opacity = 1, size = 20, x, y }) => {
|
|
4
|
+
const originalSize = 15;
|
|
5
|
+
const scale = size / originalSize;
|
|
6
|
+
const offsetIcon = originalSize / 2;
|
|
7
|
+
return /* @__PURE__ */ jsx(
|
|
8
|
+
Path,
|
|
9
|
+
{
|
|
10
|
+
data: "M10 6.5C10 8.433 8.433 10 6.5 10C4.567 10 3 8.433 3 6.5C3 4.567 4.567 3 6.5 3C8.433 3 10 4.567 10 6.5ZM9.30884 10.0159C8.53901 10.6318 7.56251 11 6.5 11C4.01472 11 2 8.98528 2 6.5C2 4.01472 4.01472 2 6.5 2C8.98528 2 11 4.01472 11 6.5C11 7.56251 10.6318 8.53901 10.0159 9.30884L12.8536 12.1464C13.0488 12.3417 13.0488 12.6583 12.8536 12.8536C12.6583 13.0488 12.3417 13.0488 12.1464 12.8536L9.30884 10.0159ZM4.25 6.5C4.25 6.22386 4.47386 6 4.75 6H6V4.75C6 4.47386 6.22386 4.25 6.5 4.25C6.77614 4.25 7 4.47386 7 4.75V6H8.25C8.52614 6 8.75 6.22386 8.75 6.5C8.75 6.77614 8.52614 7 8.25 7H7V8.25C7 8.52614 6.77614 8.75 6.5 8.75C6.22386 8.75 6 8.52614 6 8.25V7H4.75C4.47386 7 4.25 6.77614 4.25 6.5Z",
|
|
11
|
+
fill,
|
|
12
|
+
fillRule: "evenodd",
|
|
13
|
+
strokeEnabled: false,
|
|
14
|
+
x,
|
|
15
|
+
y,
|
|
16
|
+
offsetX: offsetIcon,
|
|
17
|
+
offsetY: offsetIcon,
|
|
18
|
+
scaleX: scale,
|
|
19
|
+
scaleY: scale,
|
|
20
|
+
width: originalSize,
|
|
21
|
+
height: originalSize,
|
|
22
|
+
opacity,
|
|
23
|
+
globalCompositeOperation: "luminosity",
|
|
24
|
+
hitStrokeWidth: 5
|
|
25
|
+
}
|
|
26
|
+
);
|
|
27
|
+
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { AnimatedRect, AnimatedText } from "../../konva.js";
|
|
3
|
-
export function CompoundShape({ node, theme, springs }) {
|
|
3
|
+
export function CompoundShape({ node, theme, springs, labelOffsetX = 4 }) {
|
|
4
4
|
const { labels } = node;
|
|
5
5
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
6
6
|
/* @__PURE__ */ jsx(
|
|
@@ -24,9 +24,9 @@ export function CompoundShape({ node, theme, springs }) {
|
|
|
24
24
|
AnimatedText,
|
|
25
25
|
{
|
|
26
26
|
x,
|
|
27
|
-
y: y -
|
|
28
|
-
offsetX:
|
|
29
|
-
offsetY:
|
|
27
|
+
y: y - 4,
|
|
28
|
+
offsetX: labelOffsetX,
|
|
29
|
+
offsetY: label.fontSize / 2,
|
|
30
30
|
width: springs.width.to((v) => v - x - 4),
|
|
31
31
|
fill: "#BABABA",
|
|
32
32
|
fontFamily: theme.font,
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { CaretDownIcon } from '@radix-ui/react-icons';
|
|
2
|
+
import { Button, DropdownMenu, Flex, IconButton, Separator } from '@radix-ui/themes';
|
|
3
|
+
import { useState } from 'react';
|
|
4
|
+
import { keys } from 'remeda';
|
|
5
|
+
const Mode = {
|
|
6
|
+
react: 'React',
|
|
7
|
+
dot: 'Graphviz',
|
|
8
|
+
mmd: 'Mermaid',
|
|
9
|
+
d2: 'D2'
|
|
10
|
+
};
|
|
11
|
+
const mode_keys = keys.strict(Mode);
|
|
12
|
+
export const DisplayModeSelector = () => {
|
|
13
|
+
const [current, setCurrent] = useState('react');
|
|
14
|
+
const [[first, second, ...rest], setModes] = useState(mode_keys);
|
|
15
|
+
const changeMode = (mode) => () => {
|
|
16
|
+
if (mode === current) {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
setCurrent(mode);
|
|
20
|
+
if (mode === first || mode === second) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
// change only second
|
|
24
|
+
setModes(([first, ...rest]) => [first, mode, ...rest.filter(m => m !== mode)]);
|
|
25
|
+
};
|
|
26
|
+
return (<Flex display={{
|
|
27
|
+
initial: 'none',
|
|
28
|
+
md: 'flex'
|
|
29
|
+
}} gap='3' align='center'>
|
|
30
|
+
<Button variant={current === first ? 'solid' : 'ghost'} size='1' onClick={changeMode(first)}>
|
|
31
|
+
{Mode[first]}
|
|
32
|
+
</Button>
|
|
33
|
+
<Button variant={current === second ? 'solid' : 'ghost'} size='1' onClick={changeMode(second)}>
|
|
34
|
+
{Mode[second]}
|
|
35
|
+
</Button>
|
|
36
|
+
<DropdownMenu.Root>
|
|
37
|
+
<DropdownMenu.Trigger>
|
|
38
|
+
<IconButton variant='ghost' size='1'>
|
|
39
|
+
<CaretDownIcon />
|
|
40
|
+
</IconButton>
|
|
41
|
+
</DropdownMenu.Trigger>
|
|
42
|
+
<DropdownMenu.Content>
|
|
43
|
+
{rest.map(mode => (<DropdownMenu.Item key={mode} onClick={changeMode(mode)}>
|
|
44
|
+
{Mode[mode]}
|
|
45
|
+
</DropdownMenu.Item>))}
|
|
46
|
+
</DropdownMenu.Content>
|
|
47
|
+
</DropdownMenu.Root>
|
|
48
|
+
<Separator orientation='vertical'/>
|
|
49
|
+
</Flex>);
|
|
50
|
+
};
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { CaretDownIcon, Share1Icon as ShareIcon } from '@radix-ui/react-icons';
|
|
2
|
-
import { Button, Dialog, DropdownMenu, Flex,
|
|
2
|
+
import { Button, Dialog, DropdownMenu, Flex, Text } from '@radix-ui/themes';
|
|
3
3
|
import { useState } from 'react';
|
|
4
4
|
// import { ThemePanelToggle } from '../ThemePanelToggle'
|
|
5
|
+
import { DisplayModeSelector } from './DisplayModeSelector';
|
|
5
6
|
import ExportDiagram from './ExportDiagram';
|
|
6
7
|
import { ShareDialog } from './ShareDialog';
|
|
7
8
|
const ExportMenu = ({ onExport, children }) => (<DropdownMenu.Root>
|
|
@@ -13,29 +14,14 @@ const ExportMenu = ({ onExport, children }) => (<DropdownMenu.Root>
|
|
|
13
14
|
<DropdownMenu.Group>
|
|
14
15
|
<DropdownMenu.Item onClick={_ => {
|
|
15
16
|
onExport('png');
|
|
16
|
-
// const { boundingBox } = diagramApi.diagramView()
|
|
17
|
-
// console.log('Serialized: ', diagramApi.stage.toObject())
|
|
18
|
-
// const k = new KonvaCore.Canvas({
|
|
19
|
-
// height: diagramApi.
|
|
20
|
-
// })
|
|
21
|
-
// diagramApi.stage().toBlob({
|
|
22
|
-
// ...boundingBox,
|
|
23
|
-
// callback(blob) {
|
|
24
|
-
// const url = URL.createObjectURL(blob)
|
|
25
|
-
// window.open(url)
|
|
26
|
-
// // const a = document.createElement('a')
|
|
27
|
-
// // a.href = url
|
|
28
|
-
// // a.download = 'diagram.png'
|
|
29
|
-
// // a.click()
|
|
30
|
-
// URL.revokeObjectURL(url)
|
|
31
|
-
// },
|
|
32
|
-
// })
|
|
33
17
|
}}>
|
|
34
18
|
Export as .png
|
|
35
19
|
</DropdownMenu.Item>
|
|
36
20
|
<DropdownMenu.Item disabled>Export as .dot</DropdownMenu.Item>
|
|
37
21
|
<DropdownMenu.Item disabled>Export as .mmd</DropdownMenu.Item>
|
|
38
22
|
<DropdownMenu.Item disabled>Export as .d2</DropdownMenu.Item>
|
|
23
|
+
<DropdownMenu.Item disabled>Export to Draw.io</DropdownMenu.Item>
|
|
24
|
+
<DropdownMenu.Item disabled>Export to Miro</DropdownMenu.Item>
|
|
39
25
|
</DropdownMenu.Group>
|
|
40
26
|
<DropdownMenu.Separator />
|
|
41
27
|
<DropdownMenu.Label>
|
|
@@ -49,24 +35,7 @@ const ExportMenu = ({ onExport, children }) => (<DropdownMenu.Root>
|
|
|
49
35
|
export const ViewActionsToolbar = ({ diagram }) => {
|
|
50
36
|
const [exportTo, setExportTo] = useState(null);
|
|
51
37
|
return (<Flex position='fixed' top='0' right='0' p='2' gap={'3'} justify='end' align='center'>
|
|
52
|
-
<
|
|
53
|
-
initial: 'none',
|
|
54
|
-
md: 'flex'
|
|
55
|
-
}} gap='3' align='center'>
|
|
56
|
-
<Button variant='solid' size='1'>
|
|
57
|
-
React
|
|
58
|
-
</Button>
|
|
59
|
-
<Button variant='ghost' size='1'>
|
|
60
|
-
Graphviz
|
|
61
|
-
</Button>
|
|
62
|
-
<Button variant='ghost' size='1'>
|
|
63
|
-
Mermaid
|
|
64
|
-
</Button>
|
|
65
|
-
<IconButton variant='ghost' size='1'>
|
|
66
|
-
<CaretDownIcon />
|
|
67
|
-
</IconButton>
|
|
68
|
-
<Separator orientation='vertical'/>
|
|
69
|
-
</Flex>
|
|
38
|
+
<DisplayModeSelector />
|
|
70
39
|
<Dialog.Root>
|
|
71
40
|
<Dialog.Trigger>
|
|
72
41
|
<Button variant='solid'>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "likec4",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.44.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"homepage": "https://likec4.dev",
|
|
6
6
|
"author": "Denis Davydkov <denis@davydkov.com>",
|
|
@@ -79,11 +79,11 @@
|
|
|
79
79
|
"vite": "^4.5.0"
|
|
80
80
|
},
|
|
81
81
|
"devDependencies": {
|
|
82
|
-
"@likec4/core": "0.
|
|
83
|
-
"@likec4/diagrams": "0.
|
|
84
|
-
"@likec4/generators": "0.
|
|
85
|
-
"@likec4/language-server": "0.
|
|
86
|
-
"@likec4/layouts": "0.
|
|
82
|
+
"@likec4/core": "0.44.0",
|
|
83
|
+
"@likec4/diagrams": "0.44.0",
|
|
84
|
+
"@likec4/generators": "0.44.0",
|
|
85
|
+
"@likec4/language-server": "0.44.0",
|
|
86
|
+
"@likec4/layouts": "0.44.0",
|
|
87
87
|
"@types/node": "^20.8.7",
|
|
88
88
|
"@types/prop-types": "^15.7.9",
|
|
89
89
|
"@types/react": "^18.2.0",
|