likec4 1.48.0 → 1.49.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 (113) hide show
  1. package/README.md +11 -1
  2. package/__app__/src/likec4.js +5595 -4127
  3. package/__app__/src/routes/index.js +3 -153
  4. package/__app__/src/routes/projects.js +21 -1087
  5. package/__app__/src/routes/single.js +1221 -16
  6. package/__app__/src/style.css +1 -1
  7. package/__app__/src/vendors.js +3507 -894
  8. package/__app__/src/webcomponent.js +1 -1
  9. package/config/schema.json +155 -143
  10. package/dist/THIRD-PARTY-LICENSES.md +1739 -0
  11. package/dist/_chunks/GraphvizBinaryAdapter.mjs +72 -0
  12. package/dist/_chunks/filenames.mjs +14 -0
  13. package/dist/_chunks/index.d.mts +224 -129
  14. package/dist/_chunks/index2.d.mts +2033 -1908
  15. package/dist/_chunks/libs/@chevrotain/gast.mjs +1 -3969
  16. package/dist/_chunks/libs/@chevrotain/regexp-to-ast.mjs +9 -785
  17. package/dist/_chunks/libs/@chevrotain/utils.mjs +1 -37
  18. package/dist/_chunks/libs/@hono/mcp.mjs +34 -0
  19. package/dist/_chunks/libs/@hono/node-server.mjs +1 -436
  20. package/dist/_chunks/libs/@logtape/logtape.d.mts +741 -0
  21. package/dist/_chunks/libs/@logtape/logtape.mjs +6 -1354
  22. package/dist/_chunks/libs/@lume/kiwi.mjs +1 -1355
  23. package/dist/_chunks/libs/@modelcontextprotocol/sdk.d.mts +14 -14
  24. package/dist/_chunks/libs/@modelcontextprotocol/sdk.mjs +12 -25105
  25. package/dist/_chunks/libs/@msgpack/msgpack.mjs +1 -1503
  26. package/dist/_chunks/libs/@nanostores/react.mjs +1 -30
  27. package/dist/_chunks/libs/@smithy/util-base64.mjs +1 -100
  28. package/dist/_chunks/libs/ajv.mjs +1 -777
  29. package/dist/_chunks/libs/atomically.mjs +1 -362
  30. package/dist/_chunks/libs/birpc.mjs +1 -201
  31. package/dist/_chunks/libs/chevrotain-allstar.mjs +2 -850
  32. package/dist/_chunks/libs/chevrotain.mjs +55 -6229
  33. package/dist/_chunks/libs/conf.mjs +1 -2258
  34. package/dist/_chunks/libs/defu.mjs +1 -42
  35. package/dist/_chunks/libs/esm-env.mjs +1 -5
  36. package/dist/_chunks/libs/eventemitter3.mjs +1 -243
  37. package/dist/_chunks/libs/fast-equals.mjs +1 -446
  38. package/dist/_chunks/libs/find-up-simple.mjs +1 -24
  39. package/dist/_chunks/libs/get-port.mjs +1 -107
  40. package/dist/_chunks/libs/is-docker.mjs +1 -26
  41. package/dist/_chunks/libs/is-error-instance.mjs +1 -26
  42. package/dist/_chunks/libs/is-inside-container.mjs +1 -20
  43. package/dist/_chunks/libs/is-plain-obj.mjs +1 -9
  44. package/dist/_chunks/libs/isexe.mjs +1 -127
  45. package/dist/_chunks/libs/json5.mjs +14 -959
  46. package/dist/_chunks/libs/khroma.mjs +1 -605
  47. package/dist/_chunks/libs/ky.mjs +2 -807
  48. package/dist/_chunks/libs/langium.d.mts +2880 -2844
  49. package/dist/_chunks/libs/langium.mjs +32 -20351
  50. package/dist/_chunks/libs/merge-error-cause.mjs +2 -746
  51. package/dist/_chunks/libs/nanostores.mjs +1 -198
  52. package/dist/_chunks/libs/p-limit.mjs +1 -120
  53. package/dist/_chunks/libs/p-queue.mjs +1 -449
  54. package/dist/_chunks/libs/package-manager-detector.mjs +1 -559
  55. package/dist/_chunks/libs/package-up.mjs +1 -10
  56. package/dist/_chunks/libs/parse-ms.mjs +1 -36
  57. package/dist/_chunks/libs/pathe.mjs +1 -0
  58. package/dist/_chunks/libs/picomatch.mjs +1 -1673
  59. package/dist/_chunks/libs/pretty-ms.mjs +1 -80
  60. package/dist/_chunks/libs/remeda.mjs +1 -690
  61. package/dist/_chunks/libs/safe-stringify.mjs +1 -21
  62. package/dist/_chunks/libs/strip-indent.mjs +1 -15
  63. package/dist/_chunks/libs/tinyrainbow.mjs +1 -88
  64. package/dist/_chunks/libs/ts-graphviz.mjs +4 -725
  65. package/dist/_chunks/libs/ufo.mjs +1 -240
  66. package/dist/_chunks/libs/which.mjs +1 -84
  67. package/dist/_chunks/libs/word-wrap.mjs +12 -43
  68. package/dist/_chunks/node.mjs +481 -0
  69. package/dist/_chunks/plugin.mjs +98 -772
  70. package/dist/_chunks/rolldown-runtime.mjs +1 -48
  71. package/dist/_chunks/sequence-view.mjs +1 -575
  72. package/dist/cli/index.mjs +127 -1846
  73. package/dist/config/index.d.mts +2 -2
  74. package/dist/config/index.mjs +1 -6
  75. package/dist/index.d.mts +148 -7
  76. package/dist/index.mjs +1 -21
  77. package/dist/model/builder.mjs +1 -3
  78. package/dist/model/index.d.mts +57 -3
  79. package/dist/model/index.mjs +1 -5
  80. package/dist/vite-plugin/index.d.mts +4 -3
  81. package/dist/vite-plugin/index.mjs +1 -22
  82. package/dist/vite-plugin/internal.d.mts +5 -5
  83. package/dist/vite-plugin/internal.mjs +1 -68
  84. package/package.json +60 -41
  85. package/react/{index.d.ts → index.d.mts} +112 -70
  86. package/react/{index.js → index.mjs} +21361 -22064
  87. package/react/package.json +2 -5
  88. package/vite-plugin-modules.d.ts +5 -5
  89. package/dist/_chunks/LikeC4.d.mts +0 -121
  90. package/dist/_chunks/LikeC4.mjs +0 -202
  91. package/dist/_chunks/config-app.prod.d.mts +0 -18
  92. package/dist/_chunks/config-app.prod.mjs +0 -188
  93. package/dist/_chunks/config-webcomponent.prod.mjs +0 -71
  94. package/dist/_chunks/define-config.mjs +0 -409
  95. package/dist/_chunks/index3.d.mts +0 -60
  96. package/dist/_chunks/index4.d.mts +0 -1
  97. package/dist/_chunks/libs/@smithy/is-array-buffer.mjs +0 -10
  98. package/dist/_chunks/libs/hono.mjs +0 -1829
  99. package/dist/_chunks/libs/nanoid.mjs +0 -29
  100. package/dist/_chunks/model.mjs +0 -12
  101. package/dist/_chunks/module.d.mts +0 -71
  102. package/dist/_chunks/module.mjs +0 -18657
  103. package/dist/_chunks/vite-build.mjs +0 -69
  104. package/dist/_chunks/vite-dev.mjs +0 -79
  105. package/dist/_chunks/vite-preview.mjs +0 -27
  106. package/dist/language/module.d.mts +0 -5
  107. package/dist/language/module.mjs +0 -20
  108. package/dist/vite/vite-build.d.mts +0 -26
  109. package/dist/vite/vite-build.mjs +0 -27
  110. package/dist/vite/vite-dev.d.mts +0 -34
  111. package/dist/vite/vite-dev.mjs +0 -29
  112. package/dist/vite/vite-preview.d.mts +0 -20
  113. package/dist/vite/vite-preview.mjs +0 -26
@@ -1,21 +1,193 @@
1
1
  import { jsx, jsxs, Fragment } from "react/jsx-runtime";
2
- import { cZ as createFileRoute, O as m, cY as Outlet, c$ as useDocumentTitle, cP as Container, dt as SimpleGrid, du as useInViewport, dv as e, ci as Card, aM as Box, bA as Group, R as Text, cT as Link, c_ as redirect, cM as useRouter, k as useMantineColorScheme, cX as stripSearchParams, cV as z, d7 as notFound } from "../vendors.js";
3
- import { F as Fallback } from "./index.js";
4
- import { V as ViewOutlet, L as LikeC4IconRendererContext, k as LikeC4ModelContext, u as useLikeC4Views, A as AdHocViewEditor, H as Header, E as ExportPage, l as EmbedPage, m as ViewEditor, n as ViewReact, o as ViewAsPuml, p as ViewAsMmd, q as ViewAsDot, r as ViewAsD2 } from "./projects.js";
5
- import { c as css, S as StaticLikeC4Diagram, M as Markdown, B as Box$1 } from "../likec4.js";
6
- import { useState, useEffect } from "react";
2
+ import { d8 as useRouter, d9 as useParams, da as isNotFound, db as Container, br as Alert, U as Text, dc as Code, W as Button, dd as e, de as e$1, as as e$2, bt as Group, bh as rem, df as Link, bZ as Title, d1 as createFileRoute, Q as m, d0 as Outlet, d as deepEqual, s as shallowEqual, dg as useMatches, dh as useIsomorphicLayoutEffect, d3 as useDocumentTitle, di as SimpleGrid, dj as useInViewport, dk as e$3, ci as Card, aQ as Box, d2 as redirect, k as useMantineColorScheme, dl as useComputedColorScheme, aA as ActionIcon, aF as MotionDiv, cN as isValidMotionProp, aD as Menu, bc as MenuTarget, bd as MenuDropdown, be as MenuItem, dm as MenuDivider, bp as Stack, b5 as CopyButton$1, dn as Select, dp as ModalRoot, dq as ModalOverlay, dr as ModalContent, ds as ModalBody, bL as Tabs, bM as TabsList, bN as TabsTab, bO as TabsPanel, dt as useMantineTheme, du as useMediaQuery, cq as useDisclosure, aG as Divider, dv as MenuLabel, dw as useSearch, dx as LoadingOverlay, dy as toBlob, c$ as stripSearchParams, cZ as z, cY as useNavigate, bS as useCallbackRef, aL as useIsMounted, T as Tooltip, dz as useAsync, dA as It, dB as Ot, bP as ScrollArea, dC as Nt, dD as notFound } from "../vendors.js";
3
+ import { loadModel } from "likec4:model";
4
+ import { c as css, s as styled, I as IconRendererProvider, a as LikeC4ModelProvider, u as useUpdateEffect, S as StaticLikeC4Diagram, M as Markdown, b as LikeC4AdHocViewEditor, B as Box$1, d as IconMoonStars, e as IconSun, f as createStyleContext, n as navigationPanel, i as isCssProperty, g as useLikeC4Projects$1, h as IconChevronDown, j as IconAlertTriangle, k as IconCheck, l as IconCopy, m as IconExternalLink, o as IconShare, p as pickViewBounds, q as LikeC4Diagram, r as useLikeC4Model, t as useDiagramContext, v as useDiagram, w as useOnDiagramEvent, x as LikeC4EditorProvider } from "../likec4.js";
5
+ import { getProjectIcons } from "likec4:icons";
6
+ import { useMemo, createContext, useContext, useState, useEffect, memo, useRef } from "react";
7
+ import { useStore } from "likec4/vite-plugin/internal";
7
8
  import { RichText } from "@likec4/core/types";
8
- import { pageTitle, ComponentName } from "../const.js";
9
- import { isRpcAvailable } from "likec4:rpc";
9
+ import { pageTitle, ComponentName, useHashHistory, isDevelopment, krokiPumlSvgUrl, krokiD2SvgUrl } from "../const.js";
10
+ import { likec4rpc, isRpcAvailable } from "likec4:rpc";
11
+ import { useLikeC4Projects } from "likec4:projects";
12
+ function Fallback({ error: _error, resetErrorBoundary }) {
13
+ const router = useRouter(), params = useParams({
14
+ strict: !1
15
+ });
16
+ if (isNotFound(_error))
17
+ return /* @__PURE__ */ jsx(Container, { my: "md", children: /* @__PURE__ */ jsxs(Alert, { variant: "light", color: "orange", children: [
18
+ /* @__PURE__ */ jsxs(Text, { c: "orange", fz: "md", children: [
19
+ "The diagram",
20
+ " ",
21
+ /* @__PURE__ */ jsx(Code, { color: "orange", children: params.viewId ?? "unknown" }),
22
+ " ",
23
+ "does not exist or contains errors"
24
+ ] }),
25
+ /* @__PURE__ */ jsx(
26
+ Button,
27
+ {
28
+ onClick: () => {
29
+ resetErrorBoundary(), router.navigate({
30
+ to: "/"
31
+ });
32
+ },
33
+ variant: "light",
34
+ color: "orange",
35
+ mt: "lg",
36
+ size: "xs",
37
+ children: "Go to overview"
38
+ }
39
+ )
40
+ ] }) });
41
+ const error = _error;
42
+ let message = "Unknown error";
43
+ try {
44
+ switch (!0) {
45
+ case e$2(error):
46
+ message = "Unknown error";
47
+ break;
48
+ case e$1(error):
49
+ message = error.stack ?? error.message;
50
+ break;
51
+ case typeof error == "string":
52
+ message = error;
53
+ break;
54
+ case e(error):
55
+ message = error.stack ?? error.message ?? `${error}`;
56
+ break;
57
+ default:
58
+ message = `${error}`;
59
+ break;
60
+ }
61
+ } catch (e2) {
62
+ message = `${e2}`;
63
+ }
64
+ return /* @__PURE__ */ jsx(Container, { my: "md", children: /* @__PURE__ */ jsxs(Alert, { variant: "filled", color: "red", title: "Something went wrong", children: [
65
+ /* @__PURE__ */ jsx(Code, { block: !0, color: "red", children: message }),
66
+ /* @__PURE__ */ jsxs(Group, { mt: "lg", children: [
67
+ /* @__PURE__ */ jsx(
68
+ Button,
69
+ {
70
+ onClick: () => {
71
+ router.invalidate().finally(() => {
72
+ resetErrorBoundary();
73
+ });
74
+ },
75
+ color: "red",
76
+ variant: "white",
77
+ size: "xs",
78
+ children: "Try again"
79
+ }
80
+ ),
81
+ /* @__PURE__ */ jsx(
82
+ Button,
83
+ {
84
+ onClick: () => {
85
+ resetErrorBoundary(), router.navigate({ to: "/" });
86
+ },
87
+ color: "red",
88
+ size: "xs",
89
+ children: "Go to overview"
90
+ }
91
+ )
92
+ ] })
93
+ ] }) });
94
+ }
95
+ const content = css({
96
+ paddingTop: "[120px]",
97
+ position: "relative",
98
+ zIndex: "1",
99
+ sm: {
100
+ paddingTop: "[220px]"
101
+ }
102
+ }), image = css({
103
+ position: "absolute",
104
+ inset: "0",
105
+ opacity: 0.2
106
+ }), inner = css({
107
+ position: "relative"
108
+ }), root = css({
109
+ paddingTop: "[80px]",
110
+ paddingBottom: "[80px]"
111
+ }), description = css({
112
+ maxWidth: rem(540),
113
+ margin: "auto",
114
+ marginTop: "xl",
115
+ marginBottom: "[calc({spacing.xl}*1.5)]"
116
+ }), title = css({
117
+ textAlign: "center",
118
+ fontWeight: "[900]",
119
+ fontSize: "[38px]"
120
+ });
121
+ function NotFound() {
122
+ const params = useParams({
123
+ strict: !1
124
+ });
125
+ return params.viewId ? /* @__PURE__ */ jsx(Container, { my: "md", children: /* @__PURE__ */ jsxs(Alert, { variant: "light", color: "orange", children: [
126
+ /* @__PURE__ */ jsxs(Text, { c: "orange", fz: "md", children: [
127
+ "The diagram",
128
+ " ",
129
+ /* @__PURE__ */ jsx(Code, { color: "orange", children: params.viewId }),
130
+ " ",
131
+ "does not exist or contains errors"
132
+ ] }),
133
+ /* @__PURE__ */ jsx(
134
+ Button,
135
+ {
136
+ component: Link,
137
+ to: "/",
138
+ variant: "light",
139
+ color: "orange",
140
+ mt: "lg",
141
+ size: "xs",
142
+ children: "Go to overview"
143
+ }
144
+ )
145
+ ] }) }) : /* @__PURE__ */ jsx(Container, { className: root, children: /* @__PURE__ */ jsxs("div", { className: inner, children: [
146
+ /* @__PURE__ */ jsx("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 362 145", className: image, children: /* @__PURE__ */ jsx(
147
+ "path",
148
+ {
149
+ fill: "currentColor",
150
+ d: "M62.6 142c-2.133 0-3.2-1.067-3.2-3.2V118h-56c-2 0-3-1-3-3V92.8c0-1.333.4-2.733 1.2-4.2L58.2 4c.8-1.333 2.067-2 3.8-2h28c2 0 3 1 3 3v85.4h11.2c.933 0 1.733.333 2.4 1 .667.533 1 1.267 1 2.2v21.2c0 .933-.333 1.733-1 2.4-.667.533-1.467.8-2.4.8H93v20.8c0 2.133-1.067 3.2-3.2 3.2H62.6zM33 90.4h26.4V51.2L33 90.4zM181.67 144.6c-7.333 0-14.333-1.333-21-4-6.666-2.667-12.866-6.733-18.6-12.2-5.733-5.467-10.266-13-13.6-22.6-3.333-9.6-5-20.667-5-33.2 0-12.533 1.667-23.6 5-33.2 3.334-9.6 7.867-17.133 13.6-22.6 5.734-5.467 11.934-9.533 18.6-12.2 6.667-2.8 13.667-4.2 21-4.2 7.467 0 14.534 1.4 21.2 4.2 6.667 2.667 12.8 6.733 18.4 12.2 5.734 5.467 10.267 13 13.6 22.6 3.334 9.6 5 20.667 5 33.2 0 12.533-1.666 23.6-5 33.2-3.333 9.6-7.866 17.133-13.6 22.6-5.6 5.467-11.733 9.533-18.4 12.2-6.666 2.667-13.733 4-21.2 4zm0-31c9.067 0 15.6-3.733 19.6-11.2 4.134-7.6 6.2-17.533 6.2-29.8s-2.066-22.2-6.2-29.8c-4.133-7.6-10.666-11.4-19.6-11.4-8.933 0-15.466 3.8-19.6 11.4-4 7.6-6 17.533-6 29.8s2 22.2 6 29.8c4.134 7.467 10.667 11.2 19.6 11.2zM316.116 142c-2.134 0-3.2-1.067-3.2-3.2V118h-56c-2 0-3-1-3-3V92.8c0-1.333.4-2.733 1.2-4.2l56.6-84.6c.8-1.333 2.066-2 3.8-2h28c2 0 3 1 3 3v85.4h11.2c.933 0 1.733.333 2.4 1 .666.533 1 1.267 1 2.2v21.2c0 .933-.334 1.733-1 2.4-.667.533-1.467.8-2.4.8h-11.2v20.8c0 2.133-1.067 3.2-3.2 3.2h-27.2zm-29.6-51.6h26.4V51.2l-26.4 39.2z"
151
+ }
152
+ ) }),
153
+ /* @__PURE__ */ jsxs("div", { className: content, children: [
154
+ /* @__PURE__ */ jsx(Title, { className: title, children: "Nothing to see here" }),
155
+ /* @__PURE__ */ jsx(Text, { c: "dimmed", size: "lg", ta: "center", className: description, children: "Page you are trying to open does not exist. You may have mistyped the address, or the page has been moved to another URL. If you think this is an error contact support." }),
156
+ /* @__PURE__ */ jsx(Group, { justify: "center", children: /* @__PURE__ */ jsx(Button, { component: Link, to: "/", search: !0, size: "md", children: "Take me back to home page" }) })
157
+ ] })
158
+ ] }) });
159
+ }
160
+ const ViewOutlet = styled("div", {
161
+ base: {
162
+ padding: "0",
163
+ margin: "0",
164
+ width: "full",
165
+ height: "full"
166
+ }
167
+ });
168
+ function LikeC4IconRendererContext({ children, projectId }) {
169
+ const IconRenderer = useMemo(() => getProjectIcons(projectId), [projectId]);
170
+ return /* @__PURE__ */ jsx(IconRendererProvider, { value: IconRenderer, children });
171
+ }
172
+ const LikeC4ModelDataContext = createContext(null), LikeC4ModelDataContextProvider = LikeC4ModelDataContext.Provider, useLikeC4ModelAtom = () => {
173
+ const ctx = useContext(LikeC4ModelDataContext);
174
+ if (ctx === null)
175
+ throw new Error("LikeC4ModelAtom is not provided");
176
+ return ctx;
177
+ };
178
+ function LikeC4ModelContext({ likec4model, children }) {
179
+ const model = useStore(likec4model);
180
+ return /* @__PURE__ */ jsx(LikeC4ModelDataContextProvider, { value: likec4model, children: /* @__PURE__ */ jsx(LikeC4ModelProvider, { likec4model: model, children }) });
181
+ }
10
182
  const Route$b = createFileRoute("/_single")({
11
183
  staleTime: 1 / 0,
12
184
  loaderDeps() {
13
185
  return [];
14
186
  },
15
187
  loader: async ({ context }) => {
16
- const { loadModel } = await import("likec4:model"), projectId = context.projectId, { $likec4model } = await loadModel(projectId);
188
+ const projectId = context.projectId;
17
189
  return {
18
- $likec4model,
190
+ $likec4model: (await loadModel(projectId)).$likec4model,
19
191
  projectId
20
192
  };
21
193
  },
@@ -25,8 +197,58 @@ function RouteComponent$1() {
25
197
  const { $likec4model, projectId } = Route$b.useLoaderData();
26
198
  return /* @__PURE__ */ jsx(ViewOutlet, { children: /* @__PURE__ */ jsx(m, { FallbackComponent: Fallback, children: /* @__PURE__ */ jsx(LikeC4IconRendererContext, { projectId, children: /* @__PURE__ */ jsx(LikeC4ModelContext, { likec4model: $likec4model, children: /* @__PURE__ */ jsx(Outlet, {}) }) }) }) });
27
199
  }
200
+ function useTransparentBackground(enabled = !0) {
201
+ useIsomorphicLayoutEffect(() => {
202
+ const htmlEl = document.body.parentElement;
203
+ if (!htmlEl || enabled !== !0) return;
204
+ const classname = "transparent-bg";
205
+ return htmlEl.classList.add(classname), () => {
206
+ htmlEl.classList.remove(classname);
207
+ };
208
+ }, [enabled]);
209
+ }
210
+ function useLikeC4Views() {
211
+ const $likec4model = useLikeC4ModelAtom(), [views, setViews] = useState([]);
212
+ return useEffect(() => $likec4model.subscribe((next) => {
213
+ setViews((prev) => {
214
+ const nextViews = [...next.views()].map((v) => {
215
+ const n = v.$layouted, p = prev.find((_) => _.id === v.id);
216
+ return p && deepEqual(n, p) ? p : n;
217
+ });
218
+ return shallowEqual(prev, nextViews) ? prev : nextViews;
219
+ });
220
+ }), [$likec4model]), views;
221
+ }
222
+ function useCurrentViewId() {
223
+ return useParams({
224
+ select: (params) => params.viewId,
225
+ strict: !1
226
+ }) ?? "index";
227
+ }
228
+ function useCurrentView() {
229
+ const viewId = useCurrentViewId(), $likec4model = useLikeC4ModelAtom(), [layoutType, setLayoutType] = useState("manual");
230
+ useUpdateEffect(() => {
231
+ setLayoutType("manual");
232
+ }, [viewId]);
233
+ const [view, setView] = useState(() => $likec4model.get().findView(viewId)?.$layouted ?? null);
234
+ return useEffect(() => $likec4model.subscribe((next) => {
235
+ setView((current) => {
236
+ const vm = next.findView(viewId);
237
+ if (!vm)
238
+ return null;
239
+ const nextView = layoutType === "auto" ? vm.$view : vm.$layouted;
240
+ return deepEqual(current, nextView) ? current : nextView;
241
+ });
242
+ }), [$likec4model, viewId, layoutType]), [view, setLayoutType];
243
+ }
244
+ function useCurrentProject() {
245
+ const projects = useLikeC4Projects(), projectId = useMatches({
246
+ select: (m2) => m2.find((m22) => m22.routeId === "/project/$projectId")?.params?.projectId ?? m2.at(-1)?.context.projectId ?? "default"
247
+ });
248
+ return projects.find((p) => p.id === projectId) ?? projects[0];
249
+ }
28
250
  css({
29
- color: "mantine.colors.dimmed"
251
+ color: "text.dimmed"
30
252
  });
31
253
  css({
32
254
  // background: 'var(--color-surface)',
@@ -37,7 +259,7 @@ const previewBg = css({
37
259
  padding: "0",
38
260
  margin: "0",
39
261
  backgroundOrigin: "padding-box",
40
- backgroundImage: "radial-gradient({colors.mantine.colors.defaultBorder} 15%, {colors.mantine.colors.body} 15%)",
262
+ backgroundImage: "radial-gradient({colors.default.border} 15%, {colors.body} 15%)",
41
263
  backgroundPosition: "0 0",
42
264
  backgroundSize: "12px 12px",
43
265
  _after: {
@@ -74,7 +296,7 @@ function ViewCard({ view }) {
74
296
  const [visible, setVisible] = useState(!1), { ref, inViewport } = useInViewport();
75
297
  return useEffect(() => {
76
298
  if (!inViewport || visible) return;
77
- const tm = setTimeout(() => setVisible(!0), e(30, 80));
299
+ const tm = setTimeout(() => setVisible(!0), e$3(30, 80));
78
300
  return () => clearTimeout(tm);
79
301
  }, [inViewport, visible]), /* @__PURE__ */ jsxs(
80
302
  Card,
@@ -119,6 +341,18 @@ function ViewCard({ view }) {
119
341
  }
120
342
  );
121
343
  }
344
+ function AdHocViewEditor({ projectId }) {
345
+ return /* @__PURE__ */ jsx(
346
+ LikeC4AdHocViewEditor,
347
+ {
348
+ service: {
349
+ process: async ({ predicates }) => ({
350
+ view: await likec4rpc.calcAdhocView({ projectId, predicates })
351
+ })
352
+ }
353
+ }
354
+ );
355
+ }
122
356
  const Route$9 = createFileRoute("/_single/adhoc")({
123
357
  beforeLoad: () => {
124
358
  if (!isRpcAvailable)
@@ -201,6 +435,404 @@ function WebcomponentPage() {
201
435
  }
202
436
  );
203
437
  }
438
+ function ColorSchemeToggle() {
439
+ const { setColorScheme } = useMantineColorScheme({
440
+ keepTransitions: !0
441
+ }), computedColorScheme = useComputedColorScheme("light");
442
+ return /* @__PURE__ */ jsxs(
443
+ ActionIcon,
444
+ {
445
+ visibleFrom: "sm",
446
+ size: "md",
447
+ variant: "subtle",
448
+ color: "gray",
449
+ onClick: () => setColorScheme(computedColorScheme === "light" ? "dark" : "light"),
450
+ "aria-label": "Toggle color scheme",
451
+ children: [
452
+ /* @__PURE__ */ jsx(IconMoonStars, { stroke: 1.5, display: computedColorScheme === "light" ? "block" : "none" }),
453
+ /* @__PURE__ */ jsx(IconSun, { stroke: 1.5, display: computedColorScheme === "dark" ? "block" : "none" })
454
+ ]
455
+ }
456
+ );
457
+ }
458
+ const { withProvider, withContext } = createStyleContext(navigationPanel), shouldForwardProp = (prop, variantKeys) => !variantKeys.includes(prop) && (isValidMotionProp(prop) || !isCssProperty(prop)), Root = withProvider(MotionDiv, "root", {
459
+ shouldForwardProp
460
+ }), Body = withContext(MotionDiv, "body", {
461
+ shouldForwardProp
462
+ });
463
+ withContext(MotionDiv, "label", {
464
+ shouldForwardProp
465
+ });
466
+ withContext(MotionDiv, "dropdown", {
467
+ shouldForwardProp
468
+ });
469
+ const NavigationPanel = {
470
+ Root,
471
+ Body
472
+ };
473
+ function SelectProject() {
474
+ const projects = useLikeC4Projects$1(), project = useCurrentProject();
475
+ return projects.length < 2 ? null : /* @__PURE__ */ jsxs(Menu, { shadow: "md", width: 200, trigger: "click-hover", openDelay: 200, children: [
476
+ /* @__PURE__ */ jsx(MenuTarget, { children: /* @__PURE__ */ jsx(
477
+ Button,
478
+ {
479
+ variant: "subtle",
480
+ size: "sm",
481
+ color: "gray",
482
+ px: "sm",
483
+ rightSection: /* @__PURE__ */ jsx(IconChevronDown, { opacity: 0.5, size: 14 }),
484
+ visibleFrom: "md",
485
+ children: project.title ?? project.id
486
+ }
487
+ ) }),
488
+ /* @__PURE__ */ jsxs(MenuDropdown, { children: [
489
+ /* @__PURE__ */ jsx(
490
+ MenuItem,
491
+ {
492
+ renderRoot: (props) => /* @__PURE__ */ jsx(
493
+ Link,
494
+ {
495
+ ...props,
496
+ to: "/projects"
497
+ }
498
+ ),
499
+ children: "Projects overview"
500
+ }
501
+ ),
502
+ /* @__PURE__ */ jsx(MenuDivider, {}),
503
+ projects.map(({ id, title: title2 }) => /* @__PURE__ */ jsx(
504
+ MenuItem,
505
+ {
506
+ renderRoot: (props) => /* @__PURE__ */ jsx(
507
+ Link,
508
+ {
509
+ ...props,
510
+ to: "/project/$projectId/view/$viewId/",
511
+ params: {
512
+ projectId: id,
513
+ viewId: "index"
514
+ }
515
+ }
516
+ ),
517
+ children: title2 ?? id
518
+ },
519
+ id
520
+ ))
521
+ ] })
522
+ ] });
523
+ }
524
+ const AlertLocalhost = () => /* @__PURE__ */ jsx(
525
+ Alert,
526
+ {
527
+ color: "yellow",
528
+ icon: /* @__PURE__ */ jsx(IconAlertTriangle, {}),
529
+ title: "Localhost URL",
530
+ styles: { body: { gap: rem(4) } },
531
+ children: /* @__PURE__ */ jsx(Text, { c: "yellow", size: "sm", children: "You need to deploy your project to make it available on the internet" })
532
+ }
533
+ ), CopyButtonChild = ({ copied, copy }) => /* @__PURE__ */ jsx(
534
+ Button,
535
+ {
536
+ size: "xs",
537
+ color: copied ? "teal" : "gray",
538
+ variant: "light",
539
+ leftSection: copied ? /* @__PURE__ */ jsx(IconCheck, { style: { width: rem(16) } }) : /* @__PURE__ */ jsx(IconCopy, { style: { width: rem(16) } }),
540
+ onClick: copy,
541
+ children: copied ? "Copied" : "Copy to clipboard"
542
+ }
543
+ ), EmbedPanel = ({ diagram }) => {
544
+ const router = useRouter(), { colorScheme } = useMantineColorScheme(), [theme, setTheme] = useState(colorScheme), padding = 20;
545
+ let location = router.buildLocation({
546
+ to: "/embed/$viewId/",
547
+ // '/' at the end added by Gemini 3, must have to pass typecheck
548
+ params: { viewId: diagram.id },
549
+ search: {
550
+ padding,
551
+ ...theme !== "auto" ? { theme } : {}
552
+ // changed from "theme: theme !== 'auto' ? theme : undefined" by Gemini 3
553
+ }
554
+ }).href;
555
+ location = useHashHistory ? `#${location}` : location;
556
+ const url = new URL(location, window.location.href), width = diagram.bounds.width + padding * 2, height = diagram.bounds.height + padding * 2, href = url.href, code = `
557
+ <div style="aspect-ratio:${width}/${height};width:100%;height:auto;max-width:${width}px;margin:0 auto">
558
+ <iframe src="${href}" width="100%" height="100%" style="border:0;background:transparent;"></iframe>
559
+ </div>
560
+ `.trim();
561
+ return /* @__PURE__ */ jsxs(Stack, { children: [
562
+ code.includes("http://localhost") && /* @__PURE__ */ jsx(AlertLocalhost, {}),
563
+ /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsx(Text, { size: "sm", children: "Embedded view is an iframe with a static diagram" }) }),
564
+ /* @__PURE__ */ jsxs(Stack, { gap: "xs", children: [
565
+ /* @__PURE__ */ jsxs(Group, { justify: "space-between", children: [
566
+ /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsx(Text, { fw: "500", size: "sm", children: "HTML" }) }),
567
+ /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
568
+ /* @__PURE__ */ jsx(ActionIcon, { component: "a", href, target: "_blank", variant: "light", color: "gray", children: /* @__PURE__ */ jsx(IconExternalLink, {}) }),
569
+ /* @__PURE__ */ jsx(CopyButton$1, { value: code, timeout: 1500, children: CopyButtonChild })
570
+ ] })
571
+ ] }),
572
+ /* @__PURE__ */ jsx(Code, { block: !0, children: code }),
573
+ /* @__PURE__ */ jsx(
574
+ Box,
575
+ {
576
+ style: {
577
+ alignSelf: "flex-start"
578
+ },
579
+ children: /* @__PURE__ */ jsx(
580
+ Select,
581
+ {
582
+ label: "Color scheme",
583
+ value: theme,
584
+ allowDeselect: !1,
585
+ onChange: (v) => setTheme(v ?? "auto"),
586
+ data: [
587
+ { value: "auto", label: "Auto" },
588
+ { value: "light", label: "Light" },
589
+ { value: "dark", label: "Dark" }
590
+ ]
591
+ }
592
+ )
593
+ }
594
+ )
595
+ ] })
596
+ ] });
597
+ };
598
+ function WebcomponentsPanel({ diagram }) {
599
+ const router = useRouter();
600
+ let base = router.basepath.endsWith("/") ? router.basepath : `${router.basepath}/`;
601
+ const jscode = `
602
+ <script module src="${new URL(
603
+ `${base}likec4-views.js`,
604
+ window.location.href
605
+ ).href}"><\/script>
606
+ `.trim(), htmlCode = `
607
+ <${ComponentName.View}
608
+ view-id="${encodeURIComponent(diagram.id)}"
609
+ browser="true"
610
+ dynamic-variant="sequence">
611
+ </${ComponentName.View}>
612
+ `.trim(), webcomponentPreview = router.buildLocation(
613
+ {
614
+ to: "/webcomponent/$/",
615
+ // '/' at the end added by Gemini 3, must have to pass typecheck
616
+ params: { _splat: diagram.id },
617
+ search: !0
618
+ }
619
+ );
620
+ return /* @__PURE__ */ jsxs(Stack, { children: [
621
+ jscode.includes("http://localhost") && /* @__PURE__ */ jsx(AlertLocalhost, {}),
622
+ /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsx(Text, { size: "sm", children: "Add this script to your page:" }) }),
623
+ /* @__PURE__ */ jsxs(Stack, { gap: "xs", children: [
624
+ /* @__PURE__ */ jsxs(Group, { justify: "space-between", children: [
625
+ /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsx(Text, { fw: "500", size: "sm", children: "JavaScript" }) }),
626
+ /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
627
+ /* @__PURE__ */ jsx(ActionIcon, { component: "a", href: webcomponentPreview.href, target: "_blank", variant: "light", color: "gray", children: /* @__PURE__ */ jsx(IconExternalLink, {}) }),
628
+ /* @__PURE__ */ jsx(
629
+ CopyButton$1,
630
+ {
631
+ value: jscode,
632
+ timeout: 1500,
633
+ children: CopyButtonChild
634
+ }
635
+ )
636
+ ] })
637
+ ] }),
638
+ /* @__PURE__ */ jsx(Code, { block: !0, children: jscode }),
639
+ /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsxs(Text, { size: "sm", c: "dimmed", children: [
640
+ "This script defines a custom element (webcomponent) that renders your diagrams.",
641
+ /* @__PURE__ */ jsx("br", {}),
642
+ "Script must be inserted once in the ",
643
+ /* @__PURE__ */ jsx("code", { children: "<head>" }),
644
+ " or at the end of the ",
645
+ /* @__PURE__ */ jsx("code", { children: "<body>" }),
646
+ " ",
647
+ "tag."
648
+ ] }) })
649
+ ] }),
650
+ /* @__PURE__ */ jsxs(Stack, { gap: "xs", children: [
651
+ /* @__PURE__ */ jsxs(Group, { justify: "space-between", children: [
652
+ /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsx(Text, { fw: "500", size: "sm", children: "HTML" }) }),
653
+ /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsx(
654
+ CopyButton$1,
655
+ {
656
+ value: htmlCode,
657
+ timeout: 1500,
658
+ children: CopyButtonChild
659
+ }
660
+ ) })
661
+ ] }),
662
+ /* @__PURE__ */ jsx(Code, { block: !0, children: htmlCode }),
663
+ /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsxs(Text, { size: "sm", c: "dimmed", children: [
664
+ "Insert this code to your page. Page may have multiple ",
665
+ /* @__PURE__ */ jsx("code", { children: "<likec4-view>" }),
666
+ "."
667
+ ] }) })
668
+ ] })
669
+ ] });
670
+ }
671
+ function ShareModal({
672
+ onClose
673
+ }) {
674
+ const [diagram] = useCurrentView(), [activeTab, setActiveTab] = useState("webcomponent");
675
+ return diagram ? /* @__PURE__ */ jsxs(
676
+ ModalRoot,
677
+ {
678
+ size: "xl",
679
+ opened: !0,
680
+ onClose,
681
+ children: [
682
+ /* @__PURE__ */ jsx(ModalOverlay, { backgroundOpacity: 0.5, blur: 3 }),
683
+ /* @__PURE__ */ jsx(ModalContent, { children: /* @__PURE__ */ jsxs(ModalBody, { children: [
684
+ /* @__PURE__ */ jsxs(Tabs, { value: activeTab, onChange: (tab) => setActiveTab(tab ?? "webcomponent"), children: [
685
+ /* @__PURE__ */ jsxs(TabsList, { children: [
686
+ /* @__PURE__ */ jsx(TabsTab, { value: "webcomponent", children: "Webcomponent" }),
687
+ /* @__PURE__ */ jsx(TabsTab, { value: "embed", children: "Embedded" })
688
+ ] }),
689
+ /* @__PURE__ */ jsx(TabsPanel, { value: "embed", pt: "md", children: /* @__PURE__ */ jsx(EmbedPanel, { diagram }) }),
690
+ /* @__PURE__ */ jsx(TabsPanel, { value: "webcomponent", pt: "md", children: /* @__PURE__ */ jsx(WebcomponentsPanel, { diagram }) })
691
+ ] }),
692
+ /* @__PURE__ */ jsx(Group, { justify: "flex-end", mt: "lg", children: /* @__PURE__ */ jsx(Button, { size: "sm", onClick: onClose, children: "Close" }) })
693
+ ] }) })
694
+ ]
695
+ }
696
+ ) : null;
697
+ }
698
+ const Header = memo(() => {
699
+ const projects = useLikeC4Projects$1(), isReactDiagramRoute = useMatches({
700
+ select(matches) {
701
+ return matches.some(
702
+ ({ routeId }) => routeId === "/_single/view/$viewId/" || routeId === "/project/$projectId/view/$viewId/"
703
+ );
704
+ }
705
+ }), { breakpoints } = useMantineTheme(), isTablet = useMediaQuery(`(min-width: ${breakpoints.md})`) ?? !1, [opened, { open, close }] = useDisclosure(!1);
706
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
707
+ /* @__PURE__ */ jsx(NavigationPanel.Root, { panelPosition: "right", hideBelow: "md", children: /* @__PURE__ */ jsxs(NavigationPanel.Body, { gap: "2", children: [
708
+ isReactDiagramRoute ? /* @__PURE__ */ jsxs(Fragment, { children: [
709
+ /* @__PURE__ */ jsx(SelectProject, {}),
710
+ projects.length <= 1 && /* @__PURE__ */ jsx(
711
+ Button,
712
+ {
713
+ size: isTablet ? "sm" : "xs",
714
+ leftSection: /* @__PURE__ */ jsx(IconShare, { size: 14 }),
715
+ onClick: open,
716
+ children: "Share"
717
+ }
718
+ ),
719
+ /* @__PURE__ */ jsx(ExportButton, {})
720
+ ] }) : /* @__PURE__ */ jsx(
721
+ Button,
722
+ {
723
+ component: Link,
724
+ to: "/view/$viewId/",
725
+ size: isTablet ? "sm" : "xs",
726
+ variant: "subtle",
727
+ color: "gray",
728
+ children: "Back to diagram"
729
+ }
730
+ ),
731
+ /* @__PURE__ */ jsx(Divider, { orientation: "vertical", visibleFrom: "md" }),
732
+ /* @__PURE__ */ jsx(ColorSchemeToggle, {})
733
+ ] }) }),
734
+ opened && /* @__PURE__ */ jsx(ShareModal, { onClose: close })
735
+ ] });
736
+ }), enableDownload = (params) => ({
737
+ ...params,
738
+ download: !0
739
+ });
740
+ function ExportButton() {
741
+ const isInsideProject = useMatches({
742
+ select: (matches) => matches.some(({ routeId }) => routeId === "/project/$projectId")
743
+ });
744
+ return /* @__PURE__ */ jsxs(Menu, { shadow: "md", width: 200, trigger: "click-hover", openDelay: 200, children: [
745
+ /* @__PURE__ */ jsx(MenuTarget, { children: /* @__PURE__ */ jsx(
746
+ Button,
747
+ {
748
+ variant: "subtle",
749
+ size: "sm",
750
+ color: "gray",
751
+ px: "sm",
752
+ rightSection: /* @__PURE__ */ jsx(IconChevronDown, { opacity: 0.5, size: 14 }),
753
+ visibleFrom: "md",
754
+ children: "Export"
755
+ }
756
+ ) }),
757
+ /* @__PURE__ */ jsxs(MenuDropdown, { children: [
758
+ /* @__PURE__ */ jsx(MenuLabel, { children: "Current view" }),
759
+ /* @__PURE__ */ jsx(
760
+ MenuItem,
761
+ {
762
+ renderRoot: (props) => /* @__PURE__ */ jsx(
763
+ Link,
764
+ {
765
+ target: "_blank",
766
+ to: isInsideProject ? "/project/$projectId/export/$viewId/" : "/export/$viewId/",
767
+ search: enableDownload,
768
+ ...props
769
+ }
770
+ ),
771
+ children: "Export as .png"
772
+ }
773
+ ),
774
+ /* @__PURE__ */ jsx(
775
+ MenuItem,
776
+ {
777
+ renderRoot: (props) => /* @__PURE__ */ jsx(
778
+ Link,
779
+ {
780
+ to: isInsideProject ? "/project/$projectId/view/$viewId/dot/" : "/view/$viewId/dot/",
781
+ search: !0,
782
+ ...props
783
+ }
784
+ ),
785
+ children: "Export as .dot"
786
+ }
787
+ ),
788
+ /* @__PURE__ */ jsx(
789
+ MenuItem,
790
+ {
791
+ renderRoot: (props) => /* @__PURE__ */ jsx(
792
+ Link,
793
+ {
794
+ to: isInsideProject ? "/project/$projectId/view/$viewId/d2" : "/view/$viewId/d2",
795
+ search: !0,
796
+ ...props
797
+ }
798
+ ),
799
+ children: "Export as .d2"
800
+ }
801
+ ),
802
+ /* @__PURE__ */ jsx(
803
+ MenuItem,
804
+ {
805
+ renderRoot: (props) => /* @__PURE__ */ jsx(
806
+ Link,
807
+ {
808
+ to: isInsideProject ? "/project/$projectId/view/$viewId/mmd" : "/view/$viewId/mmd",
809
+ search: !0,
810
+ ...props
811
+ }
812
+ ),
813
+ children: "Export as .mmd"
814
+ }
815
+ ),
816
+ /* @__PURE__ */ jsx(
817
+ MenuItem,
818
+ {
819
+ renderRoot: (props) => /* @__PURE__ */ jsx(
820
+ Link,
821
+ {
822
+ to: isInsideProject ? "/project/$projectId/view/$viewId/puml" : "/view/$viewId/puml",
823
+ search: !0,
824
+ ...props
825
+ }
826
+ ),
827
+ children: "Export as .puml"
828
+ }
829
+ ),
830
+ /* @__PURE__ */ jsx(MenuItem, { disabled: !0, children: "Export to Draw.io" }),
831
+ /* @__PURE__ */ jsx(MenuItem, { disabled: !0, children: "Export to Miro" }),
832
+ /* @__PURE__ */ jsx(MenuItem, { disabled: !0, children: "Export to Notion" })
833
+ ] })
834
+ ] });
835
+ }
204
836
  const Route$7 = createFileRoute("/_single/view/$viewId")({
205
837
  component: ViewLayout,
206
838
  errorComponent: ({ error, reset }) => /* @__PURE__ */ jsx(Fallback, { error, resetErrorBoundary: reset })
@@ -211,6 +843,113 @@ function ViewLayout() {
211
843
  /* @__PURE__ */ jsx(Header, {})
212
844
  ] });
213
845
  }
846
+ async function downloadAsPng({
847
+ pngFilename,
848
+ viewport
849
+ }) {
850
+ try {
851
+ const blob = await toBlob(viewport, {
852
+ backgroundColor: "transparent",
853
+ cacheBust: !0,
854
+ imagePlaceholder: "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="
855
+ });
856
+ if (!blob)
857
+ throw new Error("Failed to create PNG blob");
858
+ var url = URL.createObjectURL(blob), link = document.createElement("a");
859
+ link.setAttribute("download", `${pngFilename}.png`), link.href = url, document.body.appendChild(link), link.click(), await new Promise((resolve) => setTimeout(resolve, 1e3)), document.body.removeChild(link), URL.revokeObjectURL(url), window.close();
860
+ } catch (err) {
861
+ console.error(err), window.alert("Failed to export to PNG, check the console for more details.");
862
+ }
863
+ }
864
+ function ExportPage() {
865
+ const [diagram] = useCurrentView();
866
+ return useTransparentBackground(), diagram ? /* @__PURE__ */ jsx(GuardedExportPage, { diagram }) : /* @__PURE__ */ jsx("div", { children: "Loading..." });
867
+ }
868
+ function GuardedExportPage({ diagram }) {
869
+ const {
870
+ padding = 20,
871
+ download = !1,
872
+ dynamic
873
+ } = useSearch({
874
+ strict: !1
875
+ }), viewportRef = useRef(null), loadingOverlayRef = useRef(null), downloadedRef = useRef(!1), bounds = pickViewBounds(diagram, dynamic), downloadDiagram = () => {
876
+ const viewport = viewportRef.current;
877
+ if (!download || !viewport || !diagram || downloadedRef.current)
878
+ return;
879
+ const loadingOverlay = loadingOverlayRef.current;
880
+ loadingOverlay && (loadingOverlay.style.display = "none"), downloadedRef.current = !0, downloadAsPng({
881
+ pngFilename: diagram.id,
882
+ viewport
883
+ });
884
+ }, extraPadding = 16, width = bounds.width + padding * 2 + extraPadding, height = bounds.height + padding * 2 + extraPadding;
885
+ return /* @__PURE__ */ jsxs(
886
+ Box$1,
887
+ {
888
+ ref: viewportRef,
889
+ "data-testid": "export-page",
890
+ css: {
891
+ position: "fixed",
892
+ top: "0",
893
+ left: "0",
894
+ padding: "0",
895
+ margin: "0",
896
+ background: "transparent",
897
+ overflow: "hidden",
898
+ zIndex: 2
899
+ },
900
+ style: {
901
+ marginRight: "auto",
902
+ marginBottom: "auto",
903
+ minWidth: width,
904
+ width,
905
+ minHeight: height,
906
+ height
907
+ },
908
+ children: [
909
+ download && /* @__PURE__ */ jsx(LoadingOverlay, { ref: loadingOverlayRef, visible: !0 }),
910
+ /* @__PURE__ */ jsx(
911
+ LikeC4Diagram,
912
+ {
913
+ view: diagram,
914
+ fitView: !1,
915
+ fitViewPadding: {
916
+ top: "0px",
917
+ bottom: "0px",
918
+ left: "0px",
919
+ right: "0px"
920
+ },
921
+ background: "transparent",
922
+ reduceGraphics: !1,
923
+ dynamicViewVariant: dynamic,
924
+ className: "likec4-static-view",
925
+ pannable: !1,
926
+ zoomable: !1,
927
+ controls: !1,
928
+ enableNotations: !1,
929
+ enableElementDetails: !1,
930
+ enableRelationshipDetails: !1,
931
+ enableRelationshipBrowser: !1,
932
+ enableDynamicViewWalkthrough: !1,
933
+ enableFocusMode: !1,
934
+ enableSearch: !1,
935
+ nodesSelectable: !1,
936
+ enableElementTags: !1,
937
+ onInitialized: () => {
938
+ if (!viewportRef.current) {
939
+ console.error("viewportRef.current is null");
940
+ return;
941
+ }
942
+ const x = Math.round(-bounds.x + padding), y = Math.round(-bounds.y + padding);
943
+ [...viewportRef.current.querySelectorAll(".react-flow__viewport")].forEach((el) => {
944
+ el.style.transform = "translate(" + x + "px, " + y + "px)";
945
+ }), download && window.setTimeout(downloadDiagram, 500);
946
+ }
947
+ }
948
+ )
949
+ ]
950
+ }
951
+ );
952
+ }
214
953
  const Route$6 = createFileRoute("/_single/export/$viewId")({
215
954
  validateSearch: z.object({
216
955
  download: z.boolean().optional().catch(!1)
@@ -223,11 +962,318 @@ const Route$6 = createFileRoute("/_single/export/$viewId")({
223
962
  ]
224
963
  },
225
964
  component: ExportPage
226
- }), Route$5 = createFileRoute("/_single/embed/$viewId")({
965
+ });
966
+ function EmbedPage() {
967
+ const {
968
+ padding = 20,
969
+ dynamic
970
+ } = useSearch({
971
+ strict: !1
972
+ }), [diagram] = useCurrentView();
973
+ if (useTransparentBackground(!!diagram), !diagram)
974
+ return /* @__PURE__ */ jsx("div", { children: "Loading..." });
975
+ const bounds = pickViewBounds(diagram, dynamic);
976
+ return /* @__PURE__ */ jsx(
977
+ "div",
978
+ {
979
+ style: {
980
+ position: "absolute",
981
+ top: 0,
982
+ left: "50%",
983
+ boxSizing: "border-box",
984
+ padding,
985
+ transform: "translateX(-50%)",
986
+ aspectRatio: `${bounds.width + padding * 2} / ${bounds.height + padding * 2}`,
987
+ width: "100vw",
988
+ maxWidth: bounds.width + padding * 2,
989
+ height: "auto",
990
+ maxHeight: "100vh"
991
+ },
992
+ children: /* @__PURE__ */ jsx(
993
+ StaticLikeC4Diagram,
994
+ {
995
+ view: diagram,
996
+ fitView: !0,
997
+ background: "transparent",
998
+ fitViewPadding: 0,
999
+ dynamicViewVariant: dynamic,
1000
+ initialWidth: bounds.width,
1001
+ initialHeight: bounds.height
1002
+ }
1003
+ )
1004
+ }
1005
+ );
1006
+ }
1007
+ const Route$5 = createFileRoute("/_single/embed/$viewId")({
227
1008
  component: EmbedPage
228
- }), Route$4 = createFileRoute("/_single/view/$viewId/")({
1009
+ });
1010
+ function ViewReact() {
1011
+ const navigate = useNavigate(), [view, setLayoutType] = useCurrentView(), model = useLikeC4Model(), { dynamic } = useSearch({
1012
+ from: "__root__"
1013
+ }), onNavigateTo = useCallbackRef((viewId) => {
1014
+ navigate({
1015
+ to: "./",
1016
+ viewTransition: !1,
1017
+ params: (current) => ({
1018
+ ...current,
1019
+ viewId
1020
+ }),
1021
+ search: !0
1022
+ });
1023
+ }), title2 = view ? view.title ?? view.id : "View not found", pageTitle$1 = model.project.title ?? pageTitle;
1024
+ if (useDocumentTitle(`${title2} - ${pageTitle$1}`), !view)
1025
+ return /* @__PURE__ */ jsx(NotFound, {});
1026
+ const hasNotations = (view.notation?.nodes ?? []).length > 0;
1027
+ return /* @__PURE__ */ jsxs(
1028
+ LikeC4Diagram,
1029
+ {
1030
+ view,
1031
+ zoomable: !0,
1032
+ pannable: !0,
1033
+ controls: !0,
1034
+ fitViewPadding: {
1035
+ top: "70px",
1036
+ bottom: "32px",
1037
+ left: "32px",
1038
+ right: "32px"
1039
+ },
1040
+ showNavigationButtons: !0,
1041
+ enableSearch: !0,
1042
+ enableFocusMode: !0,
1043
+ enableDynamicViewWalkthrough: !0,
1044
+ dynamicViewVariant: dynamic,
1045
+ enableElementDetails: !0,
1046
+ enableRelationshipDetails: !0,
1047
+ enableRelationshipBrowser: !0,
1048
+ enableElementTags: !0,
1049
+ enableCompareWithLatest: !0,
1050
+ enableNotations: hasNotations,
1051
+ nodesSelectable: !0,
1052
+ onNavigateTo,
1053
+ onLayoutTypeChange: setLayoutType,
1054
+ onLogoClick: () => {
1055
+ navigate({
1056
+ to: "/"
1057
+ });
1058
+ },
1059
+ children: [
1060
+ /* @__PURE__ */ jsx(ListenForDynamicVariantChange, {}),
1061
+ /* @__PURE__ */ jsx(OpenRelationshipBrowserFromUrl, {})
1062
+ ]
1063
+ }
1064
+ );
1065
+ }
1066
+ function OpenRelationshipBrowserFromUrl() {
1067
+ const router = useRouter(), diagram = useDiagram(), { relationships } = useSearch({
1068
+ from: "__root__"
1069
+ }), processedRef = useRef(null), isInitializedRef = useRef(!1), isProcessingRef = useRef(Promise.resolve()), isMounted = useIsMounted(), openAndClear = (fqn) => {
1070
+ isProcessingRef.current = isProcessingRef.current.then(async () => {
1071
+ if (!(!isMounted() || processedRef.current === fqn))
1072
+ try {
1073
+ processedRef.current = fqn, diagram.openRelationshipsBrowser(fqn), await router.buildAndCommitLocation({
1074
+ search: (s) => {
1075
+ const { relationships: _, ...rest } = s;
1076
+ return rest;
1077
+ },
1078
+ replace: !0,
1079
+ viewTransition: !1
1080
+ });
1081
+ } catch (error) {
1082
+ console.error("Failed to open relationship browser:", error);
1083
+ }
1084
+ });
1085
+ }, process = () => {
1086
+ if (!relationships) {
1087
+ processedRef.current = null;
1088
+ return;
1089
+ }
1090
+ relationships && processedRef.current !== relationships && openAndClear(relationships);
1091
+ };
1092
+ return useOnDiagramEvent("initialized", () => {
1093
+ isInitializedRef.current = !0, process();
1094
+ }), useUpdateEffect(() => {
1095
+ process();
1096
+ }, [relationships]), null;
1097
+ }
1098
+ function ListenForDynamicVariantChange() {
1099
+ const router = useRouter(), dynamicViewVariant = useDiagramContext((c) => c.dynamicViewVariant);
1100
+ return useUpdateEffect(() => {
1101
+ (router.latestLocation.search.dynamic ?? "diagram") !== dynamicViewVariant && router.buildAndCommitLocation({
1102
+ search: (current) => ({
1103
+ ...current,
1104
+ dynamic: dynamicViewVariant
1105
+ }),
1106
+ viewTransition: !1
1107
+ });
1108
+ }, [dynamicViewVariant]), null;
1109
+ }
1110
+ function ViewEditor() {
1111
+ const navigate = useNavigate(), project = useCurrentProject(), [view, setLayoutType] = useCurrentView(), $likec4model = useLikeC4ModelAtom(), { dynamic } = useSearch({ strict: !1 }), onNavigateTo = useCallbackRef((viewId) => {
1112
+ navigate({
1113
+ to: "./",
1114
+ viewTransition: !1,
1115
+ params: (current) => ({
1116
+ ...current,
1117
+ viewId
1118
+ }),
1119
+ search: !0
1120
+ });
1121
+ });
1122
+ if (!view)
1123
+ return /* @__PURE__ */ jsx(NotFound, {});
1124
+ const hasNotations = (view.notation?.nodes ?? []).length > 0;
1125
+ return /* @__PURE__ */ jsx(
1126
+ LikeC4EditorProvider,
1127
+ {
1128
+ editor: {
1129
+ fetchView: (id, layout) => {
1130
+ const model = $likec4model.get().view(id);
1131
+ return layout === "auto" ? model.$view : model.$layouted;
1132
+ },
1133
+ handleChange: (viewId, change) => {
1134
+ const event = {
1135
+ projectId: project.id,
1136
+ viewId,
1137
+ change
1138
+ };
1139
+ return likec4rpc.updateView(event);
1140
+ }
1141
+ },
1142
+ children: /* @__PURE__ */ jsxs(
1143
+ LikeC4Diagram,
1144
+ {
1145
+ view,
1146
+ zoomable: !0,
1147
+ pannable: !0,
1148
+ controls: !0,
1149
+ fitViewPadding: {
1150
+ top: "70px",
1151
+ bottom: "32px",
1152
+ left: "50px",
1153
+ right: "32px"
1154
+ },
1155
+ showNavigationButtons: !0,
1156
+ enableNotations: isDevelopment || hasNotations,
1157
+ enableSearch: !0,
1158
+ enableDynamicViewWalkthrough: !0,
1159
+ enableFocusMode: !0,
1160
+ enableElementDetails: !0,
1161
+ enableRelationshipDetails: !0,
1162
+ enableRelationshipBrowser: !0,
1163
+ enableElementTags: !0,
1164
+ enableCompareWithLatest: !0,
1165
+ dynamicViewVariant: dynamic,
1166
+ onNavigateTo,
1167
+ onLayoutTypeChange: setLayoutType,
1168
+ onLogoClick: () => {
1169
+ navigate({
1170
+ to: "/"
1171
+ });
1172
+ },
1173
+ children: [
1174
+ /* @__PURE__ */ jsx(ListenForDynamicVariantChange, {}),
1175
+ /* @__PURE__ */ jsx(OpenRelationshipBrowserFromUrl, {})
1176
+ ]
1177
+ }
1178
+ )
1179
+ }
1180
+ );
1181
+ }
1182
+ const Route$4 = createFileRoute("/_single/view/$viewId/")({
229
1183
  component: isRpcAvailable ? ViewEditor : ViewReact
230
- }), Route$3 = createFileRoute("/_single/view/$viewId/puml")({
1184
+ });
1185
+ function CopyButton({ text }) {
1186
+ return /* @__PURE__ */ jsx(CopyButton$1, { value: text, timeout: 2e3, children: ({ copied, copy }) => /* @__PURE__ */ jsx(Tooltip, { label: copied ? "Copied" : "Copy", withArrow: !0, position: "right", children: /* @__PURE__ */ jsx(ActionIcon, { color: copied ? "teal" : "gray", variant: copied ? "light" : "subtle", onClick: copy, children: copied ? /* @__PURE__ */ jsx(IconCheck, { style: { width: rem(16) } }) : /* @__PURE__ */ jsx(IconCopy, { style: { width: rem(16) } }) }) }) });
1187
+ }
1188
+ function CopyToClipboard({ text }) {
1189
+ return /* @__PURE__ */ jsx(Box, { pos: "absolute", top: "0", right: "0", p: "4", children: /* @__PURE__ */ jsx(CopyButton, { text }) });
1190
+ }
1191
+ const svgContainer = css({
1192
+ minWidth: 300,
1193
+ "& svg": {
1194
+ width: "100%",
1195
+ height: "auto"
1196
+ }
1197
+ }), cssScrollArea = css({
1198
+ height: "100%",
1199
+ "& .mantine-ScrollArea-viewport": {
1200
+ minHeight: "100%"
1201
+ },
1202
+ "& .mantine-ScrollArea-viewport > div": {
1203
+ minHeight: "100%",
1204
+ height: "100%"
1205
+ }
1206
+ }), cssCodeBlock = css({
1207
+ minHeight: "100%"
1208
+ }), viewWithTopPadding = css({
1209
+ height: "100%",
1210
+ paddingTop: "[var(--header-height)]"
1211
+ }), fetchFromKroki$1 = async (puml) => await (await fetch(krokiPumlSvgUrl, {
1212
+ method: "POST",
1213
+ cache: "force-cache",
1214
+ body: JSON.stringify({
1215
+ diagram_source: puml,
1216
+ output_format: "svg"
1217
+ }),
1218
+ headers: {
1219
+ "Content-Type": "application/json"
1220
+ }
1221
+ })).text();
1222
+ function ViewAsPuml({ pumlSource }) {
1223
+ const [krokiSvg, { execute }] = useAsync(fetchFromKroki$1, null);
1224
+ return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsxs(
1225
+ It,
1226
+ {
1227
+ className: viewWithTopPadding,
1228
+ orientation: "horizontal",
1229
+ children: [
1230
+ /* @__PURE__ */ jsx(Ot, { children: /* @__PURE__ */ jsxs(
1231
+ ScrollArea,
1232
+ {
1233
+ className: cssScrollArea,
1234
+ p: 5,
1235
+ styles: {
1236
+ viewport: {
1237
+ borderRadius: 6
1238
+ }
1239
+ },
1240
+ children: [
1241
+ /* @__PURE__ */ jsx(Code, { block: !0, className: cssCodeBlock, children: pumlSource }),
1242
+ /* @__PURE__ */ jsx(CopyToClipboard, { text: pumlSource })
1243
+ ]
1244
+ }
1245
+ ) }),
1246
+ /* @__PURE__ */ jsx(
1247
+ Nt,
1248
+ {
1249
+ style: {
1250
+ width: 10
1251
+ }
1252
+ }
1253
+ ),
1254
+ /* @__PURE__ */ jsx(Ot, { children: /* @__PURE__ */ jsxs(ScrollArea, { h: "100%", children: [
1255
+ krokiSvg.status !== "success" && /* @__PURE__ */ jsxs(Fragment, { children: [
1256
+ /* @__PURE__ */ jsx(
1257
+ Button,
1258
+ {
1259
+ mt: "xs",
1260
+ variant: "light",
1261
+ disabled: krokiSvg.status === "loading",
1262
+ onClick: () => {
1263
+ execute(pumlSource);
1264
+ },
1265
+ children: krokiSvg.status === "loading" ? "Loading..." : "Render with Kroki"
1266
+ }
1267
+ ),
1268
+ krokiSvg.status === "error" && /* @__PURE__ */ jsx(Box$1, { children: krokiSvg.error?.message })
1269
+ ] }),
1270
+ krokiSvg.status === "success" && /* @__PURE__ */ jsx(Box$1, { className: svgContainer, children: krokiSvg.result ? /* @__PURE__ */ jsx("div", { dangerouslySetInnerHTML: { __html: krokiSvg.result } }) : /* @__PURE__ */ jsx(Box$1, { children: "Empty result" }) })
1271
+ ] }) })
1272
+ ]
1273
+ }
1274
+ ) });
1275
+ }
1276
+ const Route$3 = createFileRoute("/_single/view/$viewId/puml")({
231
1277
  component: Page$3,
232
1278
  staleTime: 1 / 0,
233
1279
  loader: async ({ params, context }) => {
@@ -246,6 +1292,53 @@ function Page$3() {
246
1292
  const { source } = Route$3.useLoaderData();
247
1293
  return /* @__PURE__ */ jsx(ViewAsPuml, { pumlSource: source });
248
1294
  }
1295
+ const renderSvg = async (viewId, diagram) => {
1296
+ const { default: mermaid } = await import("https://cdn.jsdelivr.net/npm/mermaid@11.12/dist/mermaid.esm.min.mjs");
1297
+ mermaid.initialize({
1298
+ theme: "dark"
1299
+ });
1300
+ const { svg } = await mermaid.render(viewId, diagram);
1301
+ return svg;
1302
+ };
1303
+ function ViewAsMmd({ viewId, mmdSource }) {
1304
+ const [mmdSvg, { execute }] = useAsync(renderSvg, null);
1305
+ return useEffect(() => {
1306
+ execute(viewId, mmdSource);
1307
+ }, [mmdSource]), /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsxs(
1308
+ It,
1309
+ {
1310
+ className: viewWithTopPadding,
1311
+ orientation: "horizontal",
1312
+ children: [
1313
+ /* @__PURE__ */ jsx(Ot, { children: /* @__PURE__ */ jsxs(
1314
+ ScrollArea,
1315
+ {
1316
+ className: cssScrollArea,
1317
+ p: 5,
1318
+ styles: {
1319
+ viewport: {
1320
+ borderRadius: 6
1321
+ }
1322
+ },
1323
+ children: [
1324
+ /* @__PURE__ */ jsx(Code, { block: !0, className: cssCodeBlock, children: mmdSource }),
1325
+ /* @__PURE__ */ jsx(CopyToClipboard, { text: mmdSource })
1326
+ ]
1327
+ }
1328
+ ) }),
1329
+ /* @__PURE__ */ jsx(
1330
+ Nt,
1331
+ {
1332
+ style: {
1333
+ width: 10
1334
+ }
1335
+ }
1336
+ ),
1337
+ /* @__PURE__ */ jsx(Ot, { children: /* @__PURE__ */ jsx(ScrollArea, { h: "100%", children: mmdSvg.result && /* @__PURE__ */ jsx(Box$1, { className: svgContainer, dangerouslySetInnerHTML: { __html: mmdSvg.result } }) }) })
1338
+ ]
1339
+ }
1340
+ ) });
1341
+ }
249
1342
  const Route$2 = createFileRoute("/_single/view/$viewId/mmd")({
250
1343
  component: Page$2,
251
1344
  staleTime: 1 / 0,
@@ -265,6 +1358,35 @@ function Page$2() {
265
1358
  const { viewId } = Route$2.useParams(), { source } = Route$2.useLoaderData();
266
1359
  return /* @__PURE__ */ jsx(ViewAsMmd, { viewId, mmdSource: source });
267
1360
  }
1361
+ function ViewAsDot({ dot, dotSvg }) {
1362
+ return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsxs(It, { className: viewWithTopPadding, children: [
1363
+ /* @__PURE__ */ jsx(Ot, { children: /* @__PURE__ */ jsxs(
1364
+ ScrollArea,
1365
+ {
1366
+ className: cssScrollArea,
1367
+ p: 5,
1368
+ styles: {
1369
+ viewport: {
1370
+ borderRadius: 6
1371
+ }
1372
+ },
1373
+ children: [
1374
+ /* @__PURE__ */ jsx(Code, { block: !0, className: cssCodeBlock, children: dot }),
1375
+ /* @__PURE__ */ jsx(CopyToClipboard, { text: dot })
1376
+ ]
1377
+ }
1378
+ ) }),
1379
+ /* @__PURE__ */ jsx(
1380
+ Nt,
1381
+ {
1382
+ style: {
1383
+ width: 10
1384
+ }
1385
+ }
1386
+ ),
1387
+ /* @__PURE__ */ jsx(Ot, { children: /* @__PURE__ */ jsx(ScrollArea, { h: "100%", children: /* @__PURE__ */ jsx("div", { className: svgContainer, dangerouslySetInnerHTML: { __html: dotSvg } }) }) })
1388
+ ] }) });
1389
+ }
268
1390
  const Route$1 = createFileRoute("/_single/view/$viewId/dot")({
269
1391
  component: Page$1,
270
1392
  staleTime: 1 / 0,
@@ -285,6 +1407,74 @@ function Page$1() {
285
1407
  const { dot, dotSvg } = Route$1.useLoaderData();
286
1408
  return /* @__PURE__ */ jsx(ViewAsDot, { dot, dotSvg });
287
1409
  }
1410
+ const fetchFromKroki = async (d2) => await (await fetch(krokiD2SvgUrl, {
1411
+ method: "POST",
1412
+ cache: "force-cache",
1413
+ body: JSON.stringify({
1414
+ diagram_source: d2,
1415
+ // diagram_options: {
1416
+ // theme: 'colorblind-clear'
1417
+ // },
1418
+ output_format: "svg"
1419
+ }),
1420
+ headers: {
1421
+ "Content-Type": "application/json"
1422
+ }
1423
+ })).text();
1424
+ function ViewAsD2({ d2Source }) {
1425
+ const [krokiSvg, { execute }] = useAsync(fetchFromKroki, null);
1426
+ return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsxs(
1427
+ It,
1428
+ {
1429
+ className: viewWithTopPadding,
1430
+ orientation: "horizontal",
1431
+ children: [
1432
+ /* @__PURE__ */ jsx(Ot, { children: /* @__PURE__ */ jsxs(
1433
+ ScrollArea,
1434
+ {
1435
+ className: cssScrollArea,
1436
+ p: 5,
1437
+ styles: {
1438
+ viewport: {
1439
+ borderRadius: 6
1440
+ }
1441
+ },
1442
+ children: [
1443
+ /* @__PURE__ */ jsx(Code, { block: !0, className: cssCodeBlock, children: d2Source }),
1444
+ /* @__PURE__ */ jsx(CopyToClipboard, { text: d2Source })
1445
+ ]
1446
+ }
1447
+ ) }),
1448
+ /* @__PURE__ */ jsx(
1449
+ Nt,
1450
+ {
1451
+ style: {
1452
+ width: 10
1453
+ }
1454
+ }
1455
+ ),
1456
+ /* @__PURE__ */ jsx(Ot, { children: /* @__PURE__ */ jsxs(ScrollArea, { h: "100%", children: [
1457
+ krokiSvg.status !== "success" && /* @__PURE__ */ jsxs(Fragment, { children: [
1458
+ /* @__PURE__ */ jsx(
1459
+ Button,
1460
+ {
1461
+ mt: "xs",
1462
+ variant: "light",
1463
+ disabled: krokiSvg.status === "loading",
1464
+ onClick: () => {
1465
+ execute(d2Source);
1466
+ },
1467
+ children: krokiSvg.status === "loading" ? "Loading..." : "Render with Kroki"
1468
+ }
1469
+ ),
1470
+ krokiSvg.status === "error" && /* @__PURE__ */ jsx(Box$1, { children: krokiSvg.error?.message })
1471
+ ] }),
1472
+ krokiSvg.status === "success" && /* @__PURE__ */ jsx(Box$1, { className: svgContainer, children: krokiSvg.result ? /* @__PURE__ */ jsx("div", { dangerouslySetInnerHTML: { __html: krokiSvg.result } }) : /* @__PURE__ */ jsx(Box$1, { children: "Empty result" }) })
1473
+ ] }) })
1474
+ ]
1475
+ }
1476
+ ) });
1477
+ }
288
1478
  const Route = createFileRoute("/_single/view/$viewId/d2")({
289
1479
  component: Page,
290
1480
  staleTime: 1 / 0,
@@ -305,7 +1495,14 @@ function Page() {
305
1495
  return /* @__PURE__ */ jsx(ViewAsD2, { d2Source: source });
306
1496
  }
307
1497
  export {
1498
+ AdHocViewEditor as A,
1499
+ ExportPage as E,
1500
+ Fallback as F,
1501
+ Header as H,
1502
+ LikeC4IconRendererContext as L,
1503
+ NotFound as N,
308
1504
  Route$b as R,
1505
+ ViewOutlet as V,
309
1506
  Route$a as a,
310
1507
  Route$9 as b,
311
1508
  Route$8 as c,
@@ -316,5 +1513,13 @@ export {
316
1513
  Route$3 as h,
317
1514
  Route$2 as i,
318
1515
  Route$1 as j,
319
- Route as k
1516
+ Route as k,
1517
+ LikeC4ModelContext as l,
1518
+ EmbedPage as m,
1519
+ ViewEditor as n,
1520
+ ViewReact as o,
1521
+ ViewAsPuml as p,
1522
+ ViewAsMmd as q,
1523
+ ViewAsDot as r,
1524
+ ViewAsD2 as s
320
1525
  };