likec4 0.37.1 → 0.40.0-build.1

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 (35) hide show
  1. package/app/favicon.svg +6 -0
  2. package/app/index.html +14 -0
  3. package/app/likec4.css +85 -0
  4. package/app/postcss.config.cjs +11 -0
  5. package/app/robots.txt +2 -0
  6. package/app/src/App.tsx +42 -0
  7. package/app/src/components/DiagramNotFound.tsx +30 -0
  8. package/app/src/components/ThemePanelToggle.tsx +15 -0
  9. package/app/src/components/index.ts +4 -0
  10. package/app/src/components/sidebar/DiagramsTree.module.css +83 -0
  11. package/app/src/components/sidebar/DiagramsTree.tsx +77 -0
  12. package/app/src/components/sidebar/Sidebar.tsx +67 -0
  13. package/app/src/components/sidebar/styles.module.css +85 -0
  14. package/app/src/components/view-page/ShareDialog.tsx +148 -0
  15. package/app/src/components/view-page/ViewActionsToolbar.tsx +76 -0
  16. package/app/src/data/atoms.ts +108 -0
  17. package/app/src/data/hooks.ts +16 -0
  18. package/app/src/data/index.ts +1 -0
  19. package/app/src/data/likec4.d.ts +5 -0
  20. package/app/src/data/sidebar-diagram-tree.ts +52 -0
  21. package/app/src/main.tsx +12 -0
  22. package/app/src/pages/export.module.css +4 -0
  23. package/app/src/pages/export.page.tsx +37 -0
  24. package/app/src/pages/index.module.css +11 -0
  25. package/app/src/pages/index.page.tsx +103 -0
  26. package/app/src/pages/index.ts +3 -0
  27. package/app/src/pages/view.page.tsx +67 -0
  28. package/app/src/router.ts +67 -0
  29. package/app/src/utils/index.ts +1 -0
  30. package/app/src/utils/utils.ts +6 -0
  31. package/app/tailwind.config.cjs +19 -0
  32. package/app/tsconfig.json +41 -0
  33. package/bin/likec4.mjs +3 -0
  34. package/dist/cli/index.js +348 -0
  35. package/package.json +66 -9
@@ -0,0 +1,6 @@
1
+ <svg width="256" height="256" viewBox="0 0 256 256" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path d="M152.266 152.674V14H29.2171C24.1477 14 20 18.3487 20 23.6637V153.158H152.266V152.674Z" fill="#5E98AF"/>
3
+ <path d="M170.237 152.673H236.6V92.7575C236.6 87.4424 232.452 83.0938 227.383 83.0938H170.237V152.673Z" fill="#5E98AF"/>
4
+ <path d="M152.267 171.515H86.3647V231.43C86.3647 236.745 90.5125 241.094 95.5819 241.094H152.728V171.515H152.267Z" fill="#5E98AF"/>
5
+ <path d="M170.237 171.515V241.094H227.383C232.452 241.094 236.6 236.745 236.6 231.43V171.515H170.237Z" fill="#5E98AF"/>
6
+ </svg>
package/app/index.html ADDED
@@ -0,0 +1,14 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en-US" class="dark">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="wwidth=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, shrink-to-fit=no" />
6
+ <link rel="icon" type="image/svg+xml" href="/favicon.svg">
7
+ <title>LikeC4</title>
8
+ <link rel="stylesheet" type="text/css" href="/likec4.css" />
9
+ </head>
10
+ <body>
11
+ <div id="like4-root" class="w-screen h-screen m-0 p-0"></div>
12
+ <script type="module" src="/src/main.tsx"></script>
13
+ </body>
14
+ </html>
package/app/likec4.css ADDED
@@ -0,0 +1,85 @@
1
+ @import '@radix-ui/themes/styles.css';
2
+
3
+ @tailwind base;
4
+ @tailwind components;
5
+ @tailwind utilities;
6
+
7
+ /* @layer base {
8
+
9
+ :root {
10
+ --background: 0 0% 100%;
11
+ --foreground: 222.2 84% 4.9%;
12
+
13
+ --card: 0 0% 100%;
14
+ --card-foreground: 222.2 84% 4.9%;
15
+
16
+ --popover: 0 0% 100%;
17
+ --popover-foreground: 222.2 84% 4.9%;
18
+
19
+ --primary: 222.2 47.4% 11.2%;
20
+ --primary-foreground: 210 40% 98%;
21
+
22
+ --secondary: 210 40% 96.1%;
23
+ --secondary-foreground: 222.2 47.4% 11.2%;
24
+ yarn add @fontsource-variable/noto-sans-tc
25
+ --muted: 210 40% 96.1%;
26
+ --muted-foreground: 215.4 16.3% 46.9%;
27
+
28
+ --accent: 210 40% 96.1%;
29
+ --accent-foreground: 222.2 47.4% 11.2%;
30
+
31
+ --destructive: 0 84.2% 60.2%;
32
+ --destructive-foreground: 210 40% 98%;
33
+
34
+ --border: 214.3 31.8% 91.4%;
35
+ --input: 214.3 31.8% 91.4%;
36
+ --ring: 222.2 84% 4.9%;
37
+
38
+ --radius: 0.5rem;
39
+ }
40
+
41
+ .dark {
42
+ --background: 222.2 84% 4.9%;
43
+ --foreground: 210 40% 98%;
44
+
45
+ --card: 222.2 84% 4.9%;
46
+ --card-foreground: 210 40% 98%;
47
+
48
+ --popover: 222.2 84% 4.9%;
49
+ --popover-foreground: 210 40% 98%;
50
+
51
+ --primary: 210 40% 98%;
52
+ --primary-foreground: 222.2 47.4% 11.2%;
53
+
54
+ --secondary: 217.2 32.6% 17.5%;
55
+ --secondary-foreground: 210 40% 98%;
56
+
57
+ --muted: 217.2 32.6% 17.5%;
58
+ --muted-foreground: 215 20.2% 65.1%;
59
+
60
+ --accent: 217.2 32.6% 17.5%;
61
+ --accent-foreground: 210 40% 98%;
62
+
63
+ --destructive: 0 62.8% 30.6%;
64
+ --destructive-foreground: 210 40% 98%;
65
+
66
+ --border: 217.2 32.6% 17.5%;
67
+ --input: 217.2 32.6% 17.5%;
68
+ --ring: 212.7 26.8% 83.9%;
69
+
70
+ }
71
+
72
+ } */
73
+
74
+ .radix-themes {
75
+ /* --default-font-family: 'Rubik Variable',-apple-system,BlinkMacSystemFont,'Segoe UI',Helvetica,Arial,sans-serif,'Apple Color Emoji','Segoe UI Emoji'; */
76
+ --font-weight-light: 200;
77
+ --font-weight-regular: 400;
78
+ --font-weight-medium: 500;
79
+ --font-weight-bold: 600;
80
+ }
81
+
82
+ html, body {
83
+ margin: 0;
84
+ padding: 0;
85
+ }
@@ -0,0 +1,11 @@
1
+ const autoprefixer = require('autoprefixer');
2
+ const nesting = require('tailwindcss/nesting/index.js');
3
+ const tailwindcss = require('tailwindcss');
4
+ const { resolve } = require('path');
5
+
6
+ const tailwindCfg = resolve(__dirname, './tailwind.config.cjs');
7
+
8
+ /* @type {import('postcss').Postcss} */
9
+ module.exports = {
10
+ plugins: [nesting, tailwindcss(tailwindCfg), autoprefixer],
11
+ };
package/app/robots.txt ADDED
@@ -0,0 +1,2 @@
1
+ User-agent: *
2
+ Disallow: /
@@ -0,0 +1,42 @@
1
+ import { Provider } from 'jotai'
2
+ import { useAtomsDevtools } from 'jotai-devtools'
3
+ import type { PropsWithChildren } from 'react'
4
+ import { Fragment } from 'react'
5
+ import { Sidebar } from './components'
6
+ import { ExportPage, IndexPage, ViewPage } from './pages'
7
+ import { useRoute } from './router'
8
+
9
+ const Routes = () => {
10
+ const r = useRoute()
11
+ return (
12
+ <>
13
+ {r.route === 'index' && <IndexPage key='index' />}
14
+ {r.route === 'view' && <ViewPage key='view' viewId={r.params.viewId} showUI={r.showUI} />}
15
+ {r.route === 'export' && (
16
+ <ExportPage key='export' viewId={r.params.viewId} padding={r.params.padding} />
17
+ )}
18
+ {r.showUI && (
19
+ <Fragment key='ui'>
20
+ <Sidebar />
21
+ </Fragment>
22
+ )}
23
+ </>
24
+ )
25
+ }
26
+
27
+ const AtomsDevTools = import.meta.env.DEV
28
+ ? ({ children }: PropsWithChildren) => {
29
+ useAtomsDevtools('demo')
30
+ return <>{children}</>
31
+ }
32
+ : Fragment
33
+
34
+ export default function App() {
35
+ return (
36
+ <Provider>
37
+ <AtomsDevTools>
38
+ <Routes />
39
+ </AtomsDevTools>
40
+ </Provider>
41
+ )
42
+ }
@@ -0,0 +1,30 @@
1
+ import { ExclamationTriangleIcon } from '@radix-ui/react-icons'
2
+ import { Box, Button, Card, Flex, Heading, IconButton, Text } from '@radix-ui/themes'
3
+ import { $pages } from '../router'
4
+
5
+ export const DiagramNotFound = () => {
6
+ return (
7
+ <Flex position='fixed' inset='0' align='center' justify='center'>
8
+ <Card color='red' size='3'>
9
+ <Flex gap='4' direction='row' align='start'>
10
+ <Box grow='0' shrink='0' pt='1'>
11
+ <IconButton variant='ghost' color='amber'>
12
+ <ExclamationTriangleIcon width={50} height={50} />
13
+ </IconButton>
14
+ </Box>
15
+ <Flex gap='3' direction='column'>
16
+ <Heading trim='both' color='amber' size='4'>
17
+ Diagram not found
18
+ </Heading>
19
+ <Text as='div'>The diagram you are looking for does not exist.</Text>
20
+ <Box>
21
+ <Button variant='soft' color='amber' onClick={() => $pages.index.open()}>
22
+ Home page
23
+ </Button>
24
+ </Box>
25
+ </Flex>
26
+ </Flex>
27
+ </Card>
28
+ </Flex>
29
+ )
30
+ }
@@ -0,0 +1,15 @@
1
+ import { MoonIcon } from '@radix-ui/react-icons'
2
+ import { IconButton, ThemePanel } from '@radix-ui/themes'
3
+ import { useToggle } from '@react-hookz/web/esm'
4
+
5
+ export const ThemePanelToggle = () => {
6
+ const [isOpened, toggle] = useToggle(false, true)
7
+ return (
8
+ <>
9
+ <IconButton color='gray' variant={isOpened ? 'solid' : 'soft'} onClick={toggle} size={'2'}>
10
+ <MoonIcon width={16} height={16} />
11
+ </IconButton>
12
+ {isOpened && <ThemePanel style={{ top: 50 }} />}
13
+ </>
14
+ )
15
+ }
@@ -0,0 +1,4 @@
1
+ export { Sidebar } from './sidebar/Sidebar'
2
+ export { ViewActionsToolbar } from './view-page/ViewActionsToolbar'
3
+ export { ThemePanelToggle } from './ThemePanelToggle'
4
+ export { DiagramNotFound } from './DiagramNotFound'
@@ -0,0 +1,83 @@
1
+ .treeview {
2
+ min-width: 200px;
3
+ max-width: 300px;
4
+
5
+ ul {
6
+ list-style: none;
7
+ padding: 0;
8
+ margin: 0;
9
+ display: flex;
10
+ flex-direction: column;
11
+ gap: var(--space-1);
12
+ }
13
+
14
+ :global(.tree-node-group--expanded) {
15
+ margin-top: var(--space-1);
16
+ padding-left: var(--space-5);
17
+
18
+ &:has(> :global(.tree-branch-wrapper)) {
19
+ padding-left: var(--space-3);
20
+ }
21
+ }
22
+
23
+ /* :global(.tree-branch-wrapper):where([aria-expanded='true']) > :global(.tree-branch-wrapper) {
24
+ margin-top: var(--space-1);
25
+ margin-left: var(--space-2);
26
+ } */
27
+
28
+ :global(.tree-leaf-list-item) {
29
+ /* margin-left: var(--space-2); */
30
+
31
+ & > [role='treeitem'] {
32
+ user-select: none;
33
+ cursor: pointer;
34
+ /* color: var(--gray-12); */
35
+ padding: calc(var(--space-1) * 0.75);
36
+ padding-left: var(--space-2);
37
+ padding-right: var(--space-2);
38
+ margin-top: calc(var(--space-1) * -0.15);
39
+ margin-bottom: calc(var(--space-1) * -0.15);
40
+ /* margin-left: calc(var(--space-1) * -2); */
41
+ /* margin-right: 0; */
42
+ transition-property: background-color;
43
+ transition-timing-function: cubic-bezier(0, 0.31, 0, 1.03);
44
+ transition-duration: 140ms;
45
+
46
+ border-radius: max(var(--radius-3), var(--radius-full));
47
+
48
+ svg {
49
+ color: var(--gray-a10);
50
+ }
51
+
52
+ &:hover:where(:not([aria-selected='true'])) {
53
+ background-color: var(--gray-a6);
54
+ }
55
+
56
+ &:where([aria-selected='true']) {
57
+ background-color: var(--accent-a7);
58
+ }
59
+ }
60
+ }
61
+ :global(.tree-branch-wrapper) {
62
+ & > :global(.tree-node__branch) {
63
+ cursor: pointer;
64
+ padding-top: calc(var(--space-1) * 0.75);
65
+ padding-bottom: calc(var(--space-1) * 0.75);
66
+
67
+ transition-property: background-color;
68
+ transition-timing-function: cubic-bezier(0, 0.31, 0, 1.03);
69
+ transition-duration: 120ms;
70
+
71
+ border-radius: max(var(--radius-3), var(--radius-full));
72
+
73
+ &:hover {
74
+ background-color: var(--gray-a5);
75
+ }
76
+ }
77
+ }
78
+
79
+ :global(.tree-branch-wrapper) ~ :global(.tree-leaf-list-item) {
80
+ margin-left: var(--space-3);
81
+ }
82
+
83
+ }
@@ -0,0 +1,77 @@
1
+ import { DashboardIcon, TriangleRightIcon } from '@radix-ui/react-icons'
2
+ import { Box, Flex, Text } from '@radix-ui/themes'
3
+ import TreeView, { type INode } from 'react-accessible-treeview'
4
+ import { useDiagramsTree } from '../../data'
5
+ import { $pages, useRoute } from '../../router'
6
+ import { cn } from '../../utils'
7
+ import styles from './DiagramsTree.module.css'
8
+
9
+ function inTree(id: string, data: INode[]): boolean {
10
+ return data.some(d => d.id === id)
11
+ }
12
+
13
+ export function DiagramsTree() {
14
+ const data = useDiagramsTree()
15
+ const r = useRoute()
16
+
17
+ const viewId = r.route === 'view' || r.route === 'export' ? r.params.viewId : null
18
+ const selectedId = viewId && inTree(viewId, data) ? [viewId] : []
19
+
20
+ return (
21
+ <Box className={styles.treeview}>
22
+ <TreeView
23
+ data={data}
24
+ propagateSelect
25
+ propagateSelectUpwards
26
+ selectedIds={selectedId}
27
+ onNodeSelect={({ element, isBranch }) => {
28
+ if (isBranch) {
29
+ return
30
+ }
31
+ $pages.view.open('' + element.id)
32
+ }}
33
+ nodeRenderer={({
34
+ element,
35
+ isBranch,
36
+ isExpanded,
37
+ getNodeProps,
38
+ handleExpand,
39
+ handleSelect
40
+ }) => {
41
+ return (
42
+ <Flex
43
+ {...getNodeProps({ onClick: isBranch ? handleExpand : handleSelect })}
44
+ align={'center'}
45
+ gap={isBranch ? '1' : '2'}
46
+ >
47
+ {isBranch && (
48
+ <Box style={{ lineHeight: '15px' }}>
49
+ <TriangleRightIcon
50
+ width={15}
51
+ height={15}
52
+ className={cn('transition duration-200 ease-out', isExpanded && 'rotate-90')}
53
+ />
54
+ </Box>
55
+ )}
56
+ {!isBranch && (
57
+ <Box style={{ lineHeight: '14px' }} width={'min-content'}>
58
+ <DashboardIcon width={14} height={14} />
59
+ </Box>
60
+ )}
61
+ <Box asChild grow={'1'}>
62
+ <Text
63
+ as='div'
64
+ size={'2'}
65
+ weight={isBranch ? 'bold' : undefined}
66
+ className='truncate'
67
+ >
68
+ {(isBranch ? '🗂️ ' : '') + element.name}
69
+ </Text>
70
+ </Box>
71
+ </Flex>
72
+ )
73
+ }}
74
+ />
75
+ </Box>
76
+ )
77
+ }
@@ -0,0 +1,67 @@
1
+ import { Box, Button, Flex, IconButton, ScrollArea, Separator } from '@radix-ui/themes'
2
+ import { useClickOutside, useToggle } from '@react-hookz/web/esm'
3
+
4
+ import { HamburgerMenuIcon, ArrowLeftIcon } from '@radix-ui/react-icons'
5
+ import { useRef } from 'react'
6
+ import { cn } from '~/utils'
7
+
8
+ import { DiagramsTree } from './DiagramsTree'
9
+ import styles from './styles.module.css'
10
+ import { $pages } from '../../router'
11
+
12
+ export const Sidebar = () => {
13
+ const ref = useRef<HTMLDivElement>(null)
14
+ const [isOpened, toggle] = useToggle(false, true)
15
+
16
+ useClickOutside(ref, () => isOpened && toggle())
17
+
18
+ return (
19
+ <>
20
+ <Flex
21
+ position='fixed'
22
+ left='0'
23
+ p={'2'}
24
+ className={cn(
25
+ styles.trigger,
26
+ 'inset-y-0 cursor-pointer items-start',
27
+ isOpened && 'display-none'
28
+ )}
29
+ onClick={toggle}
30
+ >
31
+ <IconButton size='2' color='gray' variant='soft'>
32
+ <HamburgerMenuIcon width={22} height={22} />
33
+ </IconButton>
34
+ </Flex>
35
+ <Flex
36
+ ref={ref}
37
+ className={styles.navsidebar}
38
+ position='fixed'
39
+ left='0'
40
+ top='0'
41
+ bottom='0'
42
+ data-opened={isOpened}
43
+ >
44
+ <ScrollArea scrollbars='vertical' type='scroll'>
45
+ <Box p='4' pl='2'>
46
+ <Button
47
+ variant='ghost'
48
+ ml='2'
49
+ mt='1'
50
+ size='1'
51
+ color='gray'
52
+ onClick={_ => {
53
+ toggle()
54
+ $pages.index.open()
55
+ }}
56
+ >
57
+ <ArrowLeftIcon />
58
+ Back to dashboard
59
+ </Button>
60
+ <Separator orientation='horizontal' my='3' size={'4'} />
61
+ <DiagramsTree />
62
+ </Box>
63
+ </ScrollArea>
64
+ </Flex>
65
+ </>
66
+ )
67
+ }
@@ -0,0 +1,85 @@
1
+ .trigger {
2
+
3
+ cursor: pointer;
4
+
5
+ &::before {
6
+ transition-property: all;
7
+ transition-timing-function: cubic-bezier(0, 0.31, 0, 1.03);
8
+ transition-duration: 140ms;
9
+
10
+ position: absolute;
11
+ content: '';
12
+ inset: 0;
13
+ background: var(--gray-7);
14
+ opacity: 0;
15
+ z-index: 1;
16
+ /* visibility: hidden; */
17
+ }
18
+
19
+ &:hover::before {
20
+ visibility: visible;
21
+ opacity: 0.7;
22
+ }
23
+
24
+ & > * {
25
+ position: relative;
26
+ z-index: 2;
27
+ }
28
+ }
29
+
30
+ .navsidebar {
31
+ backdrop-filter: blur(6px);
32
+
33
+ &::before {
34
+ transition: all 0.26s ease-in-out;
35
+ position: absolute;
36
+ content: '';
37
+ inset: 0;
38
+ background: var(--gray-7);
39
+ opacity: 0.7;
40
+ z-index: 1;
41
+ }
42
+
43
+ & > div {
44
+ position: relative;
45
+ z-index: 2;
46
+ }
47
+
48
+ transition: transform 0.21s cubic-bezier(0.4, 0, 0.2, 1);
49
+ transform: translateX(-100%);
50
+
51
+ &[data-opened='true'] {
52
+ transform: translateX(0);
53
+ }
54
+ }
55
+
56
+ :global(.rt-variant-soft),
57
+ :global(.rt-variant-solid) {
58
+ &.navitem {
59
+ @apply transition-colors;
60
+ /* color: var(--accent-10); */
61
+ /* background-color: transparent; */
62
+ justify-content: space-between;
63
+ align-items: center;
64
+ cursor: pointer;
65
+
66
+ /*
67
+ &:hover, &[data-current="true"] {
68
+ color: var(--accent-11);
69
+ background-color: var(--accent-a3);
70
+ } */
71
+
72
+ .icon {
73
+ visibility: hidden;
74
+ transform: translateX(-25%);
75
+ opacity: 0.7;
76
+ }
77
+
78
+ &:hover .icon {
79
+ visibility: visible;
80
+ transition: all 0.15s ease-out;
81
+ transform: translateX(0);
82
+ opacity: 1;
83
+ }
84
+ }
85
+ }
@@ -0,0 +1,148 @@
1
+ import type { DiagramView } from '@likec4/diagrams'
2
+ import {
3
+ ExclamationTriangleIcon,
4
+ InfoCircledIcon,
5
+ OpenInNewWindowIcon
6
+ } from '@radix-ui/react-icons'
7
+ import {
8
+ Box,
9
+ Button,
10
+ Callout,
11
+ Code,
12
+ Dialog,
13
+ Flex,
14
+ Link,
15
+ Select,
16
+ Tabs,
17
+ Text
18
+ } from '@radix-ui/themes'
19
+ import { useState } from 'react'
20
+
21
+ const embedCode = (diagram: DiagramView, theme: string) => {
22
+ const padding = 20
23
+ const params = new URLSearchParams()
24
+ params.set('embed', diagram.id)
25
+ params.set('padding', `${padding}`)
26
+ if (theme !== 'system') {
27
+ params.set('theme', theme)
28
+ }
29
+
30
+ const width = diagram.width + padding * 2
31
+ const height = diagram.height + padding * 2
32
+
33
+ const url = new URL(window.location.href)
34
+ url.search = params.toString()
35
+ const iframe = `<iframe src="${url.href}" width="100%" height="100%" style="border:0;background:transparent;"></iframe>`
36
+
37
+ const code = `
38
+ <div style="aspect-ratio:${width}/${height};max-width:${width}px;width:100%;height:auto;padding:0;margin-left:auto;margin-right:auto">
39
+ ${iframe}
40
+ </div>`.trim()
41
+
42
+ return {
43
+ code,
44
+ href: url.href
45
+ }
46
+ }
47
+
48
+ export const ShareDialog = ({ diagram }: { diagram: DiagramView }) => {
49
+ const [theme, setTheme] = useState('system')
50
+
51
+ const { code, href } = embedCode(diagram, theme)
52
+
53
+ return (
54
+ <Dialog.Content size={'2'} style={{ maxWidth: 700, minWidth: 300 }}>
55
+ <Tabs.Root defaultValue='embed'>
56
+ <Tabs.List>
57
+ <Tabs.Trigger value='embed'>Embed</Tabs.Trigger>
58
+ <Tabs.Trigger value='script'>Script</Tabs.Trigger>
59
+ <Tabs.Trigger value='public'>Public URL</Tabs.Trigger>
60
+ </Tabs.List>
61
+
62
+ <Box px='1' py='4'>
63
+ <Tabs.Content value='embed'>
64
+ <Flex direction='column' gap='4'>
65
+ {code.includes('http://localhost') && (
66
+ <Callout.Root size='1' color='amber'>
67
+ <Callout.Icon>
68
+ <ExclamationTriangleIcon />
69
+ </Callout.Icon>
70
+ <Callout.Text>
71
+ This is a local URL. You need to build and deploy your diagrams to a public URL
72
+ to make it available for embedding.
73
+ </Callout.Text>
74
+ </Callout.Root>
75
+ )}
76
+ <label>
77
+ <Flex direction='row' justify='between'>
78
+ <Text as='div' size='2' weight='medium'>
79
+ Code
80
+ </Text>
81
+ <Flex asChild display='inline-flex' gap='1' align='center'>
82
+ <Link size='2' href={href} target='_blank'>
83
+ <Text as='span'>Open in new tab</Text>
84
+ <Text as='span'>
85
+ <OpenInNewWindowIcon width={12} height={12} />
86
+ </Text>
87
+ </Link>
88
+ </Flex>
89
+ </Flex>
90
+ <Box
91
+ asChild
92
+ display={'block'}
93
+ my='2'
94
+ p='2'
95
+ className='whitespace-pre-wrap overflow-scroll select-all'
96
+ >
97
+ <Code variant='soft' autoFocus>
98
+ {code}
99
+ </Code>
100
+ </Box>
101
+ </label>
102
+ <Text as='div' size='2' color='gray' trim={'start'}>
103
+ Embeded view is an iframe with a static diagram
104
+ </Text>
105
+ <label>
106
+ <Text as='div' size='2' weight='medium' mb='1'>
107
+ Theme
108
+ </Text>
109
+ <Select.Root size='2' defaultValue={theme} onValueChange={v => setTheme(v)}>
110
+ <Select.Trigger variant='soft' />
111
+ <Select.Content>
112
+ <Select.Item value='system'>Same as system</Select.Item>
113
+ <Select.Item value='light'>Light</Select.Item>
114
+ <Select.Item value='dark'>Dark</Select.Item>
115
+ </Select.Content>
116
+ </Select.Root>
117
+ </label>
118
+ </Flex>
119
+ </Tabs.Content>
120
+
121
+ <Tabs.Content value='public'>
122
+ <Callout.Root color='amber'>
123
+ <Callout.Icon>
124
+ <InfoCircledIcon />
125
+ </Callout.Icon>
126
+ <Callout.Text>This feature is not implemented yet.</Callout.Text>
127
+ </Callout.Root>
128
+ </Tabs.Content>
129
+ <Tabs.Content value='script'>
130
+ <Callout.Root color='amber'>
131
+ <Callout.Icon>
132
+ <InfoCircledIcon />
133
+ </Callout.Icon>
134
+ <Callout.Text>This feature is not implemented yet.</Callout.Text>
135
+ </Callout.Root>
136
+ </Tabs.Content>
137
+ </Box>
138
+ </Tabs.Root>
139
+ <Flex gap='3' mt='1' justify='end'>
140
+ <Dialog.Close>
141
+ <Button variant='soft' color='gray'>
142
+ Close
143
+ </Button>
144
+ </Dialog.Close>
145
+ </Flex>
146
+ </Dialog.Content>
147
+ )
148
+ }