likec4 1.50.0 → 1.52.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 (46) hide show
  1. package/__app__/src/ProjectsOverview.js +1 -1
  2. package/__app__/src/likec4.js +277 -164
  3. package/__app__/src/main.js +2 -56
  4. package/__app__/src/routes/index.js +85 -18
  5. package/__app__/src/routes/projects.js +1 -1
  6. package/__app__/src/routes/single.js +433 -53
  7. package/__app__/src/style.css +1 -1
  8. package/__app__/src/vendors.js +18646 -17381
  9. package/__app__/src/webcomponent.js +1 -1
  10. package/config/schema.json +1 -1
  11. package/dist/THIRD-PARTY-LICENSES.md +183 -285
  12. package/dist/_chunks/LikeC4.mjs +1 -1154
  13. package/dist/_chunks/filesystem.mjs +1229 -0
  14. package/dist/_chunks/index2.d.mts +49 -18
  15. package/dist/_chunks/libs/@hono/mcp.mjs +9 -9
  16. package/dist/_chunks/libs/@hono/node-server.mjs +1 -1
  17. package/dist/_chunks/libs/@logtape/logtape.mjs +1 -1
  18. package/dist/_chunks/libs/@modelcontextprotocol/sdk.d.mts +13 -13
  19. package/dist/_chunks/libs/ansi-align.mjs +2 -0
  20. package/dist/_chunks/libs/ansi-regex.mjs +1 -0
  21. package/dist/_chunks/libs/ansi-styles.mjs +1 -0
  22. package/dist/_chunks/libs/atomically.mjs +1 -1
  23. package/dist/_chunks/libs/boxen.d.mts +1 -0
  24. package/dist/_chunks/libs/boxen.mjs +22 -0
  25. package/dist/_chunks/libs/conf.mjs +1 -1
  26. package/dist/_chunks/libs/langium.d.mts +1 -1
  27. package/dist/_chunks/libs/langium.mjs +17 -17
  28. package/dist/_chunks/libs/remeda.mjs +2 -2
  29. package/dist/_chunks/libs/tinyrainbow.mjs +1 -1
  30. package/dist/_chunks/libs/vscode-languageserver.mjs +1 -0
  31. package/dist/_chunks/sequence.mjs +1 -1
  32. package/dist/_chunks/src.mjs +1 -1
  33. package/dist/_chunks/src2.mjs +64 -64
  34. package/dist/cli/index.mjs +84 -74
  35. package/dist/index.d.mts +836 -1
  36. package/dist/index.mjs +1 -1
  37. package/dist/vite-plugin/index.mjs +1 -1
  38. package/dist/vite-plugin/internal.mjs +1 -1
  39. package/package.json +23 -24
  40. package/react/index.d.mts +56 -2
  41. package/react/index.mjs +1575 -595
  42. package/dist/_chunks/binary.mjs +0 -72
  43. package/dist/_chunks/libs/@logtape/logtape.d.mts +0 -741
  44. package/dist/_chunks/libs/@msgpack/msgpack.mjs +0 -1
  45. package/dist/_chunks/libs/@smithy/is-array-buffer.mjs +0 -1
  46. package/dist/_chunks/libs/@smithy/util-base64.mjs +0 -1
@@ -1,13 +1,15 @@
1
1
  import { jsx, jsxs, Fragment } from "react/jsx-runtime";
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 Tt, dB as _t, bP as ScrollArea, dC as Ht, dD as notFound } from "../vendors.js";
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, k as useMantineColorScheme, dg as useComputedColorScheme, cY as useNavigate, aA as ActionIcon, c9 as useHotkeys, aI as AnimatePresence, d as deepEqual, s as shallowEqual, dh as useMatches, di as useIsomorphicLayoutEffect, a9 as r, dj as onMount, v as useStore$1, r as atom, bq as useTree, bs as Tree, b6 as HoverCard, b7 as HoverCardTarget, b8 as HoverCardDropdown, R as ThemeIcon, ch as useLocalStorage, dk as Drawer, bP as ScrollArea, S as SegmentedControl, d3 as useDocumentTitle, dl as Burger, dm as SimpleGrid, dn as useInViewport, dp as e$3, ci as Card, aQ as Box$1, d2 as redirect, aF as MotionDiv, cN as isValidMotionProp, aD as Menu, bc as MenuTarget, bd as MenuDropdown, be as MenuItem, dq as MenuDivider, bp as Stack, b5 as CopyButton$1, dr as Select, ds as ModalRoot, dt as ModalOverlay, du as ModalContent, dv as ModalBody, bL as Tabs, bM as TabsList, bN as TabsTab, bO as TabsPanel, dw as useMantineTheme, dx as useMediaQuery, cq as useDisclosure, aG as Divider, dy as MenuLabel, dz as useSearch, dA as LoadingOverlay, dB as toBlob, c$ as stripSearchParams, cZ as z, bS as useCallbackRef, aL as useIsMounted, T as Tooltip, dC as useAsync, dD as Tt, dE as _t, dF as Ht, dG as notFound } from "../vendors.js";
3
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";
4
+ import { c as css, s as styled, I as IconRendererProvider, a as LikeC4ModelProvider, b as IconMoonStars, d as IconSun, n as normalizeSearch, S as SearchContext, e as SearchControl, F as FramerMotionConfig, O as Overlay, f as bodyCss, g as dialogCss, h as SearchPanelContent, u as useUpdateEffect, i as useLikeC4Model, j as StaticLikeC4Diagram, B as Box, k as IconStarFilled, l as IconStack2, m as IconLayoutDashboard, o as IconFileCode, p as IconFolderOpen, q as IconFolderFilled, r as IconArrowLeft, N as NavigationPanel$1, M as Markdown, t as LikeC4AdHocViewEditor, v as createStyleContext, w as navigationPanel, x as isCssProperty, y as useLikeC4Projects$1, z as IconChevronDown, A as IconAlertTriangle, C as IconCheck, D as IconCopy, E as IconExternalLink, G as IconShare, H as pickViewBounds, J as LikeC4Diagram, K as useDiagramContext, P as useDiagram, Q as useOnDiagramEvent, R as LikeC4EditorProvider } from "../likec4.js";
5
5
  import { getProjectIcons } from "likec4:icons";
6
- import { useMemo, createContext, useContext, useState, useEffect, memo, useCallback, useRef } from "react";
6
+ import { useMemo, createContext, useContext, useRef, useEffect, useState, useCallback, memo } from "react";
7
7
  import { useStore } from "likec4/vite-plugin/internal";
8
8
  import { RichText } from "@likec4/core/types";
9
+ import { nonexhaustive, compareNatural } from "@likec4/core/utils";
9
10
  import { pageTitle, ComponentName, useHashHistory, isDevelopment, krokiPumlSvgUrl, krokiD2SvgUrl } from "../const.js";
10
11
  import { likec4rpc, isRpcAvailable } from "likec4:rpc";
12
+ import { R as Route$c, r as resolveForceColorScheme } from "./index.js";
11
13
  import { useLikeC4Projects } from "likec4:projects";
12
14
  function Fallback({ error: _error, resetErrorBoundary }) {
13
15
  const router = useRouter(), params = useParams({
@@ -197,6 +199,95 @@ function RouteComponent$1() {
197
199
  const { $likec4model, projectId } = Route$b.useLoaderData();
198
200
  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, {}) }) }) }) });
199
201
  }
202
+ function ColorSchemeToggle() {
203
+ const { setColorScheme } = useMantineColorScheme({
204
+ keepTransitions: !0
205
+ }), computedColorScheme = useComputedColorScheme("light"), navigate = useNavigate(), { theme: urlTheme } = Route$c.useSearch(), isForced = resolveForceColorScheme(urlTheme) != null, pendingScheme = useRef(null);
206
+ return useEffect(() => {
207
+ pendingScheme.current != null && !isForced && (setColorScheme(pendingScheme.current), pendingScheme.current = null);
208
+ }, [isForced, setColorScheme]), /* @__PURE__ */ jsxs(
209
+ ActionIcon,
210
+ {
211
+ size: "md",
212
+ variant: "subtle",
213
+ color: "gray",
214
+ onClick: () => {
215
+ if (pendingScheme.current != null) return;
216
+ const next = computedColorScheme === "light" ? "dark" : "light";
217
+ isForced ? (pendingScheme.current = next, navigate({
218
+ from: Route$c.fullPath,
219
+ search: (prev) => ({ ...prev, theme: void 0 }),
220
+ replace: !0
221
+ }).catch(() => {
222
+ pendingScheme.current = null;
223
+ })) : setColorScheme(next);
224
+ },
225
+ "aria-label": "Toggle color scheme",
226
+ children: [
227
+ /* @__PURE__ */ jsx(IconMoonStars, { stroke: 1.5, display: computedColorScheme === "light" ? "block" : "none" }),
228
+ /* @__PURE__ */ jsx(IconSun, { stroke: 1.5, display: computedColorScheme === "dark" ? "block" : "none" })
229
+ ]
230
+ }
231
+ );
232
+ }
233
+ function OverviewSearchAdapter({
234
+ onClose,
235
+ children
236
+ }) {
237
+ const [searchValue, setSearchValue] = useState(""), [pickViewFor, setPickViewFor] = useState(null), navigate = useNavigate(), navigateTo = useCallback((viewId, focusOnElement) => {
238
+ onClose(), navigate({
239
+ to: "/view/$viewId/",
240
+ params: { viewId },
241
+ search: (prev) => ({
242
+ ...prev,
243
+ ...focusOnElement && { focusOnElement }
244
+ })
245
+ });
246
+ }, [navigate, onClose]), openPickView = useCallback((elementFqn) => {
247
+ setPickViewFor(elementFqn);
248
+ }, []), closePickView = useCallback(() => {
249
+ setPickViewFor(null);
250
+ }, []), value = useMemo(() => ({
251
+ searchValue,
252
+ setSearchValue,
253
+ normalizedSearch: normalizeSearch(searchValue),
254
+ navigateTo,
255
+ openPickView,
256
+ closePickView,
257
+ pickViewFor,
258
+ close: onClose,
259
+ currentViewId: null,
260
+ openedWithSearch: null
261
+ }), [searchValue, pickViewFor, navigateTo, openPickView, closePickView, onClose]);
262
+ return /* @__PURE__ */ jsx(SearchContext.Provider, { value, children });
263
+ }
264
+ const OverviewSearch = memo(() => {
265
+ const [isOpened, setIsOpened] = useState(!1), open = useCallback(() => setIsOpened(!0), []), close = useCallback(() => setIsOpened(!1), []);
266
+ return useHotkeys([
267
+ ["mod+k", open, { preventDefault: !0 }]
268
+ ]), /* @__PURE__ */ jsxs(Fragment, { children: [
269
+ /* @__PURE__ */ jsx(SearchControl, { onClick: open }),
270
+ /* @__PURE__ */ jsx(FramerMotionConfig, { children: /* @__PURE__ */ jsx(AnimatePresence, { children: isOpened && /* @__PURE__ */ jsx(
271
+ Overlay,
272
+ {
273
+ fullscreen: !0,
274
+ withBackdrop: !1,
275
+ backdrop: {
276
+ opacity: 0.9
277
+ },
278
+ classes: {
279
+ dialog: dialogCss,
280
+ body: bodyCss
281
+ },
282
+ openDelay: 0,
283
+ onClose: close,
284
+ "data-likec4-search": "true",
285
+ children: /* @__PURE__ */ jsx(OverviewSearchAdapter, { onClose: close, children: /* @__PURE__ */ jsx(SearchPanelContent, {}) })
286
+ }
287
+ ) }) })
288
+ ] });
289
+ });
290
+ OverviewSearch.displayName = "OverviewSearch";
200
291
  function useTransparentBackground(enabled = !0) {
201
292
  useIsomorphicLayoutEffect(() => {
202
293
  const htmlEl = document.body.parentElement;
@@ -247,6 +338,251 @@ function useCurrentProject() {
247
338
  });
248
339
  return projects.find((p) => p.id === projectId) ?? projects[0];
249
340
  }
341
+ const isTreeNodeData = (node) => "type" in node && ["file", "folder", "view", "deployment-view"].includes(node.type);
342
+ function dropFilename(relativePath) {
343
+ return relativePath === "" ? "" : relativePath.split("/").slice(0, -1).join("/");
344
+ }
345
+ function compareTreeNodes(a, b) {
346
+ return a.children.length === 0 && b.children.length > 0 ? 1 : a.children.length > 0 && b.children.length === 0 ? -1 : compareNatural(a.label, b.label);
347
+ }
348
+ function buildDiagramTreeData(views, groupBy) {
349
+ const root2 = {
350
+ value: "",
351
+ label: "Diagrams",
352
+ type: "folder",
353
+ children: []
354
+ }, findParent = (path) => {
355
+ let parent = root2;
356
+ if (path === "")
357
+ return parent;
358
+ const segments = path.split("/"), traversed = ["@fs"];
359
+ for (; segments.length; ) {
360
+ const label = segments.shift();
361
+ traversed.push(label);
362
+ const value = traversed.join("/");
363
+ let node = r(parent.children, (n) => n.value === value);
364
+ node || (node = { label, value, type: "folder", children: [] }, parent.children.push(node)), parent = node;
365
+ }
366
+ return parent;
367
+ };
368
+ for (const view of views) {
369
+ let relativePath;
370
+ switch (groupBy) {
371
+ case "by-files":
372
+ relativePath = view.$view.sourcePath ?? "";
373
+ break;
374
+ case "by-folders":
375
+ relativePath = dropFilename(view.$view.sourcePath ?? "");
376
+ break;
377
+ case "none":
378
+ relativePath = "";
379
+ break;
380
+ default:
381
+ nonexhaustive(groupBy);
382
+ }
383
+ const parent = findParent(relativePath);
384
+ parent.children.push({
385
+ value: view.id,
386
+ label: view.title ?? view.id,
387
+ type: view.isDeploymentView() ? "deployment-view" : "view",
388
+ children: []
389
+ }), parent !== root2 && (parent.children.sort(compareTreeNodes), groupBy === "by-files" && parent.type !== "file" && (parent.type = "file"));
390
+ }
391
+ return root2.children.sort(compareTreeNodes);
392
+ }
393
+ function useDiagramsTreeData(groupBy = "by-files") {
394
+ const model = useLikeC4Model();
395
+ return useMemo(() => buildDiagramTreeData([...model.views()], groupBy), [model, groupBy]);
396
+ }
397
+ const drawerOpenedAtom = atom(!1);
398
+ onMount(drawerOpenedAtom, () => {
399
+ drawerOpenedAtom.set(!1);
400
+ });
401
+ const useDrawerOpened = () => useStore$1(drawerOpenedAtom), SidebarDrawerOps = {
402
+ open: () => drawerOpenedAtom.set(!0),
403
+ close: () => drawerOpenedAtom.set(!1)
404
+ }, isFile = (node) => isTreeNodeData(node) && node.type === "file", FolderIcon = ({ node, expanded }) => isFile(node) ? /* @__PURE__ */ jsx(ThemeIcon, { size: "sm", variant: "transparent", color: "indigo", children: /* @__PURE__ */ jsx(IconFileCode, { size: 16 }) }) : /* @__PURE__ */ jsx(ThemeIcon, { size: "sm", variant: "transparent", color: "violet", children: expanded ? /* @__PURE__ */ jsx(IconFolderOpen, { size: 16 }) : /* @__PURE__ */ jsx(IconFolderFilled, { size: 16 }) }), setHoveredNode = () => {
405
+ }, DiagramsTree = /* @__PURE__ */ memo(({ groupBy }) => {
406
+ const views = useLikeC4Views(), data = useDiagramsTreeData(groupBy), navigate = useNavigate(), navigateTo = (viewId2) => {
407
+ SidebarDrawerOps.close(), navigate({
408
+ to: "/view/$viewId/",
409
+ viewTransition: !1,
410
+ params: { viewId: viewId2 }
411
+ });
412
+ }, [diagram] = useCurrentView(), viewId = diagram?.id ?? null, tree = useTree({
413
+ multiple: !1
414
+ });
415
+ tree.setHoveredNode = setHoveredNode;
416
+ const sourcePath = diagram?.sourcePath ?? null;
417
+ useUpdateEffect(() => {
418
+ tree.collapseAllNodes();
419
+ }, [groupBy]), useEffect(() => {
420
+ if (sourcePath) {
421
+ const segments = sourcePath.split("/");
422
+ let path = "@fs";
423
+ for (const segment of segments)
424
+ path += `/${segment}`, tree.expand(path);
425
+ }
426
+ }, [sourcePath, groupBy]), useEffect(() => {
427
+ viewId && tree.select(viewId);
428
+ }, [viewId]);
429
+ const theme = useComputedColorScheme();
430
+ return /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsx(
431
+ Tree,
432
+ {
433
+ allowRangeSelection: !1,
434
+ tree,
435
+ data,
436
+ styles: {
437
+ node: {
438
+ marginTop: 2,
439
+ marginBottom: 2
440
+ }
441
+ },
442
+ levelOffset: "md",
443
+ renderNode: ({ node, selected, expanded, elementProps, hasChildren }) => /* @__PURE__ */ jsx(DiagramPreviewHoverCard, { diagram: hasChildren ? void 0 : views.find((v) => v.id === node.value), children: /* @__PURE__ */ jsx(
444
+ Button,
445
+ {
446
+ fullWidth: !0,
447
+ color: theme === "light" ? "dark" : "gray",
448
+ variant: selected ? "transparent" : "subtle",
449
+ size: "sm",
450
+ fz: "sm",
451
+ fw: hasChildren ? "600" : "500",
452
+ justify: "flex-start",
453
+ styles: {
454
+ section: {
455
+ opacity: 0.5
456
+ }
457
+ },
458
+ leftSection: /* @__PURE__ */ jsxs(Fragment, { children: [
459
+ !hasChildren && node.value === "index" && /* @__PURE__ */ jsx(IconStarFilled, { size: 14, opacity: 0.7 }),
460
+ !hasChildren && node.value !== "index" && isTreeNodeData(node) && /* @__PURE__ */ jsxs(Fragment, { children: [
461
+ node.type === "deployment-view" && /* @__PURE__ */ jsx(IconStack2, { size: 14 }),
462
+ node.type === "view" && /* @__PURE__ */ jsx(IconLayoutDashboard, { size: 14 })
463
+ ] }),
464
+ hasChildren && /* @__PURE__ */ jsx(FolderIcon, { node, expanded })
465
+ ] }),
466
+ ...elementProps,
467
+ ...!hasChildren && {
468
+ onClick: (e2) => {
469
+ e2.stopPropagation(), navigateTo(node.value);
470
+ }
471
+ },
472
+ children: node.label
473
+ }
474
+ ) })
475
+ }
476
+ ) });
477
+ }, (prev, next) => prev.groupBy === next.groupBy);
478
+ function DiagramPreviewHoverCard({ diagram, children }) {
479
+ const ratio = diagram ? Math.max(diagram.bounds.width / 400, diagram.bounds.height / 300) : 1, width = diagram ? Math.round(diagram.bounds.width / ratio) : 0, height = diagram ? Math.round(diagram.bounds.height / ratio) : 0;
480
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
481
+ diagram && /* @__PURE__ */ jsxs(HoverCard, { position: "right-start", openDelay: 400, closeDelay: 100, keepMounted: !1, shadow: "lg", children: [
482
+ /* @__PURE__ */ jsx(HoverCardTarget, { children }),
483
+ /* @__PURE__ */ jsx(HoverCardDropdown, { style: { width, height }, p: "xs", children: /* @__PURE__ */ jsx(DiagramPreview, { diagram }) })
484
+ ] }),
485
+ !diagram && children
486
+ ] });
487
+ }
488
+ const DiagramPreview = memo(({ diagram }) => {
489
+ const ratio = Math.max(diagram.bounds.width / 400, diagram.bounds.height / 300), width = Math.round(diagram.bounds.width / ratio), height = Math.round(diagram.bounds.height / ratio);
490
+ return /* @__PURE__ */ jsx(
491
+ StaticLikeC4Diagram,
492
+ {
493
+ view: diagram,
494
+ fitView: !0,
495
+ fitViewPadding: "4px",
496
+ enableElementDetails: !1,
497
+ reduceGraphics: !0,
498
+ initialWidth: width,
499
+ initialHeight: height
500
+ }
501
+ );
502
+ }, (prev, next) => prev.diagram.id === next.diagram.id), SidebarDrawer = memo(() => {
503
+ const opened = useDrawerOpened(), [grouping, setGrouping] = useLocalStorage({
504
+ key: "sidebar-drawer-grouping",
505
+ defaultValue: "by-files"
506
+ }), isSingleProject = useMatches({
507
+ select: (matches) => matches.some((match) => match.routeId === "/_single")
508
+ });
509
+ return /* @__PURE__ */ jsxs(
510
+ Drawer.Root,
511
+ {
512
+ keepMounted: isSingleProject,
513
+ opened,
514
+ scrollAreaComponent: ScrollArea.Autosize,
515
+ onClose: SidebarDrawerOps.close,
516
+ children: [
517
+ /* @__PURE__ */ jsx(Drawer.Overlay, { blur: 2 }),
518
+ /* @__PURE__ */ jsxs(Drawer.Content, { children: [
519
+ /* @__PURE__ */ jsxs(Drawer.Header, { children: [
520
+ /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
521
+ /* @__PURE__ */ jsx(
522
+ Button,
523
+ {
524
+ component: Link,
525
+ to: "/",
526
+ leftSection: /* @__PURE__ */ jsx(IconArrowLeft, { size: 14 }),
527
+ color: "dimmed",
528
+ variant: "subtle",
529
+ px: rem(5),
530
+ styles: {
531
+ section: {
532
+ marginInlineEnd: 4
533
+ }
534
+ },
535
+ size: "xs",
536
+ children: "Overview"
537
+ }
538
+ ),
539
+ /* @__PURE__ */ jsx(
540
+ SegmentedControl,
541
+ {
542
+ size: "xs",
543
+ withItemsBorders: !1,
544
+ value: grouping,
545
+ onChange: setGrouping,
546
+ data: [
547
+ { label: "By files", value: "by-files" },
548
+ { label: "By folders", value: "by-folders" },
549
+ { label: "List", value: "none" }
550
+ ]
551
+ }
552
+ ),
553
+ /* @__PURE__ */ jsx(
554
+ Button,
555
+ {
556
+ leftSection: /* @__PURE__ */ jsx(IconStarFilled, { size: 12, stroke: 2 }),
557
+ color: "dimmed",
558
+ variant: "subtle",
559
+ px: rem(5),
560
+ styles: {
561
+ section: {
562
+ marginInlineEnd: 4
563
+ }
564
+ },
565
+ size: "xs",
566
+ renderRoot: (props) => /* @__PURE__ */ jsx(
567
+ Link,
568
+ {
569
+ to: "/view/$viewId",
570
+ params: { viewId: "index" },
571
+ ...props
572
+ }
573
+ ),
574
+ children: "Open index"
575
+ }
576
+ )
577
+ ] }),
578
+ /* @__PURE__ */ jsx(Drawer.CloseButton, {})
579
+ ] }),
580
+ /* @__PURE__ */ jsx(Drawer.Body, { children: /* @__PURE__ */ jsx(DiagramsTree, { groupBy: grouping }) })
581
+ ] })
582
+ ]
583
+ }
584
+ );
585
+ });
250
586
  css({
251
587
  color: "text.dimmed"
252
588
  });
@@ -281,16 +617,54 @@ const previewBg = css({
281
617
  function RouteComponent() {
282
618
  useDocumentTitle(pageTitle);
283
619
  const views = useLikeC4Views();
284
- return /* @__PURE__ */ jsx(Container, { size: "xl", children: /* @__PURE__ */ jsx(
285
- SimpleGrid,
286
- {
287
- p: { base: "md", sm: "xl" },
288
- cols: { base: 1, sm: 2, md: 3, xl: 4 },
289
- spacing: { base: 10, sm: "xl" },
290
- verticalSpacing: { base: "md", sm: "xl" },
291
- children: views.map((v) => /* @__PURE__ */ jsx(ViewCard, { view: v }, v.id))
292
- }
293
- ) });
620
+ return /* @__PURE__ */ jsxs(Container, { size: "xl", children: [
621
+ /* @__PURE__ */ jsx(SidebarDrawer, {}),
622
+ /* @__PURE__ */ jsxs(
623
+ "div",
624
+ {
625
+ className: css({
626
+ containerName: "likec4-root",
627
+ containerType: "inline-size",
628
+ display: "flex",
629
+ alignItems: "flex-start",
630
+ justifyContent: "space-between",
631
+ padding: "xs",
632
+ gap: "xs",
633
+ position: "sticky",
634
+ top: "0",
635
+ zIndex: "10",
636
+ backgroundColor: "likec4.panel.bg/85",
637
+ backdropFilter: "blur(8px)"
638
+ }),
639
+ children: [
640
+ /* @__PURE__ */ jsx(NavigationPanel$1.Root, { css: { position: "relative", width: "max-content", margin: "0" }, children: /* @__PURE__ */ jsxs(NavigationPanel$1.Body, { children: [
641
+ /* @__PURE__ */ jsx("div", { style: { width: 0, height: 36 }, "aria-hidden": !0 }),
642
+ /* @__PURE__ */ jsx(Burger, { size: "sm", onClick: SidebarDrawerOps.open, "aria-label": "Toggle navigation" }),
643
+ /* @__PURE__ */ jsx(
644
+ NavigationPanel$1.Logo,
645
+ {
646
+ css: { flexShrink: 0 },
647
+ onClick: () => window.scrollTo({ top: 0, behavior: "smooth" })
648
+ }
649
+ ),
650
+ /* @__PURE__ */ jsx(OverviewSearch, {})
651
+ ] }) }),
652
+ /* @__PURE__ */ jsx(NavigationPanel$1.Root, { panelPosition: "right", css: { position: "relative", margin: "0" }, children: /* @__PURE__ */ jsx(NavigationPanel$1.Body, { children: /* @__PURE__ */ jsx("div", { style: { display: "flex", alignItems: "center", minHeight: 36 }, children: /* @__PURE__ */ jsx(ColorSchemeToggle, {}) }) }) })
653
+ ]
654
+ }
655
+ ),
656
+ /* @__PURE__ */ jsx(
657
+ SimpleGrid,
658
+ {
659
+ p: { base: "md", sm: "md" },
660
+ pt: { base: "sm", sm: "sm" },
661
+ cols: { base: 1, sm: 2, md: 3, xl: 4 },
662
+ spacing: { base: 10, sm: "xl" },
663
+ verticalSpacing: { base: "md", sm: "xl" },
664
+ children: views.map((v) => /* @__PURE__ */ jsx(ViewCard, { view: v }, v.id))
665
+ }
666
+ )
667
+ ] });
294
668
  }
295
669
  function ViewCard({ view }) {
296
670
  const [visible, setVisible] = useState(!1), { ref, inViewport } = useInViewport();
@@ -308,7 +682,7 @@ function ViewCard({ view }) {
308
682
  className: "group",
309
683
  withBorder: !0,
310
684
  children: [
311
- /* @__PURE__ */ jsx(Card.Section, { children: /* @__PURE__ */ jsx(Box, { className: previewBg, style: { height: 200 }, children: visible && /* @__PURE__ */ jsx(
685
+ /* @__PURE__ */ jsx(Card.Section, { children: /* @__PURE__ */ jsx(Box$1, { className: previewBg, style: { height: 200 }, children: visible && /* @__PURE__ */ jsx(
312
686
  StaticLikeC4Diagram,
313
687
  {
314
688
  background: "transparent",
@@ -409,7 +783,7 @@ function WebcomponentPage() {
409
783
  </html>
410
784
  `;
411
785
  return /* @__PURE__ */ jsx(
412
- Box$1,
786
+ Box,
413
787
  {
414
788
  css: {
415
789
  position: "fixed",
@@ -435,26 +809,6 @@ function WebcomponentPage() {
435
809
  }
436
810
  );
437
811
  }
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
812
  const { withProvider, withContext } = createStyleContext(navigationPanel), shouldForwardProp = (prop, variantKeys) => !variantKeys.includes(prop) && (isValidMotionProp(prop) || !isCssProperty(prop)), Root = withProvider(MotionDiv, "root", {
459
813
  shouldForwardProp
460
814
  }), Body = withContext(MotionDiv, "body", {
@@ -560,10 +914,10 @@ const AlertLocalhost = () => /* @__PURE__ */ jsx(
560
914
  `.trim();
561
915
  return /* @__PURE__ */ jsxs(Stack, { children: [
562
916
  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" }) }),
917
+ /* @__PURE__ */ jsx(Box$1, { children: /* @__PURE__ */ jsx(Text, { size: "sm", children: "Embedded view is an iframe with a static diagram" }) }),
564
918
  /* @__PURE__ */ jsxs(Stack, { gap: "xs", children: [
565
919
  /* @__PURE__ */ jsxs(Group, { justify: "space-between", children: [
566
- /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsx(Text, { fw: "500", size: "sm", children: "HTML" }) }),
920
+ /* @__PURE__ */ jsx(Box$1, { children: /* @__PURE__ */ jsx(Text, { fw: "500", size: "sm", children: "HTML" }) }),
567
921
  /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
568
922
  /* @__PURE__ */ jsx(ActionIcon, { component: "a", href, target: "_blank", variant: "light", color: "gray", children: /* @__PURE__ */ jsx(IconExternalLink, {}) }),
569
923
  /* @__PURE__ */ jsx(CopyButton$1, { value: code, timeout: 1500, children: CopyButtonChild })
@@ -571,7 +925,7 @@ const AlertLocalhost = () => /* @__PURE__ */ jsx(
571
925
  ] }),
572
926
  /* @__PURE__ */ jsx(Code, { block: !0, children: code }),
573
927
  /* @__PURE__ */ jsx(
574
- Box,
928
+ Box$1,
575
929
  {
576
930
  style: {
577
931
  alignSelf: "flex-start"
@@ -619,10 +973,10 @@ function WebcomponentsPanel({ diagram }) {
619
973
  );
620
974
  return /* @__PURE__ */ jsxs(Stack, { children: [
621
975
  jscode.includes("http://localhost") && /* @__PURE__ */ jsx(AlertLocalhost, {}),
622
- /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsx(Text, { size: "sm", children: "Add this script to your page:" }) }),
976
+ /* @__PURE__ */ jsx(Box$1, { children: /* @__PURE__ */ jsx(Text, { size: "sm", children: "Add this script to your page:" }) }),
623
977
  /* @__PURE__ */ jsxs(Stack, { gap: "xs", children: [
624
978
  /* @__PURE__ */ jsxs(Group, { justify: "space-between", children: [
625
- /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsx(Text, { fw: "500", size: "sm", children: "JavaScript" }) }),
979
+ /* @__PURE__ */ jsx(Box$1, { children: /* @__PURE__ */ jsx(Text, { fw: "500", size: "sm", children: "JavaScript" }) }),
626
980
  /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
627
981
  /* @__PURE__ */ jsx(ActionIcon, { component: "a", href: webcomponentPreview.href, target: "_blank", variant: "light", color: "gray", children: /* @__PURE__ */ jsx(IconExternalLink, {}) }),
628
982
  /* @__PURE__ */ jsx(
@@ -636,7 +990,7 @@ function WebcomponentsPanel({ diagram }) {
636
990
  ] })
637
991
  ] }),
638
992
  /* @__PURE__ */ jsx(Code, { block: !0, children: jscode }),
639
- /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsxs(Text, { size: "sm", c: "dimmed", children: [
993
+ /* @__PURE__ */ jsx(Box$1, { children: /* @__PURE__ */ jsxs(Text, { size: "sm", c: "dimmed", children: [
640
994
  "This script defines a custom element (webcomponent) that renders your diagrams.",
641
995
  /* @__PURE__ */ jsx("br", {}),
642
996
  "Script must be inserted once in the ",
@@ -649,8 +1003,8 @@ function WebcomponentsPanel({ diagram }) {
649
1003
  ] }),
650
1004
  /* @__PURE__ */ jsxs(Stack, { gap: "xs", children: [
651
1005
  /* @__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(
1006
+ /* @__PURE__ */ jsx(Box$1, { children: /* @__PURE__ */ jsx(Text, { fw: "500", size: "sm", children: "HTML" }) }),
1007
+ /* @__PURE__ */ jsx(Box$1, { children: /* @__PURE__ */ jsx(
654
1008
  CopyButton$1,
655
1009
  {
656
1010
  value: htmlCode,
@@ -660,7 +1014,7 @@ function WebcomponentsPanel({ diagram }) {
660
1014
  ) })
661
1015
  ] }),
662
1016
  /* @__PURE__ */ jsx(Code, { block: !0, children: htmlCode }),
663
- /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsxs(Text, { size: "sm", c: "dimmed", children: [
1017
+ /* @__PURE__ */ jsx(Box$1, { children: /* @__PURE__ */ jsxs(Text, { size: "sm", c: "dimmed", children: [
664
1018
  "Insert this code to your page. Page may have multiple ",
665
1019
  /* @__PURE__ */ jsx("code", { children: "<likec4-view>" }),
666
1020
  "."
@@ -890,7 +1244,7 @@ function GuardedExportPage({ diagram }) {
890
1244
  });
891
1245
  }, extraPadding = 16, width = bounds.width + padding * 2 + extraPadding, height = bounds.height + padding * 2 + extraPadding;
892
1246
  return /* @__PURE__ */ jsxs(
893
- Box$1,
1247
+ Box,
894
1248
  {
895
1249
  ref: viewportRef,
896
1250
  "data-testid": "export-page",
@@ -1065,7 +1419,8 @@ function ViewReact() {
1065
1419
  },
1066
1420
  children: [
1067
1421
  /* @__PURE__ */ jsx(ListenForDynamicVariantChange, {}),
1068
- /* @__PURE__ */ jsx(OpenRelationshipBrowserFromUrl, {})
1422
+ /* @__PURE__ */ jsx(OpenRelationshipBrowserFromUrl, {}),
1423
+ /* @__PURE__ */ jsx(FocusElementFromUrl, {})
1069
1424
  ]
1070
1425
  }
1071
1426
  );
@@ -1102,6 +1457,30 @@ function OpenRelationshipBrowserFromUrl() {
1102
1457
  process();
1103
1458
  }, [relationships]), null;
1104
1459
  }
1460
+ function FocusElementFromUrl() {
1461
+ const router = useRouter(), diagram = useDiagram(), { focusOnElement } = useSearch({
1462
+ from: "__root__"
1463
+ }), processedRef = useRef(null), focusAndClear = useCallbackRef((fqn) => {
1464
+ try {
1465
+ if (processedRef.current === fqn) return;
1466
+ processedRef.current = fqn, diagram.focusOnElement(fqn), router.buildAndCommitLocation({
1467
+ search: (s) => {
1468
+ const { focusOnElement: _, ...rest } = s;
1469
+ return rest;
1470
+ },
1471
+ replace: !0,
1472
+ viewTransition: !1
1473
+ });
1474
+ } catch (error) {
1475
+ console.error("focusOnElement failed:", error);
1476
+ }
1477
+ });
1478
+ return useOnDiagramEvent("initialized", () => {
1479
+ focusOnElement && processedRef.current !== focusOnElement && focusAndClear(focusOnElement);
1480
+ }), useUpdateEffect(() => {
1481
+ focusOnElement || (processedRef.current = null);
1482
+ }, [focusOnElement]), null;
1483
+ }
1105
1484
  function ListenForDynamicVariantChange() {
1106
1485
  const router = useRouter(), dynamicViewVariant = useDiagramContext((c) => c.dynamicViewVariant);
1107
1486
  return useUpdateEffect(() => {
@@ -1179,7 +1558,8 @@ function ViewEditor() {
1179
1558
  },
1180
1559
  children: [
1181
1560
  /* @__PURE__ */ jsx(ListenForDynamicVariantChange, {}),
1182
- /* @__PURE__ */ jsx(OpenRelationshipBrowserFromUrl, {})
1561
+ /* @__PURE__ */ jsx(OpenRelationshipBrowserFromUrl, {}),
1562
+ /* @__PURE__ */ jsx(FocusElementFromUrl, {})
1183
1563
  ]
1184
1564
  }
1185
1565
  )
@@ -1193,7 +1573,7 @@ function CopyButton({ text }) {
1193
1573
  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) } }) }) }) });
1194
1574
  }
1195
1575
  function CopyToClipboard({ text }) {
1196
- return /* @__PURE__ */ jsx(Box, { pos: "absolute", top: "0", right: "0", p: "4", children: /* @__PURE__ */ jsx(CopyButton, { text }) });
1576
+ return /* @__PURE__ */ jsx(Box$1, { pos: "absolute", top: "0", right: "0", p: "4", children: /* @__PURE__ */ jsx(CopyButton, { text }) });
1197
1577
  }
1198
1578
  const svgContainer = css({
1199
1579
  minWidth: 300,
@@ -1272,9 +1652,9 @@ function ViewAsPuml({ pumlSource }) {
1272
1652
  children: krokiSvg.status === "loading" ? "Loading..." : "Render with Kroki"
1273
1653
  }
1274
1654
  ),
1275
- krokiSvg.status === "error" && /* @__PURE__ */ jsx(Box$1, { children: krokiSvg.error?.message })
1655
+ krokiSvg.status === "error" && /* @__PURE__ */ jsx(Box, { children: krokiSvg.error?.message })
1276
1656
  ] }),
1277
- 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" }) })
1657
+ krokiSvg.status === "success" && /* @__PURE__ */ jsx(Box, { className: svgContainer, children: krokiSvg.result ? /* @__PURE__ */ jsx("div", { dangerouslySetInnerHTML: { __html: krokiSvg.result } }) : /* @__PURE__ */ jsx(Box, { children: "Empty result" }) })
1278
1658
  ] }) })
1279
1659
  ]
1280
1660
  }
@@ -1341,7 +1721,7 @@ function ViewAsMmd({ viewId, mmdSource }) {
1341
1721
  }
1342
1722
  }
1343
1723
  ),
1344
- /* @__PURE__ */ jsx(_t, { children: /* @__PURE__ */ jsx(ScrollArea, { h: "100%", children: mmdSvg.result && /* @__PURE__ */ jsx(Box$1, { className: svgContainer, dangerouslySetInnerHTML: { __html: mmdSvg.result } }) }) })
1724
+ /* @__PURE__ */ jsx(_t, { children: /* @__PURE__ */ jsx(ScrollArea, { h: "100%", children: mmdSvg.result && /* @__PURE__ */ jsx(Box, { className: svgContainer, dangerouslySetInnerHTML: { __html: mmdSvg.result } }) }) })
1345
1725
  ]
1346
1726
  }
1347
1727
  ) });
@@ -1474,9 +1854,9 @@ function ViewAsD2({ d2Source }) {
1474
1854
  children: krokiSvg.status === "loading" ? "Loading..." : "Render with Kroki"
1475
1855
  }
1476
1856
  ),
1477
- krokiSvg.status === "error" && /* @__PURE__ */ jsx(Box$1, { children: krokiSvg.error?.message })
1857
+ krokiSvg.status === "error" && /* @__PURE__ */ jsx(Box, { children: krokiSvg.error?.message })
1478
1858
  ] }),
1479
- 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" }) })
1859
+ krokiSvg.status === "success" && /* @__PURE__ */ jsx(Box, { className: svgContainer, children: krokiSvg.result ? /* @__PURE__ */ jsx("div", { dangerouslySetInnerHTML: { __html: krokiSvg.result } }) : /* @__PURE__ */ jsx(Box, { children: "Empty result" }) })
1480
1860
  ] }) })
1481
1861
  ]
1482
1862
  }