@elevasis/ui 2.30.0 → 2.31.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 (109) hide show
  1. package/dist/{CoreAuthKitInner-QC62UHTZ.js → CoreAuthKitInner-KSEGSB67.js} +1 -1
  2. package/dist/api/index.js +3 -3
  3. package/dist/app/index.d.ts +122 -1
  4. package/dist/app/index.js +7 -7
  5. package/dist/auth/context.js +1 -1
  6. package/dist/auth/index.js +1 -1
  7. package/dist/charts/index.js +4 -4
  8. package/dist/{chunk-T5Z7G2J2.js → chunk-3BAPR3KA.js} +1 -1
  9. package/dist/{chunk-4VQ2PXMI.js → chunk-3FV6HBXS.js} +4 -4
  10. package/dist/{chunk-2DIYILF7.js → chunk-542WPQU2.js} +2 -2
  11. package/dist/{chunk-SBCIB5TZ.js → chunk-5LJAEZMA.js} +5 -5
  12. package/dist/{chunk-T2PAD63Y.js → chunk-7HMCB26R.js} +1 -1
  13. package/dist/chunk-7KC4P3AU.js +357 -0
  14. package/dist/{chunk-AKOD52HS.js → chunk-CQZ3DNQY.js} +4 -3
  15. package/dist/{chunk-I2KLQ2HA.js → chunk-DZTG5IAC.js} +7 -1
  16. package/dist/{chunk-JCGD4GM6.js → chunk-GRDLB6LM.js} +1 -0
  17. package/dist/{chunk-JKTPRYGV.js → chunk-HQGF4ATG.js} +9 -55
  18. package/dist/{chunk-SKXXT3E2.js → chunk-HYNYEBHM.js} +4 -4
  19. package/dist/{chunk-6EFVZV6X.js → chunk-JKSUN5GN.js} +718 -64
  20. package/dist/{chunk-LRZFLK2F.js → chunk-L2NVFLXU.js} +3 -3
  21. package/dist/{chunk-6WXDE5LZ.js → chunk-L3BVJWML.js} +1 -1
  22. package/dist/{chunk-3MDNBHVB.js → chunk-MVFCLZSK.js} +690 -221
  23. package/dist/{chunk-HYLERWRO.js → chunk-ND42LPY4.js} +6 -6
  24. package/dist/{chunk-CLUP5H3C.js → chunk-O2QOPJI5.js} +360 -126
  25. package/dist/{chunk-X2SUMO3P.js → chunk-P55BJZZW.js} +2 -1
  26. package/dist/{chunk-4FZYEEPK.js → chunk-Q6OYNEGR.js} +5 -5
  27. package/dist/{chunk-4SY6BTVZ.js → chunk-QDEETKYT.js} +4 -1
  28. package/dist/{chunk-IKQ42WHU.js → chunk-QHEWXU7I.js} +1 -1
  29. package/dist/chunk-R2XR4FCV.js +48 -0
  30. package/dist/chunk-R66W5UDG.js +26 -0
  31. package/dist/{chunk-3GV5NHSS.js → chunk-SHQXMW4F.js} +39 -211
  32. package/dist/{chunk-A7B7HLDF.js → chunk-T3IPHEYJ.js} +1889 -301
  33. package/dist/{chunk-7E3FUTND.js → chunk-TOIXUWR6.js} +1 -1
  34. package/dist/{chunk-NITGGYH2.js → chunk-TVRQ6AQI.js} +1 -1
  35. package/dist/{chunk-CN2HC4D4.js → chunk-UFTM5SZZ.js} +2 -2
  36. package/dist/{chunk-KVJ3LFH2.js → chunk-VNFR57DF.js} +4 -24
  37. package/dist/{chunk-P5WYW2GI.js → chunk-Y4FWCG7Y.js} +150 -305
  38. package/dist/components/chat/index.js +1 -1
  39. package/dist/components/index.d.ts +205 -11
  40. package/dist/components/index.js +38 -35
  41. package/dist/components/navigation/index.js +1 -1
  42. package/dist/execution/index.d.ts +2 -1
  43. package/dist/execution/index.js +1 -1
  44. package/dist/features/auth/index.d.ts +121 -0
  45. package/dist/features/auth/index.js +1 -1
  46. package/dist/features/clients/index.css +611 -0
  47. package/dist/features/clients/index.d.ts +86 -0
  48. package/dist/features/clients/index.js +719 -0
  49. package/dist/features/crm/index.d.ts +148 -2
  50. package/dist/features/crm/index.js +18 -16
  51. package/dist/features/dashboard/index.d.ts +36 -1
  52. package/dist/features/dashboard/index.js +14 -14
  53. package/dist/features/delivery/index.d.ts +121 -0
  54. package/dist/features/delivery/index.js +18 -16
  55. package/dist/features/knowledge/index.js +43 -20
  56. package/dist/features/lead-gen/index.d.ts +17 -11
  57. package/dist/features/lead-gen/index.js +19 -17
  58. package/dist/features/monitoring/index.js +17 -16
  59. package/dist/features/monitoring/requests/index.js +14 -13
  60. package/dist/features/operations/index.d.ts +38 -2
  61. package/dist/features/operations/index.js +22 -21
  62. package/dist/features/seo/index.js +1 -1
  63. package/dist/features/settings/index.d.ts +121 -0
  64. package/dist/features/settings/index.js +16 -15
  65. package/dist/graph/index.js +1 -1
  66. package/dist/hooks/delivery/index.d.ts +140 -0
  67. package/dist/hooks/delivery/index.js +3 -3
  68. package/dist/hooks/index.d.ts +583 -19
  69. package/dist/hooks/index.js +12 -12
  70. package/dist/hooks/operations/command-view/utils/transformCommandViewData.d.ts +82 -1
  71. package/dist/hooks/operations/command-view/utils/transformCommandViewData.js +1 -1
  72. package/dist/hooks/published.d.ts +583 -19
  73. package/dist/hooks/published.js +12 -12
  74. package/dist/index.d.ts +680 -21
  75. package/dist/index.js +13 -13
  76. package/dist/initialization/index.d.ts +121 -0
  77. package/dist/initialization/index.js +1 -1
  78. package/dist/knowledge/index.d.ts +97 -1
  79. package/dist/knowledge/index.js +1692 -1039
  80. package/dist/layout/index.d.ts +6 -0
  81. package/dist/layout/index.js +4 -4
  82. package/dist/organization/index.js +1 -1
  83. package/dist/profile/index.d.ts +121 -0
  84. package/dist/profile/index.js +1 -1
  85. package/dist/provider/ElevasisServiceContext.js +1 -1
  86. package/dist/provider/index.d.ts +218 -2
  87. package/dist/provider/index.js +10 -10
  88. package/dist/provider/published.d.ts +218 -2
  89. package/dist/provider/published.js +7 -7
  90. package/dist/router/context.js +1 -1
  91. package/dist/router/index.js +1 -1
  92. package/dist/sse/index.js +1 -1
  93. package/dist/supabase/index.d.ts +232 -0
  94. package/dist/supabase/index.js +1 -1
  95. package/dist/test-utils/index.js +3 -3
  96. package/dist/test-utils/setup-integration.js +1 -1
  97. package/dist/test-utils/setup.js +2 -2
  98. package/dist/theme/index.js +4 -4
  99. package/dist/theme/presets/index.js +2 -2
  100. package/dist/typeform/index.js +1 -1
  101. package/dist/typeform/schemas.js +1 -1
  102. package/dist/types/index.d.ts +204 -1
  103. package/dist/utils/index.d.ts +36 -1
  104. package/dist/utils/index.js +2 -2
  105. package/dist/vite/index.js +3 -3
  106. package/dist/vite-plugin-knowledge/index.js +2 -2
  107. package/dist/zustand/index.js +1 -1
  108. package/package.json +13 -4
  109. /package/dist/{chunk-HXZQWMKE.js → chunk-XQHZBA65.js} +0 -0
@@ -1,35 +1,36 @@
1
1
  import { ChatHeader, ChatSidebar } from './chunk-CXY7FMUM.js';
2
- import { getOrganizationGraphLensConfig, useOrganizationGraphFilters, createOrganizationGraphFilters, buildOrganizationGraph, filterOrganizationGraph, getCommandViewVisibilityProjection, resolveOrganizationGraphPathTrace, getCommandViewOperationalOverview, getCommandViewSelectionOperationalSummary, buildCommandViewDrillDownSections, expandAroundGraph, getConnectedHiddenResourceIds, FilterPanel, OrganizationGraphDetailPanel, ExpandAroundPanel, CommandViewSidebarContent, getOrganizationGraphTraceNodeOptions, formatOrganizationGraphTraceNodeOptionLabel } from './chunk-3MDNBHVB.js';
2
+ import { getOrganizationGraphLensConfig, useOrganizationGraphFilters, createOrganizationGraphFilters, buildOrganizationGraph, filterOrganizationGraph, getCommandViewVisibilityProjection, resolveOrganizationGraphPathTrace, getCommandViewOperationalOverview, getCommandViewSelectionOperationalSummary, buildCommandViewDrillDownSections, expandAroundGraph, getConnectedHiddenResourceIds, FilterPanel, OrganizationGraphDetailPanel, ExpandAroundPanel, CommandViewSidebarContent, getOrganizationGraphTraceNodeOptions, formatOrganizationGraphTraceNodeOptionLabel } from './chunk-MVFCLZSK.js';
3
3
  import { SubshellLoader } from './chunk-JDNEWB5F.js';
4
- import { resolveSemanticIconComponent } from './chunk-JCGD4GM6.js';
4
+ import { resolveSemanticIconComponent } from './chunk-GRDLB6LM.js';
5
5
  import { SubshellSidebarLoader } from './chunk-XQQEKWTL.js';
6
- import { BaseNode, useGraphTheme, BaseEdge, ExecutionStats, UnifiedWorkflowGraph, WorkflowExecutionTimeline, AgentExecutionVisualizer, AgentExecutionTimeline, GraphBackground, GraphFitViewButton, GraphFitViewHandler } from './chunk-4FZYEEPK.js';
6
+ import { BaseNode, useGraphTheme, BaseEdge, ExecutionStats, UnifiedWorkflowGraph, WorkflowExecutionTimeline, AgentExecutionVisualizer, AgentExecutionTimeline, GraphBackground, GraphFitViewButton, GraphFitViewHandler } from './chunk-Q6OYNEGR.js';
7
7
  import { FormFieldRenderer } from './chunk-3MEXPLWT.js';
8
- import { ResourceHealthPanel } from './chunk-LRZFLK2F.js';
8
+ import { ResourceHealthPanel } from './chunk-L2NVFLXU.js';
9
9
  import { useCyberColors, CyberDonut } from './chunk-CW3UNAF2.js';
10
+ import { ConfirmationModal } from './chunk-VNFR57DF.js';
11
+ import { SubshellSidebarSection } from './chunk-IIMU5YAJ.js';
10
12
  import { AppShellLoader } from './chunk-RYTEQBAO.js';
11
13
  import { PageContainer } from './chunk-BZZCNLT6.js';
12
- import { SubshellSidebarSection } from './chunk-IIMU5YAJ.js';
13
- import { CustomModal, ConfirmationModal } from './chunk-KVJ3LFH2.js';
14
+ import { CustomModal } from './chunk-R66W5UDG.js';
14
15
  import { useGraphHighlighting, calculateGraphHeight, Graph_module_css_default, GRAPH_CONSTANTS } from './chunk-22UVE3RA.js';
15
16
  import { getResourceStatusColor, useTimelineData, useAgentIterationData, getStatusIcon } from './chunk-E4WQGJNS.js';
16
- import { useErrorDetail, useExecution, useArchivedLogs, useDeleteExecution, useRetryExecution, useCancelExecution, useCommandQueueTotals, useStatusFilter, useResourceSearch, useResourcesDomainFilters, usePaginationState, useResources, useRecentExecutionsByResource, filterByDomainFilters, collectResourceFilterFacets, useResourceDefinition, isSessionCapable, useDeleteTask, useCommandQueue, useSubmitAction, useCommandViewData, useCommandViewStats, useCommandViewStore, useResourceExecutions, useCheckpointTasks, COMMAND_VIEW_VISUALIZATION_MODES, useExecutionPanelState, useDeleteSession, useCreateSession, useSessions, useSessionExecutions, useSession, useBulkDeleteExecutions, getCommandViewGraphPositions } from './chunk-6EFVZV6X.js';
17
- import { showApiErrorNotification, showSuccessNotification } from './chunk-T2PAD63Y.js';
17
+ import { useErrorDetail, useExecution, useArchivedLogs, useDeleteExecution, useRetryExecution, useCancelExecution, useCommandQueueTotals, useStatusFilter, useResourceSearch, useResourcesDomainFilters, usePaginationState, useResources, useRecentExecutionsByResource, filterByDomainFilters, collectResourceFilterFacets, useResourceDefinition, isSessionCapable, useDeleteTask, useCommandQueue, useSubmitAction, useCommandViewData, useCommandViewStats, useCommandViewStore, getDefaultParameters, useResourceExecutions, useCheckpointTasks, EXPLORE_OM_ROOT_ID, COMMAND_VIEW_VISUALIZATION_MODES, useExecutionPanelState, useDeleteSession, useCreateSession, useSessions, useSessionExecutions, useSession, buildCommandViewGraphIndex, getCommandViewClusterZoneLabels, getCommandViewSwimlaneLaneLabels, exploreNodeHasChildren, getNextExpandedSetForToggle, getExploreProjection, getRadialFromAnchorProjection, DOMAIN_ZONE_TINT_COLOR, useBulkDeleteExecutions, getCommandViewNodeDomain, getCommandViewGraphPositions, getExploreFocusSet, DEFAULT_CLUSTER_PARAMETERS, DEFAULT_SWIMLANE_PARAMETERS, DEFAULT_FOCUS_PARAMETERS, DEFAULT_NETWORK_PARAMETERS, DEFAULT_FORCE_PARAMETERS } from './chunk-JKSUN5GN.js';
18
+ import { showApiErrorNotification, showSuccessNotification } from './chunk-7HMCB26R.js';
18
19
  import { useMergedExecution } from './chunk-3ZMAGTWF.js';
19
- import { JsonViewer, CardHeader, PageTitleCaption, CollapsibleSection, TabCountBadge, ResourceCard, ContextViewer, EmptyState, APIErrorAlert } from './chunk-6WXDE5LZ.js';
20
- import { StyledMarkdown } from './chunk-3KMDHCAR.js';
21
20
  import { useOptionalElevasisFeatures, useElevasisFeatures } from './chunk-6IXOKUBC.js';
21
+ import { JsonViewer, CardHeader, PageTitleCaption, CollapsibleSection, TabCountBadge, ResourceCard, ContextViewer, EmptyState, APIErrorAlert } from './chunk-L3BVJWML.js';
22
+ import { StyledMarkdown } from './chunk-3KMDHCAR.js';
22
23
  import { SubshellContentContainer } from './chunk-TKAYX2SP.js';
23
- import { NavigationButton } from './chunk-NYBEU5TE.js';
24
24
  import { useRouterContext } from './chunk-Q7DJKLEN.js';
25
25
  import { useAppearance } from './chunk-E565XMTQ.js';
26
26
  import { topbarHeight } from './chunk-DT3QYZVU.js';
27
- import { getErrorInfo, formatErrorMessage, getResourceIcon, formatRelativeTime, PAGE_SIZE_DEFAULT, debounce } from './chunk-HXZQWMKE.js';
27
+ import { getErrorInfo, formatErrorMessage, getResourceIcon, formatRelativeTime, PAGE_SIZE_DEFAULT, debounce } from './chunk-XQHZBA65.js';
28
28
  import { ResourceStatusColors, toWorkflowLogMessages } from './chunk-KRWALB24.js';
29
29
  import { useInitialization } from './chunk-533DUEQY.js';
30
30
  import { useOrganization } from './chunk-DD3CCMCZ.js';
31
- import { Stack, Group, Text, Badge, Title, Textarea, Alert, Button, ActionIcon, Collapse, Card, ThemeIcon, SimpleGrid, Divider, Paper, Space, CopyButton, Center, Tooltip, Code, Menu, useMantineTheme, UnstyledButton, Select, RangeSlider, Box, Progress, Tabs, Pagination, TextInput, Loader, SegmentedControl, Modal, LoadingOverlay, ScrollArea } from '@mantine/core';
32
- import { IconBrain, IconFileText, IconMail, IconSend, IconClock, IconArrowUp, IconMessageCircle, IconRocket, IconEye, IconEdit, IconAlertTriangle, IconRefresh, IconX, IconCheck, IconCode, IconAlertCircle, IconChevronRight, IconTool, IconSettings, IconCpu, IconClockHour4, IconVersions, IconNetwork, IconSitemap, IconCopy, IconPlayerStop, IconReload, IconTrash, IconTerminal2, IconBug, IconChevronDown, IconPlayerPlay, IconMessage, IconArrowLeft, IconRobot, IconGitBranch, IconDotsVertical, IconFilter, IconCircleCheck, IconCategory, IconDatabase, IconApps, IconRoute, IconAdjustmentsHorizontal, IconSearch, IconCircleX, IconCircleDashed, IconExternalLink, IconFolders, IconBraces, IconBolt, IconTopologyStar3, IconInfoCircle, IconPlus, IconLayoutSidebarRightExpand, IconNote, IconArchive, IconDownload, IconTimeline, IconActivityHeartbeat, IconClockPause, IconArrowsMaximize, IconHistory } from '@tabler/icons-react';
31
+ import { __require } from './chunk-DZTG5IAC.js';
32
+ import { Stack, Group, Text, Badge, Title, Textarea, Alert, Button, ActionIcon, Collapse, Card, ThemeIcon, SimpleGrid, Divider, Paper, Space, CopyButton, Center, Tooltip, Code, Menu, useMantineTheme, UnstyledButton, Select, RangeSlider, Box, Progress, Tabs, Pagination, TextInput, Loader, SegmentedControl, Modal, LoadingOverlay, ScrollArea, Popover, Breadcrumbs, Anchor, NumberInput } from '@mantine/core';
33
+ import { IconBrain, IconFileText, IconMail, IconSend, IconClock, IconArrowUp, IconMessageCircle, IconRocket, IconEye, IconEdit, IconAlertTriangle, IconRefresh, IconX, IconCheck, IconCode, IconAlertCircle, IconChevronRight, IconTool, IconSettings, IconCpu, IconClockHour4, IconVersions, IconNetwork, IconSitemap, IconCopy, IconPlayerStop, IconReload, IconTrash, IconTerminal2, IconBug, IconChevronDown, IconPlayerPlay, IconMessage, IconArrowLeft, IconRobot, IconGitBranch, IconDotsVertical, IconFilter, IconCircleCheck, IconCategory, IconDatabase, IconApps, IconRoute, IconAdjustmentsHorizontal, IconSearch, IconCircleX, IconCircleDashed, IconExternalLink, IconFolders, IconBraces, IconBolt, IconTopologyStar3, IconInfoCircle, IconPlus, IconLayoutSidebarRightExpand, IconNote, IconArchive, IconDownload, IconTimeline, IconActivityHeartbeat, IconClockPause, IconArrowsMaximize, IconAdjustments, IconHistory } from '@tabler/icons-react';
33
34
  import { useForm } from '@mantine/form';
34
35
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
35
36
  import { memo, useState, useMemo, useRef, useCallback, useEffect, useDeferredValue, useEffectEvent } from 'react';
@@ -3372,6 +3373,52 @@ function CommandQueueDetailPage({
3372
3373
  ] }) })
3373
3374
  ] });
3374
3375
  }
3376
+ var forceLayoutAvailable = false;
3377
+ try {
3378
+ const fcose = __require("cytoscape-fcose");
3379
+ cytoscape.use(fcose.default ?? fcose);
3380
+ forceLayoutAvailable = true;
3381
+ } catch {
3382
+ }
3383
+ function useForceLayoutController(parameters) {
3384
+ const available = forceLayoutAvailable;
3385
+ const runLayout = useCallback(
3386
+ (cy) => {
3387
+ if (!available) return;
3388
+ const p = parameters ?? { nodeRepulsion: 4500, idealEdgeLength: 80, gravity: 0.25 };
3389
+ cy.layout({
3390
+ name: "fcose",
3391
+ animate: true,
3392
+ animationDuration: 600,
3393
+ randomize: false,
3394
+ nodeRepulsion: p.nodeRepulsion,
3395
+ idealEdgeLength: p.idealEdgeLength,
3396
+ gravity: p.gravity,
3397
+ fit: true,
3398
+ padding: 44
3399
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
3400
+ }).run();
3401
+ },
3402
+ // parameters is intentionally in the dep array so runLayout re-captures fresh values on every render.
3403
+ [available, parameters]
3404
+ );
3405
+ const alertElement = !available && parameters !== null ? /* @__PURE__ */ jsx(
3406
+ Box,
3407
+ {
3408
+ style: {
3409
+ position: "absolute",
3410
+ inset: 0,
3411
+ zIndex: 10,
3412
+ display: "flex",
3413
+ alignItems: "center",
3414
+ justifyContent: "center",
3415
+ pointerEvents: "none"
3416
+ },
3417
+ children: /* @__PURE__ */ jsx(Alert, { color: "yellow", maw: 420, style: { pointerEvents: "auto" }, children: "Force layout unavailable \u2014 `cytoscape-fcose` is not installed. Pick another mode or install the optional dependency." })
3418
+ }
3419
+ ) : null;
3420
+ return { available, runLayout, alertElement };
3421
+ }
3375
3422
  var cellStyle = {
3376
3423
  padding: "var(--mantine-spacing-sm) var(--mantine-spacing-md)",
3377
3424
  display: "flex",
@@ -3441,6 +3488,449 @@ function CommandViewHealthStrip({
3441
3488
  ] })
3442
3489
  ] });
3443
3490
  }
3491
+ var DOMAIN_LABELS = {
3492
+ operations: "Operations",
3493
+ runtime: "Runtime",
3494
+ business: "Business",
3495
+ delivery: "Delivery",
3496
+ knowledge: "Knowledge",
3497
+ admin: "Admin",
3498
+ system: "System",
3499
+ platform: "Platform",
3500
+ other: "Other"
3501
+ };
3502
+ var DOMAIN_ORDER = [
3503
+ "platform",
3504
+ "operations",
3505
+ "runtime",
3506
+ "business",
3507
+ "delivery",
3508
+ "knowledge",
3509
+ "admin",
3510
+ "system",
3511
+ "other"
3512
+ ];
3513
+ function ClusterLegend() {
3514
+ return /* @__PURE__ */ jsxs(Stack, { gap: 4, children: [
3515
+ /* @__PURE__ */ jsx(Text, { size: "xs", fw: 600, c: "dimmed", tt: "uppercase", children: "Domain zones" }),
3516
+ /* @__PURE__ */ jsx(Group, { gap: "xs", wrap: "wrap", children: DOMAIN_ORDER.map((domain) => {
3517
+ const tint = DOMAIN_ZONE_TINT_COLOR[domain] ?? "#868e96";
3518
+ return /* @__PURE__ */ jsxs(Group, { gap: 4, wrap: "nowrap", children: [
3519
+ /* @__PURE__ */ jsx(
3520
+ Box,
3521
+ {
3522
+ style: {
3523
+ width: 10,
3524
+ height: 10,
3525
+ borderRadius: 2,
3526
+ backgroundColor: tint,
3527
+ opacity: 0.7,
3528
+ flexShrink: 0
3529
+ }
3530
+ }
3531
+ ),
3532
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: DOMAIN_LABELS[domain] ?? domain })
3533
+ ] }, domain);
3534
+ }) })
3535
+ ] });
3536
+ }
3537
+ function SwimlaneLegend() {
3538
+ return /* @__PURE__ */ jsxs(Stack, { gap: 4, children: [
3539
+ /* @__PURE__ */ jsx(Text, { size: "xs", fw: 600, c: "dimmed", tt: "uppercase", children: "Domain lanes" }),
3540
+ /* @__PURE__ */ jsx(Stack, { gap: 2, children: DOMAIN_ORDER.map((domain) => {
3541
+ const tint = DOMAIN_ZONE_TINT_COLOR[domain] ?? "#868e96";
3542
+ return /* @__PURE__ */ jsxs(Group, { gap: 6, wrap: "nowrap", children: [
3543
+ /* @__PURE__ */ jsx(
3544
+ Box,
3545
+ {
3546
+ style: {
3547
+ width: 16,
3548
+ height: 2,
3549
+ borderRadius: 1,
3550
+ backgroundColor: tint,
3551
+ opacity: 0.7,
3552
+ flexShrink: 0
3553
+ }
3554
+ }
3555
+ ),
3556
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: DOMAIN_LABELS[domain] ?? domain })
3557
+ ] }, domain);
3558
+ }) })
3559
+ ] });
3560
+ }
3561
+ function FocusLegend() {
3562
+ return /* @__PURE__ */ jsxs(Stack, { gap: 4, children: [
3563
+ /* @__PURE__ */ jsx(Text, { size: "xs", fw: 600, c: "dimmed", tt: "uppercase", children: "Focus rings" }),
3564
+ /* @__PURE__ */ jsxs(Stack, { gap: 2, children: [
3565
+ /* @__PURE__ */ jsxs(Group, { gap: 6, wrap: "nowrap", children: [
3566
+ /* @__PURE__ */ jsx(
3567
+ Box,
3568
+ {
3569
+ style: {
3570
+ width: 10,
3571
+ height: 10,
3572
+ borderRadius: "50%",
3573
+ border: "2px solid var(--mantine-color-blue-5)",
3574
+ flexShrink: 0
3575
+ }
3576
+ }
3577
+ ),
3578
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "Selected node (center)" })
3579
+ ] }),
3580
+ /* @__PURE__ */ jsxs(Group, { gap: 6, wrap: "nowrap", children: [
3581
+ /* @__PURE__ */ jsx(
3582
+ Box,
3583
+ {
3584
+ style: {
3585
+ width: 10,
3586
+ height: 10,
3587
+ borderRadius: "50%",
3588
+ border: "2px solid var(--mantine-color-teal-5)",
3589
+ flexShrink: 0
3590
+ }
3591
+ }
3592
+ ),
3593
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "Ring 1 \u2014 direct neighbors" })
3594
+ ] }),
3595
+ /* @__PURE__ */ jsxs(Group, { gap: 6, wrap: "nowrap", children: [
3596
+ /* @__PURE__ */ jsx(
3597
+ Box,
3598
+ {
3599
+ style: {
3600
+ width: 10,
3601
+ height: 10,
3602
+ borderRadius: "50%",
3603
+ border: "2px solid var(--mantine-color-gray-5)",
3604
+ flexShrink: 0
3605
+ }
3606
+ }
3607
+ ),
3608
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "Ring 2 \u2014 second-degree neighbors" })
3609
+ ] })
3610
+ ] })
3611
+ ] });
3612
+ }
3613
+ function NetworkLegend() {
3614
+ return /* @__PURE__ */ jsxs(Stack, { gap: 4, children: [
3615
+ /* @__PURE__ */ jsx(Text, { size: "xs", fw: 600, c: "dimmed", tt: "uppercase", children: "Network layout" }),
3616
+ /* @__PURE__ */ jsxs(Stack, { gap: 2, children: [
3617
+ /* @__PURE__ */ jsxs(Group, { gap: 6, wrap: "nowrap", children: [
3618
+ /* @__PURE__ */ jsx(
3619
+ Box,
3620
+ {
3621
+ style: {
3622
+ width: 10,
3623
+ height: 10,
3624
+ borderRadius: "50%",
3625
+ border: "2px solid var(--mantine-color-blue-5)",
3626
+ flexShrink: 0
3627
+ }
3628
+ }
3629
+ ),
3630
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "Highest-degree node (center)" })
3631
+ ] }),
3632
+ /* @__PURE__ */ jsxs(Group, { gap: 6, wrap: "nowrap", children: [
3633
+ /* @__PURE__ */ jsx(
3634
+ Box,
3635
+ {
3636
+ style: {
3637
+ width: 24,
3638
+ height: 1,
3639
+ backgroundColor: "var(--mantine-color-gray-5)",
3640
+ flexShrink: 0
3641
+ }
3642
+ }
3643
+ ),
3644
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "Phyllotaxis spiral by degree" })
3645
+ ] })
3646
+ ] })
3647
+ ] });
3648
+ }
3649
+ function ForceLegend() {
3650
+ return /* @__PURE__ */ jsxs(Stack, { gap: 4, children: [
3651
+ /* @__PURE__ */ jsx(Text, { size: "xs", fw: 600, c: "dimmed", tt: "uppercase", children: "Force layout" }),
3652
+ /* @__PURE__ */ jsxs(Stack, { gap: 2, children: [
3653
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "Physics simulation via cytoscape-fcose. Node positions are non-deterministic and are not persisted across reloads." }),
3654
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: 'Use the "Re-run layout" button in layout parameters to trigger a fresh simulation with the current force settings.' })
3655
+ ] })
3656
+ ] });
3657
+ }
3658
+ function ExploreLegend() {
3659
+ return /* @__PURE__ */ jsxs(Stack, { gap: 4, children: [
3660
+ /* @__PURE__ */ jsx(Text, { size: "xs", fw: 600, c: "dimmed", tt: "uppercase", children: "Multi-level drill-down" }),
3661
+ /* @__PURE__ */ jsxs(Stack, { gap: 2, children: [
3662
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "Click any node with children to expand its subgraph; siblings at the same level auto-collapse; click a higher segment in the breadcrumb to collapse downward; Esc collapses all." }),
3663
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "Expansion recurses up to 3 levels deep: zone centroid \u2192 feature \u2192 surface/capability." }),
3664
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "Resource visibility inside expanded zones is controlled by the existing Show/Hide resources toggle." })
3665
+ ] })
3666
+ ] });
3667
+ }
3668
+ function Legend({ visualizationMode, onDismiss }) {
3669
+ const modeLabel = visualizationMode === "explore" ? "Explore" : visualizationMode === "cluster" ? "Cluster" : visualizationMode === "swimlane" ? "Swimlane" : visualizationMode === "focus" ? "Focus" : visualizationMode === "force" ? "Force" : "Network";
3670
+ return /* @__PURE__ */ jsxs(
3671
+ Box,
3672
+ {
3673
+ style: {
3674
+ background: "var(--glass-background, rgba(11,16,21,0.88))",
3675
+ backdropFilter: "var(--glass-blur, blur(8px))",
3676
+ border: "1px solid var(--color-border)",
3677
+ borderRadius: "var(--mantine-radius-md)",
3678
+ padding: "var(--mantine-spacing-xs)",
3679
+ minWidth: 180,
3680
+ maxWidth: 260
3681
+ },
3682
+ children: [
3683
+ /* @__PURE__ */ jsxs(Group, { justify: "space-between", align: "center", mb: 6, wrap: "nowrap", children: [
3684
+ /* @__PURE__ */ jsxs(Text, { size: "xs", fw: 700, children: [
3685
+ modeLabel,
3686
+ " Legend"
3687
+ ] }),
3688
+ /* @__PURE__ */ jsx(Tooltip, { label: "Dismiss legend", children: /* @__PURE__ */ jsx(ActionIcon, { size: "xs", variant: "subtle", onClick: onDismiss, "aria-label": "Dismiss legend", children: /* @__PURE__ */ jsx(IconX, { size: 12 }) }) })
3689
+ ] }),
3690
+ visualizationMode === "explore" && /* @__PURE__ */ jsx(ExploreLegend, {}),
3691
+ visualizationMode === "cluster" && /* @__PURE__ */ jsx(ClusterLegend, {}),
3692
+ visualizationMode === "swimlane" && /* @__PURE__ */ jsx(SwimlaneLegend, {}),
3693
+ visualizationMode === "focus" && /* @__PURE__ */ jsx(FocusLegend, {}),
3694
+ visualizationMode === "network" && /* @__PURE__ */ jsx(NetworkLegend, {}),
3695
+ visualizationMode === "force" && /* @__PURE__ */ jsx(ForceLegend, {})
3696
+ ]
3697
+ }
3698
+ );
3699
+ }
3700
+ function ExploreBreadcrumb() {
3701
+ const expandedNodeIds = useCommandViewStore((s) => s.expandedNodeIds);
3702
+ const clearExpandedNodes = useCommandViewStore((s) => s.clearExpandedNodes);
3703
+ if (expandedNodeIds.size === 0) return null;
3704
+ const expandedCount = expandedNodeIds.size;
3705
+ return /* @__PURE__ */ jsxs(Breadcrumbs, { separator: "\u203A", "data-testid": "explore-breadcrumb", children: [
3706
+ /* @__PURE__ */ jsx(Anchor, { onClick: clearExpandedNodes, component: "button", type: "button", children: "OM" }),
3707
+ /* @__PURE__ */ jsx("span", { children: expandedCount === 1 ? Array.from(expandedNodeIds)[0] : `${expandedCount} expanded` })
3708
+ ] });
3709
+ }
3710
+ function ClusterControls({ value, onChange }) {
3711
+ return /* @__PURE__ */ jsxs(Stack, { gap: "sm", children: [
3712
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", fw: 600, tt: "uppercase", children: "Cluster parameters" }),
3713
+ /* @__PURE__ */ jsx(
3714
+ NumberInput,
3715
+ {
3716
+ label: "Zone padding",
3717
+ description: "Row height within each domain zone",
3718
+ value: value.zonePadding,
3719
+ onChange: (v) => onChange({ ...value, zonePadding: typeof v === "number" ? v : DEFAULT_CLUSTER_PARAMETERS.zonePadding }),
3720
+ min: 40,
3721
+ max: 200,
3722
+ step: 4,
3723
+ size: "xs"
3724
+ }
3725
+ ),
3726
+ /* @__PURE__ */ jsx(
3727
+ NumberInput,
3728
+ {
3729
+ label: "Inter-zone gap",
3730
+ description: "Vertical spacing between domain zone tiers",
3731
+ value: value.interZoneGap,
3732
+ onChange: (v) => onChange({ ...value, interZoneGap: typeof v === "number" ? v : DEFAULT_CLUSTER_PARAMETERS.interZoneGap }),
3733
+ min: 100,
3734
+ max: 800,
3735
+ step: 25,
3736
+ size: "xs"
3737
+ }
3738
+ )
3739
+ ] });
3740
+ }
3741
+ function FocusControls({ value, onChange }) {
3742
+ return /* @__PURE__ */ jsxs(Stack, { gap: "sm", children: [
3743
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", fw: 600, tt: "uppercase", children: "Focus parameters" }),
3744
+ /* @__PURE__ */ jsx(
3745
+ NumberInput,
3746
+ {
3747
+ label: "Depth",
3748
+ description: "Number of relationship rings around the focused node",
3749
+ value: value.depth,
3750
+ onChange: (v) => onChange({ ...value, depth: typeof v === "number" ? v : DEFAULT_FOCUS_PARAMETERS.depth }),
3751
+ min: 1,
3752
+ max: 3,
3753
+ step: 1,
3754
+ size: "xs"
3755
+ }
3756
+ ),
3757
+ /* @__PURE__ */ jsx(
3758
+ NumberInput,
3759
+ {
3760
+ label: "Ring radius",
3761
+ description: "Distance of the first-degree ring from the focused node",
3762
+ value: value.radius,
3763
+ onChange: (v) => onChange({ ...value, radius: typeof v === "number" ? v : DEFAULT_FOCUS_PARAMETERS.radius }),
3764
+ min: 150,
3765
+ max: 800,
3766
+ step: 20,
3767
+ size: "xs"
3768
+ }
3769
+ )
3770
+ ] });
3771
+ }
3772
+ function ForceControls({ value, onChange, onRerun }) {
3773
+ return /* @__PURE__ */ jsxs(Stack, { gap: "sm", children: [
3774
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", fw: 600, tt: "uppercase", children: "Force parameters" }),
3775
+ /* @__PURE__ */ jsx(
3776
+ NumberInput,
3777
+ {
3778
+ label: "Node repulsion",
3779
+ description: "Repulsive force between nodes \u2014 higher spreads nodes further apart",
3780
+ value: value.nodeRepulsion,
3781
+ onChange: (v) => onChange({
3782
+ ...value,
3783
+ nodeRepulsion: typeof v === "number" ? v : DEFAULT_FORCE_PARAMETERS.nodeRepulsion
3784
+ }),
3785
+ min: 500,
3786
+ step: 250,
3787
+ size: "xs"
3788
+ }
3789
+ ),
3790
+ /* @__PURE__ */ jsx(
3791
+ NumberInput,
3792
+ {
3793
+ label: "Ideal edge length",
3794
+ description: "Target length for edges in the physics simulation",
3795
+ value: value.idealEdgeLength,
3796
+ onChange: (v) => onChange({
3797
+ ...value,
3798
+ idealEdgeLength: typeof v === "number" ? v : DEFAULT_FORCE_PARAMETERS.idealEdgeLength
3799
+ }),
3800
+ min: 30,
3801
+ step: 10,
3802
+ size: "xs"
3803
+ }
3804
+ ),
3805
+ /* @__PURE__ */ jsx(
3806
+ NumberInput,
3807
+ {
3808
+ label: "Gravity",
3809
+ description: "Attraction toward the center \u2014 prevents disconnected components from flying apart",
3810
+ value: value.gravity,
3811
+ onChange: (v) => onChange({
3812
+ ...value,
3813
+ gravity: typeof v === "number" ? v : DEFAULT_FORCE_PARAMETERS.gravity
3814
+ }),
3815
+ min: 0,
3816
+ max: 1,
3817
+ step: 0.05,
3818
+ decimalScale: 2,
3819
+ size: "xs"
3820
+ }
3821
+ ),
3822
+ /* @__PURE__ */ jsx(Button, { size: "xs", variant: "light", onClick: onRerun, children: "Re-run layout" })
3823
+ ] });
3824
+ }
3825
+ function NetworkControls({ value, onChange }) {
3826
+ return /* @__PURE__ */ jsxs(Stack, { gap: "sm", children: [
3827
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", fw: 600, tt: "uppercase", children: "Network parameters" }),
3828
+ /* @__PURE__ */ jsx(
3829
+ NumberInput,
3830
+ {
3831
+ label: "Link length",
3832
+ description: "Radial growth factor per node in the spiral layout",
3833
+ value: value.linkLength,
3834
+ onChange: (v) => onChange({ ...value, linkLength: typeof v === "number" ? v : DEFAULT_NETWORK_PARAMETERS.linkLength }),
3835
+ min: 20,
3836
+ max: 250,
3837
+ step: 5,
3838
+ size: "xs"
3839
+ }
3840
+ ),
3841
+ /* @__PURE__ */ jsx(
3842
+ NumberInput,
3843
+ {
3844
+ label: "Repulsion",
3845
+ description: "Base radius offset from the graph center",
3846
+ value: value.repulsion,
3847
+ onChange: (v) => onChange({ ...value, repulsion: typeof v === "number" ? v : DEFAULT_NETWORK_PARAMETERS.repulsion }),
3848
+ min: 50,
3849
+ max: 500,
3850
+ step: 10,
3851
+ size: "xs"
3852
+ }
3853
+ )
3854
+ ] });
3855
+ }
3856
+ var SORT_KEY_OPTIONS = [
3857
+ { label: "Kind", value: "kind" },
3858
+ { label: "Domain", value: "domain" },
3859
+ { label: "Health", value: "health" }
3860
+ ];
3861
+ function SwimlaneControls({ value, onChange }) {
3862
+ return /* @__PURE__ */ jsxs(Stack, { gap: "sm", children: [
3863
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", fw: 600, tt: "uppercase", children: "Swimlane parameters" }),
3864
+ /* @__PURE__ */ jsx(
3865
+ NumberInput,
3866
+ {
3867
+ label: "Lane height",
3868
+ description: "Vertical spacing between domain lanes",
3869
+ value: value.laneHeight,
3870
+ onChange: (v) => onChange({ ...value, laneHeight: typeof v === "number" ? v : DEFAULT_SWIMLANE_PARAMETERS.laneHeight }),
3871
+ min: 80,
3872
+ max: 300,
3873
+ step: 10,
3874
+ size: "xs"
3875
+ }
3876
+ ),
3877
+ /* @__PURE__ */ jsxs(Stack, { gap: 4, children: [
3878
+ /* @__PURE__ */ jsx(Text, { size: "xs", fw: 500, children: "Sort key" }),
3879
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "Node ordering within each lane" }),
3880
+ /* @__PURE__ */ jsx(
3881
+ SegmentedControl,
3882
+ {
3883
+ value: value.sortKey,
3884
+ onChange: (v) => onChange({ ...value, sortKey: v }),
3885
+ data: SORT_KEY_OPTIONS,
3886
+ size: "xs",
3887
+ fullWidth: true
3888
+ }
3889
+ )
3890
+ ] })
3891
+ ] });
3892
+ }
3893
+ function VisualizationModeControls({
3894
+ visualizationMode,
3895
+ parameters,
3896
+ onChangeParameters,
3897
+ onRerunForceLayout
3898
+ }) {
3899
+ return /* @__PURE__ */ jsxs(Popover, { width: 240, position: "bottom-end", withArrow: true, shadow: "md", withinPortal: true, children: [
3900
+ /* @__PURE__ */ jsx(Popover.Target, { children: /* @__PURE__ */ jsx(Tooltip, { label: "Layout parameters", children: /* @__PURE__ */ jsx(
3901
+ ActionIcon,
3902
+ {
3903
+ variant: "light",
3904
+ radius: "xl",
3905
+ size: "lg",
3906
+ style: {
3907
+ background: "var(--glass-background)",
3908
+ backdropFilter: "var(--glass-blur)",
3909
+ color: "var(--color-text)",
3910
+ flexShrink: 0
3911
+ },
3912
+ "aria-label": "Layout parameters",
3913
+ children: /* @__PURE__ */ jsx(IconAdjustments, { size: 18 })
3914
+ }
3915
+ ) }) }),
3916
+ /* @__PURE__ */ jsxs(
3917
+ Popover.Dropdown,
3918
+ {
3919
+ style: {
3920
+ background: "var(--color-surface)",
3921
+ border: "1px solid var(--color-border)"
3922
+ },
3923
+ children: [
3924
+ visualizationMode === "cluster" && parameters.mode === "cluster" && /* @__PURE__ */ jsx(ClusterControls, { value: parameters, onChange: onChangeParameters }),
3925
+ visualizationMode === "swimlane" && parameters.mode === "swimlane" && /* @__PURE__ */ jsx(SwimlaneControls, { value: parameters, onChange: onChangeParameters }),
3926
+ visualizationMode === "focus" && parameters.mode === "focus" && /* @__PURE__ */ jsx(FocusControls, { value: parameters, onChange: onChangeParameters }),
3927
+ visualizationMode === "network" && parameters.mode === "network" && /* @__PURE__ */ jsx(NetworkControls, { value: parameters, onChange: onChangeParameters }),
3928
+ visualizationMode === "force" && parameters.mode === "force" && /* @__PURE__ */ jsx(ForceControls, { value: parameters, onChange: onChangeParameters, onRerun: onRerunForceLayout })
3929
+ ]
3930
+ }
3931
+ )
3932
+ ] });
3933
+ }
3444
3934
 
3445
3935
  // src/features/operations/organization-graph/commandViewGraphHealth.ts
3446
3936
  function toResourceHealth(node, stats) {
@@ -3697,6 +4187,8 @@ function OrganizationGraphPathTraceControls({
3697
4187
  ] });
3698
4188
  }
3699
4189
  var HIDE_SCROLLBAR_CLASS_NAME = "hide-scrollbar";
4190
+ var GRAPH_MOTION_QUERY = "(prefers-reduced-motion: reduce)";
4191
+ var EXPLORE_EXPANSION_ANIMATION_LIMIT = 28;
3700
4192
  var ORGANIZATION_NODE_ID = "organization-model";
3701
4193
  var EMPTY_TRACE_SELECTION = {
3702
4194
  sourceId: null,
@@ -3783,6 +4275,13 @@ function withAlpha(color, alpha) {
3783
4275
  }
3784
4276
  return toRgbaString({ ...parsed, a: alpha });
3785
4277
  }
4278
+ function shouldReduceGraphMotion() {
4279
+ return typeof window !== "undefined" && window.matchMedia(GRAPH_MOTION_QUERY).matches;
4280
+ }
4281
+ function getGraphPixelRatio() {
4282
+ if (typeof window === "undefined") return "auto";
4283
+ return window.devicePixelRatio > 1.5 ? 1.25 : "auto";
4284
+ }
3786
4285
  function getRelativeLuminance(color) {
3787
4286
  const parsed = parseColor(color);
3788
4287
  if (!parsed) {
@@ -3832,8 +4331,8 @@ function getNodeThemeByKind(tokens) {
3832
4331
  };
3833
4332
  return Object.fromEntries(
3834
4333
  Object.entries(accentByKind).map(([kind, accent]) => {
3835
- const background = mixColors(accent, tokens.surface === "transparent" ? tokens.background : tokens.surface, 0.34);
3836
- const border = mixColors(accent, tokens.border, 0.82);
4334
+ const background = mixColors(accent, tokens.surface === "transparent" ? tokens.background : tokens.surface, 0.18);
4335
+ const border = mixColors(accent, tokens.border, 0.5);
3837
4336
  return [
3838
4337
  kind,
3839
4338
  {
@@ -3861,6 +4360,22 @@ function getEdgeColor(edge, tokens) {
3861
4360
  return mixColors(tokens.primary, tokens.success, 0.32);
3862
4361
  }
3863
4362
  }
4363
+ function getNodeHaloColor(node, fallback) {
4364
+ const domain = getCommandViewNodeDomain(node);
4365
+ return DOMAIN_ZONE_TINT_COLOR[domain] ?? fallback;
4366
+ }
4367
+ function getExploreEdgeColor(sourceId, targetId, nodeById, tokens) {
4368
+ if (sourceId.startsWith("centroid:")) {
4369
+ const zone = sourceId.slice("centroid:".length);
4370
+ return mixColors(DOMAIN_ZONE_TINT_COLOR[zone] ?? tokens.primary, tokens.textDimmed, 0.46);
4371
+ }
4372
+ const sourceNode = nodeById.get(sourceId);
4373
+ const targetNode = nodeById.get(targetId);
4374
+ const sourceTint = sourceNode ? getNodeHaloColor(sourceNode, tokens.primary) : null;
4375
+ const targetTint = targetNode ? getNodeHaloColor(targetNode, tokens.primary) : null;
4376
+ const tint = sourceTint ?? targetTint ?? tokens.primary;
4377
+ return mixColors(tint, tokens.textDimmed, 0.42);
4378
+ }
3864
4379
  function getNodeSize(kind) {
3865
4380
  if (kind === "organization") {
3866
4381
  return { width: 230, height: 88 };
@@ -3870,9 +4385,21 @@ function getNodeSize(kind) {
3870
4385
  }
3871
4386
  return { width: 174, height: 66 };
3872
4387
  }
3873
- function getNodeScore(node, graph) {
3874
- const relationshipCount = graph.edges.filter((edge) => edge.sourceId === node.id || edge.targetId === node.id).length;
3875
- return Math.max(1, relationshipCount);
4388
+ function getDegreeWeightedSize(kind, degree) {
4389
+ const base = getNodeSize(kind);
4390
+ if (kind === "organization") {
4391
+ return base;
4392
+ }
4393
+ const minScale = 0.75;
4394
+ const maxScale = 1.55;
4395
+ const scale = Math.min(maxScale, minScale + degree / 8 * (maxScale - minScale));
4396
+ return {
4397
+ width: Math.round(base.width * scale),
4398
+ height: Math.round(base.height * scale)
4399
+ };
4400
+ }
4401
+ function getNodeScore(node, degreeByNodeId) {
4402
+ return Math.max(1, degreeByNodeId.get(node.id) ?? 0);
3876
4403
  }
3877
4404
  function getCommandViewRingColor(level, tokens) {
3878
4405
  switch (level) {
@@ -3887,39 +4414,254 @@ function getCommandViewRingColor(level, tokens) {
3887
4414
  return mixColors(tokens.textDimmed, tokens.border, 0.6);
3888
4415
  }
3889
4416
  }
3890
- function toCytoscapeElementsWithHealth(graph, tokens, commandViewHealthByNodeId, visualizationMode, selectedElement) {
4417
+ function getDomainCardPosition(domain) {
4418
+ const zoneOrigins = {
4419
+ operations: { x: -520, y: -270 },
4420
+ runtime: { x: 130, y: -270 },
4421
+ business: { x: -520, y: 95 },
4422
+ delivery: { x: 130, y: 95 },
4423
+ knowledge: { x: 0, y: 605 },
4424
+ admin: { x: -520, y: 420 },
4425
+ system: { x: 130, y: 420 },
4426
+ platform: { x: 0, y: -520 },
4427
+ other: { x: 0, y: 800 }
4428
+ };
4429
+ return zoneOrigins[domain] ?? { x: 0, y: 0 };
4430
+ }
4431
+ var DOMAIN_DISPLAY_LABELS = {
4432
+ operations: "Operations",
4433
+ runtime: "Runtime",
4434
+ business: "Business",
4435
+ delivery: "Delivery",
4436
+ knowledge: "Knowledge",
4437
+ admin: "Admin",
4438
+ system: "System",
4439
+ platform: "Platform",
4440
+ other: "Other"
4441
+ };
4442
+ function toCytoscapeElementsWithHealth(graph, graphIndex, tokens, commandViewHealthByNodeId, visualizationMode, selectedElement, expandedClusterDomains, parameters, expandedNodeIds) {
3891
4443
  const nodeThemeByKind = getNodeThemeByKind(tokens);
4444
+ if (visualizationMode === "explore") {
4445
+ const { syntheticNodes, backboneEdges, positions } = getExploreProjection();
4446
+ const elements = [];
4447
+ for (const sNode of syntheticNodes) {
4448
+ const isOmRoot = sNode.isOmRoot === true;
4449
+ const fillColor = sNode.zoneTint ? mixColors(withAlpha(sNode.zoneTint, 0.22), tokens.background, 0.22) : mixColors(tokens.primary, tokens.surface === "transparent" ? tokens.background : tokens.surface, 0.18);
4450
+ const borderColor = sNode.zoneTint ? mixColors(sNode.zoneTint, tokens.border, 0.48) : mixColors(tokens.primary, tokens.border, 0.52);
4451
+ const haloColor = sNode.zoneTint ?? tokens.primary;
4452
+ elements.push({
4453
+ data: {
4454
+ id: sNode.id,
4455
+ kind: isOmRoot ? "organization" : "centroid",
4456
+ label: sNode.label,
4457
+ isCentroid: sNode.isCentroid,
4458
+ domainZone: sNode.domainZone ?? void 0,
4459
+ zoneTint: sNode.zoneTint ?? void 0,
4460
+ fillColor,
4461
+ borderColor,
4462
+ textColor: tokens.text,
4463
+ haloColor,
4464
+ edgeHaloColor: withAlpha(haloColor, 0.1),
4465
+ hasChildren: isOmRoot ? "false" : "true",
4466
+ width: isOmRoot ? 100 : 140,
4467
+ height: isOmRoot ? 100 : 140
4468
+ },
4469
+ position: positions.get(sNode.id)
4470
+ });
4471
+ }
4472
+ for (const edge of backboneEdges) {
4473
+ elements.push({
4474
+ data: {
4475
+ id: `backbone:${edge.source}:${edge.target}`,
4476
+ source: edge.source,
4477
+ target: edge.target,
4478
+ kind: "contains",
4479
+ label: "",
4480
+ strokeColor: mixColors(tokens.border, tokens.textDimmed, 0.45),
4481
+ edgeHaloColor: withAlpha(tokens.primary, 0.05),
4482
+ isExploreBackbone: "true"
4483
+ }
4484
+ });
4485
+ }
4486
+ if (expandedNodeIds && expandedNodeIds.size > 0) {
4487
+ const addedRealNodeIds = /* @__PURE__ */ new Set();
4488
+ const addedExpansionEdgeIds = /* @__PURE__ */ new Set();
4489
+ for (const nodeId of expandedNodeIds) {
4490
+ const anchorPos = positions.get(nodeId) ?? { x: 0, y: 0 };
4491
+ const level = nodeId.startsWith("centroid:") ? 1 : 2;
4492
+ const childPositions = getRadialFromAnchorProjection(graph, nodeId, anchorPos, level, graphIndex);
4493
+ for (const [childId, childPos] of childPositions) {
4494
+ positions.set(childId, childPos);
4495
+ if (addedRealNodeIds.has(childId)) {
4496
+ const edgeId2 = `expand:${nodeId}:${childId}`;
4497
+ if (!addedExpansionEdgeIds.has(edgeId2)) {
4498
+ addedExpansionEdgeIds.add(edgeId2);
4499
+ const strokeColor = getExploreEdgeColor(nodeId, childId, graphIndex.nodeById, tokens);
4500
+ elements.push({
4501
+ data: {
4502
+ id: edgeId2,
4503
+ source: nodeId,
4504
+ target: childId,
4505
+ kind: "contains",
4506
+ label: "",
4507
+ strokeColor,
4508
+ edgeHaloColor: withAlpha(strokeColor, 0.08),
4509
+ isExploreExpansion: "true",
4510
+ exploreDepth: String(level)
4511
+ }
4512
+ });
4513
+ }
4514
+ continue;
4515
+ }
4516
+ const node = graphIndex.nodeById.get(childId);
4517
+ if (!node) continue;
4518
+ addedRealNodeIds.add(childId);
4519
+ const degree = getNodeScore(node, graphIndex.degreeByNodeId);
4520
+ const size = getDegreeWeightedSize(node.kind, degree);
4521
+ const theme = nodeThemeByKind[node.kind];
4522
+ const health = commandViewHealthByNodeId?.get(node.id) ?? null;
4523
+ const fillColor = health && node.kind === "resource" ? mixColors(tokens.surfaceHover, tokens.background, 0.76) : theme.background;
4524
+ const borderColor = health ? getCommandViewRingColor(health.healthLevel, tokens) : theme.border;
4525
+ const label = truncateGraphLabel(node.label, node.kind === "resource" ? 22 : 30);
4526
+ const displayLabel = health ? `${label}
4527
+ ${health.summaryLabel}` : label;
4528
+ const nodeDomain = getCommandViewNodeDomain(node);
4529
+ const zoneTint = DOMAIN_ZONE_TINT_COLOR[nodeDomain] ?? null;
4530
+ const haloColor = health ? borderColor : zoneTint ?? theme.border;
4531
+ elements.push({
4532
+ data: {
4533
+ ...node,
4534
+ width: size.width,
4535
+ height: size.height,
4536
+ score: degree,
4537
+ label: displayLabel,
4538
+ fillColor,
4539
+ borderColor,
4540
+ textColor: health ? tokens.text : theme.color,
4541
+ zoneTint: zoneTint ?? void 0,
4542
+ haloColor,
4543
+ edgeHaloColor: withAlpha(haloColor, 0.08),
4544
+ hasChildren: exploreNodeHasChildren(graph, node.id, graphIndex) ? "true" : "false"
4545
+ },
4546
+ position: childPos
4547
+ });
4548
+ const edgeId = `expand:${nodeId}:${childId}`;
4549
+ if (!addedExpansionEdgeIds.has(edgeId)) {
4550
+ addedExpansionEdgeIds.add(edgeId);
4551
+ const strokeColor = getExploreEdgeColor(nodeId, childId, graphIndex.nodeById, tokens);
4552
+ elements.push({
4553
+ data: {
4554
+ id: edgeId,
4555
+ source: nodeId,
4556
+ target: childId,
4557
+ kind: "contains",
4558
+ label: "",
4559
+ strokeColor,
4560
+ edgeHaloColor: withAlpha(strokeColor, 0.08),
4561
+ isExploreExpansion: "true",
4562
+ exploreDepth: String(level)
4563
+ }
4564
+ });
4565
+ }
4566
+ }
4567
+ }
4568
+ }
4569
+ return elements;
4570
+ }
3892
4571
  const presetPositions = getCommandViewGraphPositions({
3893
4572
  graph,
3894
4573
  visualizationMode,
3895
- selectedNodeId: selectedElement?.type === "node" ? selectedElement.id : null
4574
+ selectedNodeId: selectedElement?.type === "node" ? selectedElement.id : null,
4575
+ parameters
3896
4576
  });
3897
- return [
3898
- ...graph.nodes.map((node) => {
3899
- const size = getNodeSize(node.kind);
3900
- const theme = nodeThemeByKind[node.kind];
3901
- const health = commandViewHealthByNodeId?.get(node.id) ?? null;
3902
- const fillColor = health && node.kind === "resource" ? mixColors(tokens.surfaceHover, tokens.background, 0.76) : theme.background;
3903
- const borderColor = health ? getCommandViewRingColor(health.healthLevel, tokens) : theme.border;
3904
- const maxLabelLength = node.kind === "resource" ? 22 : 30;
3905
- const label = truncateGraphLabel(node.label, maxLabelLength);
3906
- const displayLabel = health ? `${label}
4577
+ const isCluster = visualizationMode === "cluster";
4578
+ const expandedSet = new Set(expandedClusterDomains);
4579
+ const domainGroups = isCluster ? graph.nodes.reduce((acc, node) => {
4580
+ const domain = getCommandViewNodeDomain(node);
4581
+ const existing = acc.get(domain);
4582
+ if (existing) {
4583
+ existing.push(node);
4584
+ } else {
4585
+ acc.set(domain, [node]);
4586
+ }
4587
+ return acc;
4588
+ }, /* @__PURE__ */ new Map()) : null;
4589
+ const hiddenBehindCardIds = /* @__PURE__ */ new Set();
4590
+ if (isCluster && domainGroups) {
4591
+ for (const [domain, members] of domainGroups) {
4592
+ if (!expandedSet.has(domain)) {
4593
+ for (const member of members) {
4594
+ hiddenBehindCardIds.add(member.id);
4595
+ }
4596
+ }
4597
+ }
4598
+ }
4599
+ const domainCardElements = [];
4600
+ if (isCluster && domainGroups) {
4601
+ const cardBackground = withAlpha(tokens.surface === "transparent" ? tokens.background : tokens.surface, 0.92);
4602
+ const cardBorder = mixColors(tokens.border, tokens.textDimmed, 0.35);
4603
+ const cardText = tokens.textDimmed;
4604
+ for (const [domain, members] of domainGroups) {
4605
+ if (!expandedSet.has(domain)) {
4606
+ const position = getDomainCardPosition(domain);
4607
+ const displayName = DOMAIN_DISPLAY_LABELS[domain] ?? domain;
4608
+ domainCardElements.push({
4609
+ data: {
4610
+ id: `domain-card:${domain}`,
4611
+ kind: "domain-card",
4612
+ label: `${displayName}
4613
+ ${members.length} node${members.length === 1 ? "" : "s"}`,
4614
+ domain,
4615
+ count: members.length,
4616
+ fillColor: cardBackground,
4617
+ borderColor: cardBorder,
4618
+ textColor: cardText,
4619
+ haloColor: cardBorder,
4620
+ edgeHaloColor: withAlpha(cardBorder, 0.06),
4621
+ hasChildren: "true",
4622
+ width: 200,
4623
+ height: 88
4624
+ },
4625
+ position
4626
+ });
4627
+ }
4628
+ }
4629
+ }
4630
+ const memberElements = graph.nodes.filter((node) => !hiddenBehindCardIds.has(node.id)).map((node) => {
4631
+ const degree = getNodeScore(node, graphIndex.degreeByNodeId);
4632
+ const size = getDegreeWeightedSize(node.kind, degree);
4633
+ const theme = nodeThemeByKind[node.kind];
4634
+ const health = commandViewHealthByNodeId?.get(node.id) ?? null;
4635
+ const fillColor = health && node.kind === "resource" ? mixColors(tokens.surfaceHover, tokens.background, 0.76) : theme.background;
4636
+ const borderColor = health ? getCommandViewRingColor(health.healthLevel, tokens) : theme.border;
4637
+ const maxLabelLength = node.kind === "resource" ? 22 : 30;
4638
+ const label = truncateGraphLabel(node.label, maxLabelLength);
4639
+ const displayLabel = health ? `${label}
3907
4640
  ${health.summaryLabel}` : label;
3908
- return {
3909
- data: {
3910
- ...node,
3911
- width: size.width,
3912
- height: size.height,
3913
- score: getNodeScore(node, graph),
3914
- label: displayLabel,
3915
- fillColor,
3916
- borderColor,
3917
- textColor: health ? tokens.text : theme.color
3918
- },
3919
- position: presetPositions.get(node.id)
3920
- };
3921
- }),
3922
- ...graph.edges.map((edge) => ({
4641
+ const nodeDomain = getCommandViewNodeDomain(node);
4642
+ const zoneTint = DOMAIN_ZONE_TINT_COLOR[nodeDomain] ?? null;
4643
+ const haloColor = health ? borderColor : zoneTint ?? theme.border;
4644
+ return {
4645
+ data: {
4646
+ ...node,
4647
+ width: size.width,
4648
+ height: size.height,
4649
+ score: degree,
4650
+ label: displayLabel,
4651
+ fillColor,
4652
+ borderColor,
4653
+ textColor: health ? tokens.text : theme.color,
4654
+ zoneTint: zoneTint ?? void 0,
4655
+ haloColor,
4656
+ edgeHaloColor: withAlpha(haloColor, 0.08),
4657
+ hasChildren: exploreNodeHasChildren(graph, node.id, graphIndex) ? "true" : "false"
4658
+ },
4659
+ position: presetPositions.get(node.id)
4660
+ };
4661
+ });
4662
+ const edgeElements = graph.edges.filter((edge) => !hiddenBehindCardIds.has(edge.sourceId) && !hiddenBehindCardIds.has(edge.targetId)).map((edge) => {
4663
+ const strokeColor = getEdgeColor(edge, tokens);
4664
+ return {
3923
4665
  data: {
3924
4666
  id: edge.id,
3925
4667
  source: edge.sourceId,
@@ -3927,12 +4669,15 @@ ${health.summaryLabel}` : label;
3927
4669
  kind: edge.kind,
3928
4670
  label: edge.label ?? edge.relationshipType ?? edge.kind.replace(/_/g, " "),
3929
4671
  relationshipType: edge.relationshipType,
3930
- strokeColor: getEdgeColor(edge, tokens)
4672
+ strokeColor,
4673
+ edgeHaloColor: withAlpha(strokeColor, 0.08)
3931
4674
  }
3932
- }))
3933
- ];
4675
+ };
4676
+ });
4677
+ return [...domainCardElements, ...memberElements, ...edgeElements];
3934
4678
  }
3935
4679
  function getLayoutOptions(mode, selectedElement, traceResult) {
4680
+ const reducedMotion = shouldReduceGraphMotion();
3936
4681
  const selectedNodeId = selectedElement?.type === "node" ? selectedElement.id : void 0;
3937
4682
  const traceRootId = traceResult?.source?.id;
3938
4683
  const impactRootId = selectedNodeId ?? traceResult?.target?.id ?? traceRootId ?? ORGANIZATION_NODE_ID;
@@ -3941,8 +4686,8 @@ function getLayoutOptions(mode, selectedElement, traceResult) {
3941
4686
  return {
3942
4687
  name: "concentric",
3943
4688
  fit: true,
3944
- animate: true,
3945
- animationDuration: 220,
4689
+ animate: !reducedMotion,
4690
+ animationDuration: reducedMotion ? 0 : 220,
3946
4691
  padding: 48,
3947
4692
  spacingFactor: 1,
3948
4693
  concentric: (node) => {
@@ -3958,8 +4703,8 @@ function getLayoutOptions(mode, selectedElement, traceResult) {
3958
4703
  return {
3959
4704
  name: "breadthfirst",
3960
4705
  fit: true,
3961
- animate: true,
3962
- animationDuration: 220,
4706
+ animate: !reducedMotion,
4707
+ animationDuration: reducedMotion ? 0 : 220,
3963
4708
  directed: true,
3964
4709
  circle: false,
3965
4710
  spacingFactor: 1.08,
@@ -3974,10 +4719,12 @@ function getLayoutOptions(mode, selectedElement, traceResult) {
3974
4719
  padding: 48
3975
4720
  };
3976
4721
  }
3977
- function createCytoscapeStyle(tokens) {
3978
- const selectedBorder = mixColors(tokens.text, tokens.primary, 0.25);
3979
- const traceBorder = mixColors(tokens.warning, tokens.primary, 0.75);
3980
- const edgeLabelBackground = withAlpha(tokens.background, 0.78);
4722
+ function createCytoscapeStyle(tokens, visualizationMode) {
4723
+ const selectedBorder = mixColors(tokens.primary, tokens.textDimmed, 0.58);
4724
+ const traceBorder = mixColors(tokens.warning, tokens.border, 0.58);
4725
+ const edgeLabelBackground = withAlpha(tokens.background, 0.86);
4726
+ const reducedMotion = shouldReduceGraphMotion();
4727
+ const baseEdgeOpacity = visualizationMode === "cluster" || visualizationMode === "swimlane" ? 0.16 : visualizationMode === "focus" ? 0.42 : 0.28;
3981
4728
  return [
3982
4729
  {
3983
4730
  selector: "node",
@@ -3987,60 +4734,238 @@ function createCytoscapeStyle(tokens) {
3987
4734
  width: "data(width)",
3988
4735
  height: "data(height)",
3989
4736
  "background-color": "data(fillColor)",
3990
- "border-width": 2,
4737
+ "background-opacity": 0.86,
4738
+ "border-width": 1.4,
3991
4739
  "border-style": "solid",
3992
4740
  "border-color": "data(borderColor)",
3993
4741
  color: "data(textColor)",
3994
- "font-size": 10,
3995
- "font-weight": 700,
4742
+ "font-size": 9.5,
4743
+ "font-weight": 650,
3996
4744
  "text-wrap": "wrap",
3997
4745
  "text-max-width": "120px",
3998
4746
  "text-valign": "center",
3999
4747
  "text-halign": "center",
4000
4748
  padding: "10px",
4001
4749
  "overlay-opacity": 0,
4002
- "text-outline-width": 0
4750
+ "text-outline-width": 1,
4751
+ "text-outline-color": tokens.background,
4752
+ "text-outline-opacity": 0.56,
4753
+ "underlay-color": "data(haloColor)",
4754
+ "underlay-opacity": 0,
4755
+ "underlay-padding": 7,
4756
+ "underlay-shape": "ellipse",
4757
+ "outline-width": 0,
4758
+ "transition-property": "opacity, border-color, border-width, background-opacity, underlay-opacity, underlay-padding, outline-opacity",
4759
+ "transition-duration": reducedMotion ? 0 : 90,
4760
+ "transition-timing-function": "ease-out-cubic",
4761
+ "z-index-compare": "manual",
4762
+ "z-index": 2
4003
4763
  }
4004
4764
  },
4005
4765
  {
4766
+ // OD-9: organization node — largest ellipse, anchors the graph.
4006
4767
  selector: 'node[kind = "organization"]',
4007
4768
  style: {
4008
- shape: "cut-rectangle",
4009
- "border-width": 3,
4010
- "font-size": 13
4769
+ shape: "ellipse",
4770
+ width: 100,
4771
+ height: 100,
4772
+ "border-width": 2.4,
4773
+ "font-size": 13.5,
4774
+ "font-weight": 800,
4775
+ "text-valign": "center",
4776
+ "text-halign": "center",
4777
+ "text-max-width": "100px",
4778
+ "min-zoomed-font-size": 12
4011
4779
  }
4012
4780
  },
4013
4781
  {
4014
- selector: 'node[kind = "resource"]',
4782
+ // OD-9: feature nodes — large ellipse.
4783
+ selector: 'node[kind = "feature"]',
4015
4784
  style: {
4016
- "border-width": 1.5,
4017
- "font-size": 8,
4018
- "font-weight": 650,
4019
- "text-max-width": "104px",
4020
- "background-opacity": 0.82,
4021
- "min-zoomed-font-size": 7
4785
+ shape: "ellipse",
4786
+ width: 80,
4787
+ height: 80,
4788
+ "font-size": 12.5,
4789
+ "font-weight": 700,
4790
+ "text-valign": "center",
4791
+ "text-halign": "center",
4792
+ "text-max-width": "100px",
4793
+ "min-zoomed-font-size": 11
4794
+ }
4795
+ },
4796
+ {
4797
+ // OD-9: surface and capability nodes — medium ellipse.
4798
+ selector: 'node[kind = "surface"], node[kind = "capability"]',
4799
+ style: {
4800
+ shape: "ellipse",
4801
+ width: 70,
4802
+ height: 70,
4803
+ "font-size": 12,
4804
+ "font-weight": 700,
4805
+ "text-valign": "center",
4806
+ "text-halign": "center",
4807
+ "text-max-width": "100px",
4808
+ "min-zoomed-font-size": 11
4809
+ }
4810
+ },
4811
+ {
4812
+ // OD-9: entity, stage, knowledge nodes — compact ellipse.
4813
+ selector: 'node[kind = "entity"], node[kind = "stage"], node[kind = "knowledge"]',
4814
+ style: {
4815
+ shape: "ellipse",
4816
+ width: 65,
4817
+ height: 65,
4818
+ "font-size": 11.5,
4819
+ "font-weight": 600,
4820
+ "text-valign": "center",
4821
+ "text-halign": "center",
4822
+ "text-max-width": "100px",
4823
+ "min-zoomed-font-size": 11
4824
+ }
4825
+ },
4826
+ {
4827
+ // OD-9: resource nodes — smallest ellipse (kept from polish slice).
4828
+ selector: 'node[kind = "resource"]',
4829
+ style: {
4830
+ shape: "ellipse",
4831
+ width: 60,
4832
+ height: 60,
4833
+ "border-width": 1.2,
4834
+ "font-size": 11,
4835
+ "font-weight": 600,
4836
+ "text-valign": "center",
4837
+ "text-halign": "center",
4838
+ "text-max-width": "100px",
4839
+ "background-opacity": 0.82,
4840
+ "min-zoomed-font-size": 11
4841
+ }
4842
+ },
4843
+ {
4844
+ // OD-10: zone-tinted node fills — any node with a zoneTint data attribute gets a tinted background.
4845
+ selector: "node[zoneTint]",
4846
+ style: {
4847
+ "background-color": "data(fillColor)",
4848
+ "background-opacity": 0.82,
4849
+ "border-color": "data(borderColor)",
4850
+ "underlay-color": "data(haloColor)",
4851
+ "underlay-opacity": 0,
4852
+ "underlay-padding": 6
4853
+ }
4854
+ },
4855
+ {
4856
+ // Explore centroids — larger circles with bolder labels so they read as zone anchors.
4857
+ selector: 'node[isCentroid = "true"]',
4858
+ style: {
4859
+ shape: "ellipse",
4860
+ width: 140,
4861
+ height: 140,
4862
+ "background-opacity": 0.76,
4863
+ "border-width": 1.8,
4864
+ "border-opacity": 0.9,
4865
+ "outline-width": 1,
4866
+ "outline-color": "data(haloColor)",
4867
+ "outline-opacity": 0.08,
4868
+ "outline-offset": 4,
4869
+ "underlay-opacity": 0.025,
4870
+ "underlay-padding": 9,
4871
+ "font-size": 14,
4872
+ "font-weight": 700,
4873
+ "text-valign": "center",
4874
+ "text-halign": "center",
4875
+ "text-max-width": "120px",
4876
+ "min-zoomed-font-size": 13
4877
+ }
4878
+ },
4879
+ {
4880
+ selector: 'node[hasChildren = "true"]',
4881
+ style: {
4882
+ "outline-width": 1,
4883
+ "outline-style": "solid",
4884
+ "outline-color": "data(haloColor)",
4885
+ "outline-opacity": 0.07,
4886
+ "outline-offset": 3
4887
+ }
4888
+ },
4889
+ {
4890
+ // Domain-card virtual nodes: rounded rect, muted background, crisp border.
4891
+ selector: 'node[kind = "domain-card"]',
4892
+ style: {
4893
+ shape: "round-rectangle",
4894
+ width: 200,
4895
+ height: 88,
4896
+ "background-color": "data(fillColor)",
4897
+ "border-width": 1,
4898
+ "border-color": "data(borderColor)",
4899
+ color: "data(textColor)",
4900
+ "font-size": 12,
4901
+ "font-weight": 700,
4902
+ "text-wrap": "wrap",
4903
+ "text-max-width": "180px",
4904
+ "text-valign": "center",
4905
+ "text-halign": "center",
4906
+ "overlay-opacity": 0,
4907
+ "underlay-shape": "round-rectangle"
4908
+ }
4909
+ },
4910
+ {
4911
+ selector: 'node[kind = "domain-card"]:active, node[kind = "domain-card"]:hover',
4912
+ style: {
4913
+ "border-width": 2,
4914
+ "border-color": selectedBorder
4022
4915
  }
4023
4916
  },
4024
4917
  {
4025
4918
  selector: "edge",
4026
4919
  style: {
4027
- width: 1.25,
4920
+ width: 1.05,
4028
4921
  label: "",
4029
4922
  color: tokens.text,
4030
4923
  "font-size": 10,
4031
4924
  "font-weight": 700,
4032
4925
  "curve-style": "bezier",
4926
+ "line-cap": "round",
4033
4927
  "line-color": "data(strokeColor)",
4928
+ "line-outline-color": "data(edgeHaloColor)",
4929
+ "line-outline-width": 0.35,
4034
4930
  "target-arrow-color": "data(strokeColor)",
4035
4931
  "target-arrow-shape": "triangle",
4036
- "arrow-scale": 0.95,
4932
+ "arrow-scale": 0.78,
4037
4933
  "text-background-color": edgeLabelBackground,
4038
4934
  "text-background-opacity": 1,
4039
4935
  "text-background-padding": "4px",
4040
4936
  "text-border-opacity": 0,
4041
4937
  "text-rotation": "autorotate",
4042
4938
  "overlay-opacity": 0,
4043
- "line-opacity": 0.38
4939
+ "line-opacity": baseEdgeOpacity,
4940
+ "underlay-color": "data(edgeHaloColor)",
4941
+ "underlay-opacity": 0,
4942
+ "underlay-padding": 2,
4943
+ "transition-property": "opacity, width, line-opacity, line-color, underlay-opacity",
4944
+ "transition-duration": reducedMotion ? 0 : 90,
4945
+ "transition-timing-function": "ease-out-cubic",
4946
+ "z-index": 1
4947
+ }
4948
+ },
4949
+ {
4950
+ selector: 'edge[isExploreBackbone = "true"]',
4951
+ style: {
4952
+ width: 1.45,
4953
+ "line-opacity": visualizationMode === "explore" ? 0.22 : baseEdgeOpacity,
4954
+ "line-style": "dashed",
4955
+ "line-dash-pattern": [7, 8],
4956
+ "arrow-scale": 0.6,
4957
+ "line-outline-width": 0
4958
+ }
4959
+ },
4960
+ {
4961
+ selector: 'edge[isExploreExpansion = "true"]',
4962
+ style: {
4963
+ width: 1.55,
4964
+ "line-opacity": 0.56,
4965
+ "line-outline-width": 0.7,
4966
+ "underlay-opacity": 0,
4967
+ "underlay-padding": 0,
4968
+ "arrow-scale": 0.7
4044
4969
  }
4045
4970
  },
4046
4971
  {
@@ -4062,18 +4987,66 @@ function createCytoscapeStyle(tokens) {
4062
4987
  opacity: 0.1
4063
4988
  }
4064
4989
  },
4990
+ {
4991
+ selector: "node.is-faded",
4992
+ style: {
4993
+ "underlay-opacity": 0,
4994
+ "outline-opacity": 0
4995
+ }
4996
+ },
4997
+ {
4998
+ selector: "edge.is-faded",
4999
+ style: {
5000
+ "underlay-opacity": 0,
5001
+ "line-outline-width": 0
5002
+ }
5003
+ },
4065
5004
  {
4066
5005
  selector: "node.is-context",
4067
5006
  style: {
4068
- opacity: 1
5007
+ opacity: 1,
5008
+ "underlay-opacity": 0.025
5009
+ }
5010
+ },
5011
+ {
5012
+ selector: "node.is-hovered",
5013
+ style: {
5014
+ opacity: 1,
5015
+ "border-width": 2,
5016
+ "border-color": selectedBorder,
5017
+ "background-opacity": 0.92,
5018
+ "underlay-opacity": 0.045,
5019
+ "underlay-padding": 10,
5020
+ "outline-opacity": 0.12,
5021
+ "z-index": 20
5022
+ }
5023
+ },
5024
+ {
5025
+ selector: "node.is-hover-neighbor",
5026
+ style: {
5027
+ opacity: 1,
5028
+ "underlay-opacity": 0.02
4069
5029
  }
4070
5030
  },
4071
5031
  {
4072
5032
  selector: "edge.is-connected",
4073
5033
  style: {
4074
5034
  opacity: 1,
4075
- width: 2.9,
4076
- "line-opacity": 0.9
5035
+ width: 1.9,
5036
+ "line-opacity": 0.72,
5037
+ "underlay-opacity": 0,
5038
+ "line-outline-width": 0.8
5039
+ }
5040
+ },
5041
+ {
5042
+ selector: "edge.is-hover-connected",
5043
+ style: {
5044
+ opacity: 1,
5045
+ width: 1.8,
5046
+ "line-opacity": 0.76,
5047
+ "underlay-opacity": 0,
5048
+ "line-outline-width": 0.8,
5049
+ "z-index": 10
4077
5050
  }
4078
5051
  },
4079
5052
  {
@@ -4081,8 +5054,15 @@ function createCytoscapeStyle(tokens) {
4081
5054
  style: {
4082
5055
  opacity: 1,
4083
5056
  "border-color": selectedBorder,
4084
- "border-width": 3.5,
4085
- "background-opacity": 1
5057
+ "border-width": 2.8,
5058
+ "background-opacity": 0.95,
5059
+ "underlay-color": selectedBorder,
5060
+ "underlay-opacity": 0.06,
5061
+ "underlay-padding": 12,
5062
+ "outline-color": selectedBorder,
5063
+ "outline-opacity": 0.16,
5064
+ "outline-width": 1,
5065
+ "z-index": 30
4086
5066
  }
4087
5067
  },
4088
5068
  {
@@ -4090,23 +5070,28 @@ function createCytoscapeStyle(tokens) {
4090
5070
  style: {
4091
5071
  opacity: 1,
4092
5072
  "border-color": traceBorder,
4093
- "border-width": 4
5073
+ "border-width": 2.6,
5074
+ "underlay-color": traceBorder,
5075
+ "underlay-opacity": 0.05,
5076
+ "underlay-padding": 11
4094
5077
  }
4095
5078
  },
4096
5079
  {
4097
5080
  selector: "node.is-trace-endpoint",
4098
5081
  style: {
4099
5082
  opacity: 1,
4100
- "border-color": mixColors(tokens.warning, tokens.text, 0.62),
4101
- "border-width": 5
5083
+ "border-color": mixColors(tokens.warning, tokens.textDimmed, 0.58),
5084
+ "border-width": 3
4102
5085
  }
4103
5086
  },
4104
5087
  {
4105
5088
  selector: "edge.is-selected",
4106
5089
  style: {
4107
5090
  opacity: 1,
4108
- width: 4.2,
4109
- "line-opacity": 1,
5091
+ width: 2.6,
5092
+ "line-opacity": 0.9,
5093
+ "line-outline-width": 1,
5094
+ "underlay-opacity": 0,
4110
5095
  label: "data(label)"
4111
5096
  }
4112
5097
  },
@@ -4114,134 +5099,236 @@ function createCytoscapeStyle(tokens) {
4114
5099
  selector: "edge.is-trace-edge",
4115
5100
  style: {
4116
5101
  opacity: 1,
4117
- width: 4.6,
4118
- "line-opacity": 1,
5102
+ width: 2.8,
5103
+ "line-opacity": 0.92,
4119
5104
  label: "data(label)",
4120
5105
  "line-color": traceBorder,
4121
5106
  "target-arrow-color": traceBorder,
4122
- "line-style": "solid"
5107
+ "line-style": "solid",
5108
+ "line-outline-color": withAlpha(traceBorder, 0.16),
5109
+ "line-outline-width": 1,
5110
+ "underlay-color": withAlpha(traceBorder, 0.12),
5111
+ "underlay-opacity": 0
4123
5112
  }
4124
5113
  },
4125
5114
  {
4126
5115
  selector: "node.is-expanded-node",
4127
5116
  style: {
4128
5117
  opacity: 1,
4129
- "border-color": mixColors(tokens.primary, tokens.warning, 0.72),
4130
- "border-width": 3.4,
4131
- "background-opacity": 1
5118
+ "border-color": mixColors(tokens.primary, tokens.warning, 0.42),
5119
+ "border-width": 2.2,
5120
+ "background-opacity": 0.92,
5121
+ "underlay-opacity": 0.04,
5122
+ "underlay-padding": 10,
5123
+ "outline-opacity": 0.12
4132
5124
  }
4133
5125
  },
4134
5126
  {
4135
5127
  selector: "edge.is-expanded-edge",
4136
5128
  style: {
4137
5129
  opacity: 1,
4138
- width: 3.4,
4139
- "line-opacity": 0.94,
5130
+ width: 2,
5131
+ "line-opacity": 0.76,
5132
+ "line-outline-width": 0.9,
5133
+ "underlay-opacity": 0,
4140
5134
  label: "data(label)"
4141
5135
  }
4142
5136
  }
4143
5137
  ];
4144
5138
  }
4145
- function syncGraphClasses(cy, selectedElement, traceResult, hiddenIds, hiddenEdgeIds, expandedNodeIds, expandedEdgeIds) {
5139
+ var GRAPH_SYNC_CLASS_NAMES = [
5140
+ "is-hidden",
5141
+ "is-faded",
5142
+ "is-context",
5143
+ "is-selected",
5144
+ "is-connected",
5145
+ "is-trace-node",
5146
+ "is-trace-edge",
5147
+ "is-trace-endpoint",
5148
+ "is-expanded-node",
5149
+ "is-expanded-edge"
5150
+ ];
5151
+ function addGraphClassTarget(targets, elementId, className) {
5152
+ let classNames = targets.get(elementId);
5153
+ if (!classNames) {
5154
+ classNames = /* @__PURE__ */ new Set();
5155
+ targets.set(elementId, classNames);
5156
+ }
5157
+ classNames.add(className);
5158
+ }
5159
+ function removeGraphClassTarget(targets, elementId, className) {
5160
+ targets.get(elementId)?.delete(className);
5161
+ }
5162
+ function applyGraphClassTargets(cy, targets) {
5163
+ const previousTargets = cy.scratch("_elevasis_graphClassTargets") ?? /* @__PURE__ */ new Map();
5164
+ const nextTargets = /* @__PURE__ */ new Map();
5165
+ for (const [elementId, classNames] of targets) {
5166
+ const key = GRAPH_SYNC_CLASS_NAMES.filter((className) => classNames.has(className)).join(" ");
5167
+ if (key) {
5168
+ nextTargets.set(elementId, key);
5169
+ }
5170
+ }
5171
+ const touchedIds = /* @__PURE__ */ new Set([...previousTargets.keys(), ...nextTargets.keys()]);
4146
5172
  cy.batch(() => {
4147
- cy.elements().removeClass(
4148
- "is-hidden is-faded is-context is-selected is-connected is-trace-node is-trace-edge is-trace-endpoint is-expanded-node is-expanded-edge"
4149
- );
4150
- for (const nodeId of hiddenIds) {
4151
- const node = cy.getElementById(nodeId);
4152
- if (!node.empty()) {
4153
- node.addClass("is-hidden");
5173
+ for (const elementId of touchedIds) {
5174
+ const previousKey = previousTargets.get(elementId) ?? "";
5175
+ const nextKey = nextTargets.get(elementId) ?? "";
5176
+ if (previousKey === nextKey) {
5177
+ continue;
4154
5178
  }
4155
- }
4156
- for (const edgeId of hiddenEdgeIds) {
4157
- const edge2 = cy.getElementById(edgeId);
4158
- if (!edge2.empty()) {
4159
- edge2.addClass("is-hidden");
5179
+ const element = cy.getElementById(elementId);
5180
+ if (element.empty()) {
5181
+ continue;
4160
5182
  }
4161
- }
4162
- const hasTrace = Boolean(
4163
- traceResult && (traceResult.highlightNodeIds.length > 0 || traceResult.highlightEdgeIds.length > 0)
4164
- );
4165
- if (!selectedElement && !hasTrace) {
4166
- for (const nodeId of expandedNodeIds) {
4167
- const node = cy.getElementById(nodeId);
4168
- if (!node.empty()) {
4169
- node.addClass("is-expanded-node");
4170
- }
5183
+ const previousClassNames = previousKey.length > 0 ? previousKey.split(" ") : [];
5184
+ const nextClassNames = nextKey.length > 0 ? nextKey.split(" ") : [];
5185
+ const nextClassSet = new Set(nextClassNames);
5186
+ const previousClassSet = new Set(previousClassNames);
5187
+ const classesToRemove = previousClassNames.filter((className) => !nextClassSet.has(className)).join(" ");
5188
+ const classesToAdd = nextClassNames.filter((className) => !previousClassSet.has(className)).join(" ");
5189
+ if (classesToRemove.length > 0) {
5190
+ element.removeClass(classesToRemove);
4171
5191
  }
4172
- for (const edgeId of expandedEdgeIds) {
4173
- const edge2 = cy.getElementById(edgeId);
4174
- if (!edge2.empty()) {
4175
- edge2.addClass("is-expanded-edge");
4176
- }
5192
+ if (classesToAdd.length > 0) {
5193
+ element.addClass(classesToAdd);
4177
5194
  }
4178
- return;
4179
5195
  }
4180
- cy.elements().addClass("is-faded");
4181
- for (const nodeId of expandedNodeIds) {
4182
- const node = cy.getElementById(nodeId);
4183
- if (!node.empty()) {
4184
- node.removeClass("is-faded").addClass("is-context is-expanded-node");
4185
- }
5196
+ });
5197
+ cy.scratch("_elevasis_graphClassTargets", nextTargets);
5198
+ }
5199
+ function syncOverlayLabelPositions(cy, labels, labelElements) {
5200
+ if (labels.length === 0) return;
5201
+ const pan = cy.pan();
5202
+ const zoom = cy.zoom();
5203
+ for (const label of labels) {
5204
+ const element = labelElements.get(label.domain);
5205
+ if (!element) continue;
5206
+ const screenX = label.x * zoom + pan.x;
5207
+ const screenY = label.y * zoom + pan.y;
5208
+ element.style.transform = `translate(${screenX}px, ${screenY}px) translate(-50%, -50%)`;
5209
+ }
5210
+ }
5211
+ function isRenderedNodeWithinViewport(cy, node, padding = 96) {
5212
+ const container = cy.container();
5213
+ if (!container) return true;
5214
+ const rect = container.getBoundingClientRect();
5215
+ const position = node.renderedPosition();
5216
+ return position.x >= padding && position.y >= padding && position.x <= rect.width - padding && position.y <= rect.height - padding;
5217
+ }
5218
+ function syncGraphClasses(cy, selectedElement, traceResult, hiddenIds, hiddenEdgeIds, expandedNodeIds, expandedEdgeIds, visualizationMode, graph, graphIndex, exploreExpandedNodeIds) {
5219
+ const syncKey = JSON.stringify({
5220
+ sel: selectedElement?.id ?? null,
5221
+ expN: [...expandedNodeIds].sort().join(","),
5222
+ expExpl: [...exploreExpandedNodeIds].sort().join(","),
5223
+ expE: [...expandedEdgeIds].sort().join(","),
5224
+ hidN: [...hiddenIds].sort().join(","),
5225
+ hidE: [...hiddenEdgeIds].sort().join(","),
5226
+ trace: traceResult?.highlightNodeIds.join(">") ?? null,
5227
+ vmode: visualizationMode
5228
+ });
5229
+ const lastKey = cy.scratch("_elevasis_lastSyncKey");
5230
+ if (syncKey === lastKey) return;
5231
+ cy.scratch("_elevasis_lastSyncKey", syncKey);
5232
+ const targets = /* @__PURE__ */ new Map();
5233
+ const hasTrace = Boolean(
5234
+ traceResult && (traceResult.highlightNodeIds.length > 0 || traceResult.highlightEdgeIds.length > 0)
5235
+ );
5236
+ const selectedNode = selectedElement?.type === "node" ? cy.getElementById(selectedElement.id) : null;
5237
+ const selectedEdge = selectedElement?.type === "edge" ? cy.getElementById(selectedElement.id) : null;
5238
+ const hasRenderableSelection = Boolean(
5239
+ selectedElement && (selectedNode && !selectedNode.empty() || selectedEdge && !selectedEdge.empty())
5240
+ );
5241
+ const shouldFadeContext = hasTrace || hasRenderableSelection;
5242
+ for (const nodeId of hiddenIds) {
5243
+ addGraphClassTarget(targets, nodeId, "is-hidden");
5244
+ }
5245
+ for (const edgeId of hiddenEdgeIds) {
5246
+ addGraphClassTarget(targets, edgeId, "is-hidden");
5247
+ }
5248
+ if (shouldFadeContext) {
5249
+ cy.elements().forEach((element) => {
5250
+ addGraphClassTarget(targets, element.id(), "is-faded");
5251
+ });
5252
+ }
5253
+ for (const nodeId of expandedNodeIds) {
5254
+ removeGraphClassTarget(targets, nodeId, "is-faded");
5255
+ addGraphClassTarget(targets, nodeId, "is-expanded-node");
5256
+ if (shouldFadeContext) {
5257
+ addGraphClassTarget(targets, nodeId, "is-context");
4186
5258
  }
4187
- for (const edgeId of expandedEdgeIds) {
4188
- const edge2 = cy.getElementById(edgeId);
4189
- if (!edge2.empty()) {
4190
- edge2.removeClass("is-faded").addClass("is-connected is-expanded-edge");
4191
- }
5259
+ }
5260
+ for (const edgeId of expandedEdgeIds) {
5261
+ removeGraphClassTarget(targets, edgeId, "is-faded");
5262
+ addGraphClassTarget(targets, edgeId, "is-expanded-edge");
5263
+ if (shouldFadeContext) {
5264
+ addGraphClassTarget(targets, edgeId, "is-connected");
4192
5265
  }
4193
- if (traceResult) {
4194
- for (const nodeId of traceResult.highlightNodeIds) {
4195
- const node = cy.getElementById(nodeId);
4196
- if (!node.empty()) {
4197
- node.removeClass("is-faded").addClass("is-context is-trace-node");
4198
- }
4199
- }
4200
- for (const edgeId of traceResult.highlightEdgeIds) {
4201
- const edge2 = cy.getElementById(edgeId);
4202
- if (!edge2.empty()) {
4203
- edge2.removeClass("is-faded").addClass("is-connected is-trace-edge");
4204
- }
4205
- }
4206
- if (traceResult.source) {
4207
- cy.getElementById(traceResult.source.id).removeClass("is-faded").addClass("is-trace-endpoint");
4208
- }
4209
- if (traceResult.target) {
4210
- cy.getElementById(traceResult.target.id).removeClass("is-faded").addClass("is-trace-endpoint");
4211
- }
5266
+ }
5267
+ if (traceResult) {
5268
+ for (const nodeId of traceResult.highlightNodeIds) {
5269
+ removeGraphClassTarget(targets, nodeId, "is-faded");
5270
+ addGraphClassTarget(targets, nodeId, "is-context");
5271
+ addGraphClassTarget(targets, nodeId, "is-trace-node");
4212
5272
  }
4213
- if (!selectedElement) {
4214
- return;
5273
+ for (const edgeId of traceResult.highlightEdgeIds) {
5274
+ removeGraphClassTarget(targets, edgeId, "is-faded");
5275
+ addGraphClassTarget(targets, edgeId, "is-connected");
5276
+ addGraphClassTarget(targets, edgeId, "is-trace-edge");
4215
5277
  }
4216
- if (selectedElement.type === "node") {
4217
- const node = cy.getElementById(selectedElement.id);
4218
- if (node.empty()) {
4219
- cy.elements().removeClass("is-faded");
4220
- return;
4221
- }
4222
- const neighborhood = node.closedNeighborhood();
4223
- neighborhood.removeClass("is-faded").addClass("is-context");
4224
- neighborhood.edges().addClass("is-connected");
4225
- node.removeClass("is-faded").addClass("is-selected");
4226
- return;
5278
+ if (traceResult.source) {
5279
+ removeGraphClassTarget(targets, traceResult.source.id, "is-faded");
5280
+ addGraphClassTarget(targets, traceResult.source.id, "is-trace-endpoint");
4227
5281
  }
4228
- const edge = cy.getElementById(selectedElement.id);
4229
- if (edge.empty()) {
4230
- cy.elements().removeClass("is-faded");
4231
- return;
5282
+ if (traceResult.target) {
5283
+ removeGraphClassTarget(targets, traceResult.target.id, "is-faded");
5284
+ addGraphClassTarget(targets, traceResult.target.id, "is-trace-endpoint");
4232
5285
  }
4233
- const relatedElements = edge.connectedNodes().union(edge);
4234
- relatedElements.removeClass("is-faded").addClass("is-context");
4235
- edge.addClass("is-selected is-connected");
4236
- edge.connectedNodes().addClass("is-selected");
4237
- });
5286
+ }
5287
+ if (selectedNode && !selectedNode.empty()) {
5288
+ if (visualizationMode === "explore") {
5289
+ const focus = getExploreFocusSet(graph, selectedNode.id(), exploreExpandedNodeIds, graphIndex);
5290
+ for (const nodeId of focus.nodeIds) {
5291
+ removeGraphClassTarget(targets, nodeId, "is-faded");
5292
+ addGraphClassTarget(targets, nodeId, "is-context");
5293
+ }
5294
+ for (const edgeId of focus.edgeIds) {
5295
+ removeGraphClassTarget(targets, edgeId, "is-faded");
5296
+ addGraphClassTarget(targets, edgeId, "is-connected");
5297
+ }
5298
+ } else {
5299
+ const neighborhood = selectedNode.closedNeighborhood();
5300
+ neighborhood.forEach((element) => {
5301
+ removeGraphClassTarget(targets, element.id(), "is-faded");
5302
+ addGraphClassTarget(targets, element.id(), "is-context");
5303
+ });
5304
+ neighborhood.edges().forEach((edge) => {
5305
+ addGraphClassTarget(targets, edge.id(), "is-connected");
5306
+ });
5307
+ }
5308
+ removeGraphClassTarget(targets, selectedNode.id(), "is-faded");
5309
+ addGraphClassTarget(targets, selectedNode.id(), "is-selected");
5310
+ } else if (selectedEdge && !selectedEdge.empty()) {
5311
+ selectedEdge.connectedNodes().union(selectedEdge).forEach((element) => {
5312
+ removeGraphClassTarget(targets, element.id(), "is-faded");
5313
+ addGraphClassTarget(targets, element.id(), "is-context");
5314
+ });
5315
+ removeGraphClassTarget(targets, selectedEdge.id(), "is-faded");
5316
+ addGraphClassTarget(targets, selectedEdge.id(), "is-selected");
5317
+ addGraphClassTarget(targets, selectedEdge.id(), "is-connected");
5318
+ selectedEdge.connectedNodes().forEach((node) => {
5319
+ addGraphClassTarget(targets, node.id(), "is-selected");
5320
+ });
5321
+ }
5322
+ applyGraphClassTargets(cy, targets);
4238
5323
  }
4239
5324
  function syncCytoscapeElements(cy, elements) {
4240
5325
  const nextIds = new Set(elements.map((element) => element.data.id));
5326
+ let didMutateElements = false;
4241
5327
  cy.batch(() => {
4242
5328
  cy.elements().forEach((element) => {
4243
5329
  if (!nextIds.has(element.id())) {
4244
5330
  element.remove();
5331
+ didMutateElements = true;
4245
5332
  }
4246
5333
  });
4247
5334
  for (const element of elements) {
@@ -4249,14 +5336,33 @@ function syncCytoscapeElements(cy, elements) {
4249
5336
  const existing = cy.getElementById(id);
4250
5337
  if (existing.empty()) {
4251
5338
  cy.add(element);
5339
+ didMutateElements = true;
4252
5340
  continue;
4253
5341
  }
4254
- existing.data(element.data);
5342
+ const currentData = existing.data();
5343
+ let dataChanged = false;
5344
+ for (const key of Object.keys(element.data)) {
5345
+ if (currentData[key] !== element.data[key]) {
5346
+ dataChanged = true;
5347
+ break;
5348
+ }
5349
+ }
5350
+ if (dataChanged) {
5351
+ existing.data(element.data);
5352
+ didMutateElements = true;
5353
+ }
4255
5354
  if ("position" in element && element.position && existing.isNode()) {
4256
- existing.position(element.position);
5355
+ const currentPos = existing.position();
5356
+ if (currentPos.x !== element.position.x || currentPos.y !== element.position.y) {
5357
+ existing.position(element.position);
5358
+ didMutateElements = true;
5359
+ }
4257
5360
  }
4258
5361
  }
4259
5362
  });
5363
+ if (didMutateElements) {
5364
+ cy.scratch("_elevasis_lastSyncKey", null);
5365
+ }
4260
5366
  }
4261
5367
  function OrganizationGraphCanvas({
4262
5368
  graph,
@@ -4268,32 +5374,104 @@ function OrganizationGraphCanvas({
4268
5374
  hiddenEdgeIds,
4269
5375
  expandedNodeIds,
4270
5376
  expandedEdgeIds,
5377
+ expandedClusterDomains,
5378
+ exploreExpandedNodeIds,
4271
5379
  themeTokens,
4272
5380
  commandViewHealthByNodeId,
4273
5381
  focusRequest,
4274
5382
  toolbar,
4275
- onSelectElement
5383
+ parameters,
5384
+ runForceLayout,
5385
+ onSelectElement,
5386
+ onToggleClusterDomain,
5387
+ onToggleNodeExpansion
4276
5388
  }) {
4277
5389
  const containerRef = useRef(null);
4278
5390
  const cytoscapeRef = useRef(null);
4279
5391
  const previousModeRef = useRef(mode);
4280
5392
  const onSelectElementRef = useRef(onSelectElement);
5393
+ const onToggleClusterDomainRef = useRef(onToggleClusterDomain);
5394
+ const onToggleNodeExpansionRef = useRef(onToggleNodeExpansion);
5395
+ const graphRef = useRef(graph);
5396
+ const visualizationModeRef = useRef(visualizationMode);
5397
+ const exploreExpandedNodeIdsRef = useRef(exploreExpandedNodeIds);
4281
5398
  const lastSizeRef = useRef(null);
5399
+ const hasAnimatedInitialFitRef = useRef(false);
5400
+ const previousExploreExpandedNodeIdsRef = useRef(/* @__PURE__ */ new Set());
5401
+ const lastSelectedRef = useRef(null);
5402
+ const hoveredElementIdsRef = useRef(/* @__PURE__ */ new Set());
5403
+ const overlayLabelsRef = useRef([]);
5404
+ const overlayLabelElementsRef = useRef(/* @__PURE__ */ new Map());
4282
5405
  const layoutSelectedElement = visualizationMode === "focus" ? selectedElement : null;
5406
+ const graphIndex = useMemo(() => buildCommandViewGraphIndex(graph), [graph]);
5407
+ const graphIndexRef = useRef(graphIndex);
4283
5408
  const elements = useMemo(
4284
5409
  () => toCytoscapeElementsWithHealth(
4285
5410
  graph,
5411
+ graphIndex,
4286
5412
  themeTokens,
4287
5413
  commandViewHealthByNodeId,
4288
5414
  visualizationMode,
4289
- layoutSelectedElement
5415
+ layoutSelectedElement,
5416
+ expandedClusterDomains,
5417
+ parameters,
5418
+ exploreExpandedNodeIds
4290
5419
  ),
4291
- [commandViewHealthByNodeId, graph, layoutSelectedElement, themeTokens, visualizationMode]
5420
+ [
5421
+ commandViewHealthByNodeId,
5422
+ expandedClusterDomains,
5423
+ exploreExpandedNodeIds,
5424
+ graph,
5425
+ graphIndex,
5426
+ layoutSelectedElement,
5427
+ parameters,
5428
+ themeTokens,
5429
+ visualizationMode
5430
+ ]
5431
+ );
5432
+ const cytoscapeStyle = useMemo(
5433
+ () => createCytoscapeStyle(themeTokens, visualizationMode),
5434
+ [themeTokens, visualizationMode]
4292
5435
  );
4293
- const cytoscapeStyle = useMemo(() => createCytoscapeStyle(themeTokens), [themeTokens]);
5436
+ const expandedDomainsKey = expandedClusterDomains.join(",");
5437
+ const overlayLabels = useMemo(() => {
5438
+ if (visualizationMode === "cluster") {
5439
+ const expanded = new Set(expandedClusterDomains);
5440
+ return getCommandViewClusterZoneLabels().filter((entry) => !expanded.has(entry.domain));
5441
+ }
5442
+ if (visualizationMode === "swimlane") {
5443
+ return getCommandViewSwimlaneLaneLabels();
5444
+ }
5445
+ return [];
5446
+ }, [visualizationMode, expandedDomainsKey]);
5447
+ useEffect(() => {
5448
+ overlayLabelsRef.current = overlayLabels;
5449
+ const cy = cytoscapeRef.current;
5450
+ if (cy) {
5451
+ syncOverlayLabelPositions(cy, overlayLabels, overlayLabelElementsRef.current);
5452
+ }
5453
+ }, [overlayLabels]);
4294
5454
  useEffect(() => {
4295
5455
  onSelectElementRef.current = onSelectElement;
4296
5456
  }, [onSelectElement]);
5457
+ useEffect(() => {
5458
+ onToggleClusterDomainRef.current = onToggleClusterDomain;
5459
+ }, [onToggleClusterDomain]);
5460
+ useEffect(() => {
5461
+ onToggleNodeExpansionRef.current = onToggleNodeExpansion;
5462
+ }, [onToggleNodeExpansion]);
5463
+ useEffect(() => {
5464
+ graphRef.current = graph;
5465
+ }, [graph]);
5466
+ useEffect(() => {
5467
+ graphIndexRef.current = graphIndex;
5468
+ }, [graphIndex]);
5469
+ useEffect(() => {
5470
+ visualizationModeRef.current = visualizationMode;
5471
+ }, [visualizationMode]);
5472
+ useEffect(() => {
5473
+ exploreExpandedNodeIdsRef.current = exploreExpandedNodeIds;
5474
+ }, [exploreExpandedNodeIds]);
4297
5475
  useEffect(() => {
4298
5476
  if (!containerRef.current) {
4299
5477
  return;
@@ -4305,17 +5483,91 @@ function OrganizationGraphCanvas({
4305
5483
  layout: getLayoutOptions(mode, null, traceResult),
4306
5484
  minZoom: 0.22,
4307
5485
  maxZoom: 1.6,
4308
- wheelSensitivity: 1.35
5486
+ wheelSensitivity: 1.35,
5487
+ textureOnViewport: true,
5488
+ pixelRatio: getGraphPixelRatio()
4309
5489
  });
4310
5490
  cytoscapeRef.current = cy;
5491
+ window.__elevasis_cy = cy;
5492
+ const canvasElement = containerRef.current;
5493
+ const resetGraphHover = () => {
5494
+ const hoveredElementIds = hoveredElementIdsRef.current;
5495
+ if (hoveredElementIds.size > 0) {
5496
+ cy.batch(() => {
5497
+ for (const elementId of hoveredElementIds) {
5498
+ const element = cy.getElementById(elementId);
5499
+ if (!element.empty()) {
5500
+ element.removeClass("is-hovered is-hover-neighbor is-hover-connected");
5501
+ }
5502
+ }
5503
+ hoveredElementIds.clear();
5504
+ });
5505
+ }
5506
+ if (canvasElement) {
5507
+ canvasElement.style.cursor = "";
5508
+ }
5509
+ };
5510
+ cy.on("mouseover", "node", (event) => {
5511
+ const node = event.target;
5512
+ const connectedEdges = node.connectedEdges();
5513
+ const hoveredElementIds = hoveredElementIdsRef.current;
5514
+ node.addClass("is-hovered");
5515
+ hoveredElementIds.add(node.id());
5516
+ connectedEdges.addClass("is-hover-connected");
5517
+ connectedEdges.forEach((connectedEdge) => {
5518
+ hoveredElementIds.add(connectedEdge.id());
5519
+ });
5520
+ connectedEdges.connectedNodes().forEach((connectedNode) => {
5521
+ if (connectedNode.id() !== node.id()) {
5522
+ connectedNode.addClass("is-hover-neighbor");
5523
+ hoveredElementIds.add(connectedNode.id());
5524
+ }
5525
+ });
5526
+ if (canvasElement) {
5527
+ canvasElement.style.cursor = "pointer";
5528
+ }
5529
+ });
5530
+ cy.on("mouseout", "node", resetGraphHover);
5531
+ cy.on("mouseover", "edge", (event) => {
5532
+ const edge = event.target;
5533
+ const hoveredElementIds = hoveredElementIdsRef.current;
5534
+ edge.addClass("is-hover-connected");
5535
+ hoveredElementIds.add(edge.id());
5536
+ edge.connectedNodes().addClass("is-hover-neighbor");
5537
+ edge.connectedNodes().forEach((connectedNode) => {
5538
+ hoveredElementIds.add(connectedNode.id());
5539
+ });
5540
+ if (canvasElement) {
5541
+ canvasElement.style.cursor = "pointer";
5542
+ }
5543
+ });
5544
+ cy.on("mouseout", "edge", resetGraphHover);
4311
5545
  cy.on("tap", "node", (event) => {
4312
- onSelectElementRef.current({ type: "node", id: event.target.id() });
5546
+ const node = event.target;
5547
+ if (node.data("kind") === "domain-card") {
5548
+ onToggleClusterDomainRef.current(node.data("domain"));
5549
+ return;
5550
+ }
5551
+ if (visualizationModeRef.current === "explore") {
5552
+ const nodeId = node.id();
5553
+ const expandedSet = exploreExpandedNodeIdsRef.current;
5554
+ const currentGraphIndex = graphIndexRef.current;
5555
+ if (nodeId !== EXPLORE_OM_ROOT_ID && !expandedSet.has(nodeId) && exploreNodeHasChildren(graphRef.current, nodeId, currentGraphIndex)) {
5556
+ const nextSet = getNextExpandedSetForToggle(graphRef.current, nodeId, expandedSet, currentGraphIndex);
5557
+ useCommandViewStore.setState({
5558
+ expandedNodeIdsArray: Array.from(nextSet),
5559
+ expandedNodeIds: nextSet
5560
+ });
5561
+ }
5562
+ }
5563
+ onSelectElementRef.current({ type: "node", id: node.id() });
4313
5564
  });
4314
5565
  cy.on("tap", "edge", (event) => {
4315
5566
  onSelectElementRef.current({ type: "edge", id: event.target.id() });
4316
5567
  });
4317
5568
  cy.on("tap", (event) => {
4318
5569
  if (event.target === cy) {
5570
+ resetGraphHover();
4319
5571
  onSelectElementRef.current(null);
4320
5572
  }
4321
5573
  });
@@ -4335,9 +5587,58 @@ function OrganizationGraphCanvas({
4335
5587
  });
4336
5588
  });
4337
5589
  resizeObserver.observe(containerRef.current);
4338
- syncGraphClasses(cy, selectedElement, traceResult, hiddenIds, hiddenEdgeIds, expandedNodeIds, expandedEdgeIds);
5590
+ syncGraphClasses(
5591
+ cy,
5592
+ selectedElement,
5593
+ traceResult,
5594
+ hiddenIds,
5595
+ hiddenEdgeIds,
5596
+ expandedNodeIds,
5597
+ expandedEdgeIds,
5598
+ visualizationMode,
5599
+ graph,
5600
+ graphIndex,
5601
+ exploreExpandedNodeIds
5602
+ );
5603
+ if (!hasAnimatedInitialFitRef.current) {
5604
+ hasAnimatedInitialFitRef.current = true;
5605
+ const root = cy.nodes('[kind = "organization"]').first();
5606
+ if (root && !root.empty()) {
5607
+ if (shouldReduceGraphMotion()) {
5608
+ cy.fit(void 0, 44);
5609
+ } else {
5610
+ cy.animate(
5611
+ { fit: { eles: root.closedNeighborhood(), padding: 80 }, duration: 280 },
5612
+ {
5613
+ easing: "ease-out-cubic",
5614
+ complete: () => {
5615
+ if (cy.destroyed()) return;
5616
+ cy.animate({ fit: { eles: cy.nodes(), padding: 44 }, duration: 700 }, { easing: "ease-out-cubic" });
5617
+ }
5618
+ }
5619
+ );
5620
+ }
5621
+ } else {
5622
+ cy.fit(void 0, 44);
5623
+ }
5624
+ }
5625
+ let frame = 0;
5626
+ const syncTransform = () => {
5627
+ if (overlayLabelsRef.current.length === 0) return;
5628
+ if (frame) return;
5629
+ frame = window.requestAnimationFrame(() => {
5630
+ frame = 0;
5631
+ if (cy.destroyed()) return;
5632
+ syncOverlayLabelPositions(cy, overlayLabelsRef.current, overlayLabelElementsRef.current);
5633
+ });
5634
+ };
5635
+ cy.on("pan zoom", syncTransform);
5636
+ syncTransform();
4339
5637
  return () => {
5638
+ if (frame) window.cancelAnimationFrame(frame);
5639
+ cy.off("pan zoom", syncTransform);
4340
5640
  resizeObserver.disconnect();
5641
+ hoveredElementIdsRef.current.clear();
4341
5642
  cy.destroy();
4342
5643
  cytoscapeRef.current = null;
4343
5644
  };
@@ -4348,9 +5649,86 @@ function OrganizationGraphCanvas({
4348
5649
  return;
4349
5650
  }
4350
5651
  syncCytoscapeElements(cy, elements);
4351
- cy.layout(getLayoutOptions(mode, selectedElement, traceResult)).run();
4352
- syncGraphClasses(cy, selectedElement, traceResult, hiddenIds, hiddenEdgeIds, expandedNodeIds, expandedEdgeIds);
5652
+ if (visualizationMode === "force") {
5653
+ runForceLayout(cy);
5654
+ } else if (visualizationMode === "explore") ; else {
5655
+ cy.layout(getLayoutOptions(mode, selectedElement, traceResult)).run();
5656
+ }
5657
+ syncGraphClasses(
5658
+ cy,
5659
+ selectedElement,
5660
+ traceResult,
5661
+ hiddenIds,
5662
+ hiddenEdgeIds,
5663
+ expandedNodeIds,
5664
+ expandedEdgeIds,
5665
+ visualizationMode,
5666
+ graph,
5667
+ graphIndex,
5668
+ exploreExpandedNodeIds
5669
+ );
5670
+ if (visualizationMode === "cluster") {
5671
+ window.requestAnimationFrame(() => {
5672
+ if (cy.destroyed()) return;
5673
+ cy.fit(void 0, 44);
5674
+ cy.zoom(cy.zoom() * 1.3);
5675
+ cy.center();
5676
+ });
5677
+ }
4353
5678
  }, [elements]);
5679
+ useEffect(() => {
5680
+ const cy = cytoscapeRef.current;
5681
+ if (!cy || visualizationMode !== "explore") {
5682
+ previousExploreExpandedNodeIdsRef.current = new Set(exploreExpandedNodeIds);
5683
+ return;
5684
+ }
5685
+ const previous = previousExploreExpandedNodeIdsRef.current;
5686
+ previousExploreExpandedNodeIdsRef.current = new Set(exploreExpandedNodeIds);
5687
+ const newlyAddedNodeIds = [];
5688
+ for (const nodeId of exploreExpandedNodeIds) {
5689
+ if (!previous.has(nodeId)) {
5690
+ newlyAddedNodeIds.push(nodeId);
5691
+ }
5692
+ }
5693
+ if (newlyAddedNodeIds.length === 0) {
5694
+ return;
5695
+ }
5696
+ if (shouldReduceGraphMotion()) {
5697
+ return;
5698
+ }
5699
+ const { positions: explorePositions } = getExploreProjection();
5700
+ cy.batch(() => {
5701
+ for (const nodeId of newlyAddedNodeIds) {
5702
+ const anchorPos = explorePositions.get(nodeId) ?? cy.getElementById(nodeId).position();
5703
+ if (!anchorPos) continue;
5704
+ const level = nodeId.startsWith("centroid:") ? 1 : 2;
5705
+ const childPositions = getRadialFromAnchorProjection(graph, nodeId, anchorPos, level, graphIndex);
5706
+ if (childPositions.size > EXPLORE_EXPANSION_ANIMATION_LIMIT) {
5707
+ continue;
5708
+ }
5709
+ for (const [childId, targetPos] of childPositions) {
5710
+ const node = cy.getElementById(childId);
5711
+ if (node.empty()) continue;
5712
+ node.position({ x: anchorPos.x, y: anchorPos.y });
5713
+ node.animate({ position: { x: targetPos.x, y: targetPos.y } }, { duration: 320, easing: "ease-out-cubic" });
5714
+ const edge = cy.getElementById(`expand:${nodeId}:${childId}`);
5715
+ if (!edge.empty()) {
5716
+ edge.style({ width: 0.4, "line-opacity": 0, "underlay-opacity": 0 });
5717
+ edge.animate(
5718
+ { style: { width: 1.55, "line-opacity": 0.56, "underlay-opacity": 0 } },
5719
+ {
5720
+ duration: 280,
5721
+ easing: "ease-out-cubic",
5722
+ complete: () => {
5723
+ if (!edge.removed()) edge.removeStyle("width line-opacity underlay-opacity");
5724
+ }
5725
+ }
5726
+ );
5727
+ }
5728
+ }
5729
+ }
5730
+ });
5731
+ }, [exploreExpandedNodeIds, graph, graphIndex, visualizationMode]);
4354
5732
  useEffect(() => {
4355
5733
  const cy = cytoscapeRef.current;
4356
5734
  if (!cy) {
@@ -4361,34 +5739,98 @@ function OrganizationGraphCanvas({
4361
5739
  if (!modeChanged && mode === "map") {
4362
5740
  return;
4363
5741
  }
4364
- cy.layout(getLayoutOptions(mode, selectedElement, traceResult)).run();
5742
+ if (visualizationMode === "force") {
5743
+ runForceLayout(cy);
5744
+ } else {
5745
+ cy.layout(getLayoutOptions(mode, selectedElement, traceResult)).run();
5746
+ }
4365
5747
  }, [mode, selectedElement, traceResult]);
4366
5748
  useEffect(() => {
4367
5749
  const cy = cytoscapeRef.current;
4368
5750
  if (!cy) {
4369
5751
  return;
4370
5752
  }
4371
- syncGraphClasses(cy, selectedElement, traceResult, hiddenIds, hiddenEdgeIds, expandedNodeIds, expandedEdgeIds);
4372
- if (mode === "map" && selectedElement?.type === "node") {
4373
- const node = cy.getElementById(selectedElement.id);
4374
- if (!node.empty()) {
4375
- const visibleNeighborhood = node.closedNeighborhood().not(".is-hidden");
4376
- cy.stop();
4377
- cy.animate(
4378
- {
4379
- fit: {
4380
- eles: visibleNeighborhood.empty() ? node : visibleNeighborhood,
4381
- padding: 72
4382
- },
4383
- duration: 220
4384
- },
4385
- {
4386
- easing: "ease-out-cubic"
4387
- }
4388
- );
5753
+ syncGraphClasses(
5754
+ cy,
5755
+ selectedElement,
5756
+ traceResult,
5757
+ hiddenIds,
5758
+ hiddenEdgeIds,
5759
+ expandedNodeIds,
5760
+ expandedEdgeIds,
5761
+ visualizationMode,
5762
+ graph,
5763
+ graphIndex,
5764
+ exploreExpandedNodeIds
5765
+ );
5766
+ }, [
5767
+ expandedEdgeIds,
5768
+ expandedNodeIds,
5769
+ exploreExpandedNodeIds,
5770
+ graph,
5771
+ graphIndex,
5772
+ hiddenEdgeIds,
5773
+ hiddenIds,
5774
+ mode,
5775
+ selectedElement,
5776
+ traceResult,
5777
+ visualizationMode
5778
+ ]);
5779
+ useEffect(() => {
5780
+ const cy = cytoscapeRef.current;
5781
+ if (!cy) {
5782
+ return;
5783
+ }
5784
+ const currentSelectionId = selectedElement?.type === "node" ? selectedElement.id : null;
5785
+ if (currentSelectionId === null) {
5786
+ lastSelectedRef.current = null;
5787
+ return;
5788
+ }
5789
+ if (currentSelectionId === lastSelectedRef.current) {
5790
+ return;
5791
+ }
5792
+ lastSelectedRef.current = currentSelectionId;
5793
+ const node = cy.getElementById(currentSelectionId);
5794
+ if (node.empty()) {
5795
+ return;
5796
+ }
5797
+ if (visualizationMode === "explore") {
5798
+ if (isRenderedNodeWithinViewport(cy, node)) {
5799
+ return;
5800
+ }
5801
+ cy.stop();
5802
+ if (shouldReduceGraphMotion()) {
5803
+ cy.center(node);
5804
+ return;
5805
+ }
5806
+ cy.animate(
5807
+ {
5808
+ center: { eles: node },
5809
+ duration: 160
5810
+ },
5811
+ { easing: "ease-out-cubic" }
5812
+ );
5813
+ } else if (mode === "map") {
5814
+ const visibleNeighborhood = node.closedNeighborhood().not(".is-hidden");
5815
+ cy.stop();
5816
+ if (shouldReduceGraphMotion()) {
5817
+ cy.fit(visibleNeighborhood.empty() ? node : visibleNeighborhood, 72);
5818
+ return;
4389
5819
  }
5820
+ cy.animate(
5821
+ {
5822
+ fit: {
5823
+ eles: visibleNeighborhood.empty() ? node : visibleNeighborhood,
5824
+ padding: 72
5825
+ },
5826
+ duration: 220
5827
+ },
5828
+ {
5829
+ easing: "ease-out-cubic"
5830
+ }
5831
+ );
4390
5832
  }
4391
- }, [expandedEdgeIds, expandedNodeIds, hiddenEdgeIds, hiddenIds, mode, selectedElement, traceResult]);
5833
+ }, [selectedElement, visualizationMode]);
4392
5834
  useEffect(() => {
4393
5835
  const cy = cytoscapeRef.current;
4394
5836
  if (!cy || !focusRequest) {
@@ -4400,6 +5842,10 @@ function OrganizationGraphCanvas({
4400
5842
  }
4401
5843
  const visibleNeighborhood = node.closedNeighborhood().not(".is-hidden");
4402
5844
  cy.stop();
5845
+ if (shouldReduceGraphMotion()) {
5846
+ cy.fit(visibleNeighborhood.empty() ? node : visibleNeighborhood, 72);
5847
+ return;
5848
+ }
4403
5849
  cy.animate(
4404
5850
  {
4405
5851
  fit: {
@@ -4427,17 +5873,71 @@ function OrganizationGraphCanvas({
4427
5873
  },
4428
5874
  children: [
4429
5875
  /* @__PURE__ */ jsx(Box, { ref: containerRef, style: { width: "100%", height: "100%" } }),
5876
+ overlayLabels.length > 0 && /* @__PURE__ */ jsx(
5877
+ Box,
5878
+ {
5879
+ "aria-hidden": true,
5880
+ style: {
5881
+ position: "absolute",
5882
+ inset: 0,
5883
+ pointerEvents: "none",
5884
+ overflow: "hidden",
5885
+ zIndex: 1
5886
+ },
5887
+ children: overlayLabels.map((label) => {
5888
+ const tint = DOMAIN_ZONE_TINT_COLOR[label.domain] ?? "#868e96";
5889
+ return /* @__PURE__ */ jsx(
5890
+ "div",
5891
+ {
5892
+ ref: (element) => {
5893
+ if (element) {
5894
+ overlayLabelElementsRef.current.set(label.domain, element);
5895
+ const cy = cytoscapeRef.current;
5896
+ if (cy) {
5897
+ syncOverlayLabelPositions(cy, overlayLabelsRef.current, overlayLabelElementsRef.current);
5898
+ }
5899
+ } else {
5900
+ overlayLabelElementsRef.current.delete(label.domain);
5901
+ }
5902
+ },
5903
+ style: {
5904
+ position: "absolute",
5905
+ left: 0,
5906
+ top: 0,
5907
+ transform: "translate(-9999px, -9999px)",
5908
+ willChange: "transform",
5909
+ fontSize: 11,
5910
+ fontWeight: 600,
5911
+ letterSpacing: "0.06em",
5912
+ textTransform: "uppercase",
5913
+ color: tint,
5914
+ opacity: 0.55,
5915
+ whiteSpace: "nowrap",
5916
+ textShadow: "0 1px 2px var(--color-background, rgba(0,0,0,0.4))"
5917
+ },
5918
+ children: label.text
5919
+ },
5920
+ label.domain
5921
+ );
5922
+ })
5923
+ }
5924
+ ),
4430
5925
  /* @__PURE__ */ jsxs(
4431
5926
  Group,
4432
5927
  {
4433
- gap: "xs",
5928
+ gap: 4,
4434
5929
  wrap: "nowrap",
4435
5930
  style: {
4436
5931
  position: "absolute",
4437
5932
  top: 12,
4438
5933
  right: 12,
4439
5934
  zIndex: 2,
4440
- maxWidth: "calc(100% - 24px)"
5935
+ maxWidth: "calc(100% - 24px)",
5936
+ background: "var(--glass-background, rgba(11,16,21,0.88))",
5937
+ backdropFilter: "var(--glass-blur, blur(8px))",
5938
+ border: "1px solid var(--color-border)",
5939
+ borderRadius: "var(--mantine-radius-md)",
5940
+ padding: 4
4441
5941
  },
4442
5942
  children: [
4443
5943
  toolbar,
@@ -4515,7 +6015,7 @@ function OrganizationGraphPage({ lens = "default", timeRange = "30d" }) {
4515
6015
  const setSelectedNodeId = useCommandViewStore((state) => state.setSelectedNodeId);
4516
6016
  const visualizationMode = useCommandViewStore((state) => state.visualizationMode);
4517
6017
  const setVisualizationMode = useCommandViewStore((state) => state.setVisualizationMode);
4518
- const effectiveVisualizationMode = visualizationMode === "spatial" ? "network" : visualizationMode;
6018
+ const effectiveVisualizationMode = visualizationMode;
4519
6019
  const resourcesHidden = useCommandViewStore((state) => state.resourcesHidden);
4520
6020
  const setResourcesHidden = useCommandViewStore((state) => state.setResourcesHidden);
4521
6021
  const diagnosticsHidden = useCommandViewStore((state) => state.diagnosticsHidden);
@@ -4525,9 +6025,17 @@ function OrganizationGraphPage({ lens = "default", timeRange = "30d" }) {
4525
6025
  const setRevealedIds = useCommandViewStore((state) => state.setRevealedIds);
4526
6026
  const clearRevealedIds = useCommandViewStore((state) => state.clearRevealedIds);
4527
6027
  const markVisibilityInteraction = useCommandViewStore((state) => state.markVisibilityInteraction);
6028
+ const lastOpenBottomTab = useCommandViewStore((state) => state.lastOpenBottomTab);
6029
+ const setLastOpenBottomTab = useCommandViewStore((state) => state.setLastOpenBottomTab);
6030
+ const expandedClusterDomains = useCommandViewStore((state) => state.expandedClusterDomains);
6031
+ const toggleClusterDomain = useCommandViewStore((state) => state.toggleClusterDomain);
6032
+ const exploreExpandedNodeIds = useCommandViewStore((state) => state.expandedNodeIds);
6033
+ const toggleNodeExpansion = useCommandViewStore((state) => state.toggleNodeExpansion);
4528
6034
  const lensConfig = useMemo(() => getOrganizationGraphLensConfig(lens), [lens]);
4529
6035
  const [mode, setMode] = useState(lensConfig.initialMode);
4530
- const [activePanelTab, setActivePanelTab] = useState(lens === "command-view" ? "details" : "controls");
6036
+ const [activePanelTab, setActivePanelTab] = useState(
6037
+ lens === "command-view" ? lastOpenBottomTab : "controls"
6038
+ );
4531
6039
  const [selectedElement, setSelectedElement] = useState(null);
4532
6040
  const [focusRequest, setFocusRequest] = useState(null);
4533
6041
  const [pathTraceSelection, setPathTraceSelection] = useState(EMPTY_TRACE_SELECTION);
@@ -4535,6 +6043,15 @@ function OrganizationGraphPage({ lens = "default", timeRange = "30d" }) {
4535
6043
  const [expandAroundPreview, setExpandAroundPreview] = useState(null);
4536
6044
  const [appliedExpandAroundNodeIds, setAppliedExpandAroundNodeIds] = useState(EMPTY_EXPANDED_NODE_IDS);
4537
6045
  const [appliedExpandAroundEdgeIds, setAppliedExpandAroundEdgeIds] = useState(EMPTY_EXPANDED_EDGE_IDS);
6046
+ const [visualizationParameters, setVisualizationParameters] = useState(
6047
+ () => getDefaultParameters(visualizationMode)
6048
+ );
6049
+ const forceParams = visualizationParameters.mode === "force" ? visualizationParameters : null;
6050
+ const { runLayout: runForceLayout } = useForceLayoutController(
6051
+ effectiveVisualizationMode === "force" ? forceParams : null
6052
+ );
6053
+ const canvasCytoscapeRef = useRef(null);
6054
+ const [legendDismissed, setLegendDismissed] = useState(false);
4538
6055
  const { filters, resetFilters, updateFilters } = useOrganizationGraphFilters(lensConfig.initialFilters);
4539
6056
  const toolbarResetValue = useMemo(
4540
6057
  () => createOrganizationGraphFilters(lensConfig.initialFilters),
@@ -4545,10 +6062,9 @@ function OrganizationGraphPage({ lens = "default", timeRange = "30d" }) {
4545
6062
  const themeSignature = Object.values(rawThemeTokens).join("|");
4546
6063
  const themeTokens = useMemo(() => rawThemeTokens, [themeSignature]);
4547
6064
  useEffect(() => {
4548
- if (visualizationMode === "spatial") {
4549
- setVisualizationMode("network");
4550
- }
4551
- }, [setVisualizationMode, visualizationMode]);
6065
+ setVisualizationParameters(getDefaultParameters(visualizationMode));
6066
+ setLegendDismissed(false);
6067
+ }, [visualizationMode]);
4552
6068
  const baseGraph = useMemo(() => {
4553
6069
  if (!organizationModel) {
4554
6070
  return null;
@@ -4790,6 +6306,9 @@ function OrganizationGraphPage({ lens = "default", timeRange = "30d" }) {
4790
6306
  if (!graph || !selectedElement) {
4791
6307
  return;
4792
6308
  }
6309
+ if (selectedElement.type === "node" && (selectedElement.id === EXPLORE_OM_ROOT_ID || selectedElement.id.startsWith("centroid:"))) {
6310
+ return;
6311
+ }
4793
6312
  const elementExists = selectedElement.type === "node" ? Boolean(visibleGraph?.nodes.some((node) => node.id === selectedElement.id)) : Boolean(visibleGraph?.edges.some((edge) => edge.id === selectedElement.id));
4794
6313
  if (!elementExists) {
4795
6314
  setSelectedElement(null);
@@ -4858,7 +6377,14 @@ function OrganizationGraphPage({ lens = "default", timeRange = "30d" }) {
4858
6377
  ) })
4859
6378
  ] });
4860
6379
  }
4861
- return /* @__PURE__ */ jsx(Stack, { gap: "lg", style: { flex: 1, minHeight: 0 }, children: /* @__PURE__ */ jsx(Tabs, { value: activePanelTab, onChange: setActivePanelTab, keepMounted: false, children: /* @__PURE__ */ jsxs(
6380
+ const handleBottomTabChange = (tab) => {
6381
+ const next = tab === activePanelTab ? null : tab;
6382
+ setActivePanelTab(next);
6383
+ if (lens === "command-view") {
6384
+ setLastOpenBottomTab(next);
6385
+ }
6386
+ };
6387
+ return /* @__PURE__ */ jsx(Stack, { gap: "lg", style: { flex: 1, minHeight: 0 }, children: /* @__PURE__ */ jsx(Tabs, { value: activePanelTab, onChange: handleBottomTabChange, keepMounted: false, children: /* @__PURE__ */ jsxs(
4862
6388
  Paper,
4863
6389
  {
4864
6390
  withBorder: true,
@@ -4890,64 +6416,118 @@ function OrganizationGraphPage({ lens = "default", timeRange = "30d" }) {
4890
6416
  }
4891
6417
  }
4892
6418
  ) : null,
4893
- /* @__PURE__ */ jsx(
6419
+ /* @__PURE__ */ jsxs(
4894
6420
  Box,
4895
6421
  {
4896
6422
  style: {
4897
- height: lens === "command-view" ? "54vh" : "62vh",
4898
- minHeight: lens === "command-view" ? 420 : 520,
4899
- maxHeight: lens === "command-view" ? 620 : void 0,
6423
+ // When the bottom panel is collapsed (activePanelTab === null) for command-view,
6424
+ // expand the canvas to ~94% viewport height to reclaim the freed space.
6425
+ height: lens === "command-view" ? activePanelTab === null ? "94vh" : "78vh" : "62vh",
6426
+ minHeight: lens === "command-view" ? 560 : 520,
6427
+ maxHeight: lens === "command-view" ? activePanelTab === null ? "96vh" : 860 : void 0,
4900
6428
  position: "relative"
4901
6429
  },
4902
- children: renderGraph && renderGraph.nodes.length > 0 ? /* @__PURE__ */ jsx(
4903
- OrganizationGraphCanvas,
4904
- {
4905
- graph: renderGraph,
4906
- mode,
4907
- visualizationMode: lens === "command-view" ? effectiveVisualizationMode : "network",
4908
- selectedElement,
4909
- traceResult: activeTraceResult,
4910
- hiddenIds: EMPTY_GRAPH_HIDDEN_IDS,
4911
- hiddenEdgeIds: EMPTY_GRAPH_HIDDEN_EDGE_IDS,
4912
- expandedNodeIds: lens === "command-view" ? appliedExpandAroundNodeIds : EMPTY_EXPANDED_NODE_IDS,
4913
- expandedEdgeIds: lens === "command-view" ? appliedExpandAroundEdgeIds : EMPTY_EXPANDED_EDGE_IDS,
4914
- themeTokens,
4915
- commandViewHealthByNodeId,
4916
- focusRequest,
4917
- toolbar: /* @__PURE__ */ jsxs(Group, { gap: 4, wrap: "nowrap", children: [
4918
- lens === "command-view" ? /* @__PURE__ */ jsx(
4919
- SegmentedControl,
4920
- {
4921
- value: effectiveVisualizationMode,
4922
- onChange: (value) => setVisualizationMode(value),
4923
- size: "xs",
4924
- data: COMMAND_VIEW_VISUALIZATION_MODES
4925
- }
4926
- ) : null,
4927
- /* @__PURE__ */ jsx(
4928
- SegmentedControl,
4929
- {
4930
- value: mode,
4931
- onChange: (value) => setMode(value),
4932
- size: "xs",
4933
- data: [
4934
- { label: "Map", value: "map" },
4935
- { label: "Trace", value: "trace" },
4936
- { label: "Impact", value: "impact" }
4937
- ]
4938
- }
4939
- )
4940
- ] }),
4941
- onSelectElement: handleSelectElement
4942
- }
4943
- ) : /* @__PURE__ */ jsx(
4944
- EmptyState,
4945
- {
4946
- icon: IconTopologyStar3,
4947
- title: "No graph elements match the current filters",
4948
- description: lens === "command-view" && visibilityProjection.hiddenResourceCount > 0 ? "Reveal resources or adjust filters to bring graph elements back into view." : "Adjust the graph controls to bring semantic nodes, topology nodes, or relationship matches back into view."
4949
- }
4950
- )
6430
+ children: [
6431
+ renderGraph && renderGraph.nodes.length > 0 ? /* @__PURE__ */ jsx(
6432
+ OrganizationGraphCanvas,
6433
+ {
6434
+ graph: renderGraph,
6435
+ mode,
6436
+ visualizationMode: lens === "command-view" ? effectiveVisualizationMode : "network",
6437
+ selectedElement,
6438
+ traceResult: activeTraceResult,
6439
+ hiddenIds: EMPTY_GRAPH_HIDDEN_IDS,
6440
+ hiddenEdgeIds: EMPTY_GRAPH_HIDDEN_EDGE_IDS,
6441
+ expandedNodeIds: lens === "command-view" ? appliedExpandAroundNodeIds : EMPTY_EXPANDED_NODE_IDS,
6442
+ expandedEdgeIds: lens === "command-view" ? appliedExpandAroundEdgeIds : EMPTY_EXPANDED_EDGE_IDS,
6443
+ themeTokens,
6444
+ commandViewHealthByNodeId,
6445
+ focusRequest,
6446
+ parameters: lens === "command-view" ? visualizationParameters : void 0,
6447
+ runForceLayout,
6448
+ toolbar: /* @__PURE__ */ jsxs(Group, { gap: 4, wrap: "nowrap", children: [
6449
+ lens === "command-view" ? /* @__PURE__ */ jsx(
6450
+ SegmentedControl,
6451
+ {
6452
+ value: effectiveVisualizationMode,
6453
+ onChange: (value) => setVisualizationMode(value),
6454
+ size: "xs",
6455
+ data: COMMAND_VIEW_VISUALIZATION_MODES
6456
+ }
6457
+ ) : null,
6458
+ /* @__PURE__ */ jsx(
6459
+ SegmentedControl,
6460
+ {
6461
+ value: mode,
6462
+ onChange: (value) => setMode(value),
6463
+ size: "xs",
6464
+ data: [
6465
+ { label: "Map", value: "map" },
6466
+ { label: "Trace", value: "trace" },
6467
+ { label: "Impact", value: "impact" }
6468
+ ]
6469
+ }
6470
+ ),
6471
+ lens === "command-view" ? /* @__PURE__ */ jsx(
6472
+ VisualizationModeControls,
6473
+ {
6474
+ visualizationMode: effectiveVisualizationMode,
6475
+ parameters: visualizationParameters,
6476
+ onChangeParameters: setVisualizationParameters,
6477
+ onRerunForceLayout: () => {
6478
+ if (canvasCytoscapeRef.current) runForceLayout(canvasCytoscapeRef.current);
6479
+ }
6480
+ }
6481
+ ) : null
6482
+ ] }),
6483
+ expandedClusterDomains: lens === "command-view" ? expandedClusterDomains : [],
6484
+ exploreExpandedNodeIds: lens === "command-view" ? exploreExpandedNodeIds : /* @__PURE__ */ new Set(),
6485
+ onToggleClusterDomain: toggleClusterDomain,
6486
+ onToggleNodeExpansion: toggleNodeExpansion,
6487
+ onSelectElement: handleSelectElement
6488
+ }
6489
+ ) : null,
6490
+ renderGraph && renderGraph.nodes.length > 0 && lens === "command-view" && !legendDismissed ? /* @__PURE__ */ jsx(
6491
+ Box,
6492
+ {
6493
+ style: {
6494
+ position: "absolute",
6495
+ bottom: 12,
6496
+ left: 12,
6497
+ zIndex: 2,
6498
+ pointerEvents: "auto"
6499
+ },
6500
+ children: /* @__PURE__ */ jsx(Legend, { visualizationMode: effectiveVisualizationMode, onDismiss: () => setLegendDismissed(true) })
6501
+ }
6502
+ ) : null,
6503
+ renderGraph && renderGraph.nodes.length > 0 && lens === "command-view" && effectiveVisualizationMode === "explore" ? /* @__PURE__ */ jsx(
6504
+ Box,
6505
+ {
6506
+ style: {
6507
+ position: "absolute",
6508
+ top: 12,
6509
+ left: 12,
6510
+ zIndex: 2,
6511
+ maxWidth: "calc(100% - 24px)",
6512
+ background: "var(--glass-background, rgba(11,16,21,0.88))",
6513
+ backdropFilter: "var(--glass-blur, blur(8px))",
6514
+ border: "1px solid var(--color-border)",
6515
+ borderRadius: "var(--mantine-radius-md)",
6516
+ padding: "6px 10px",
6517
+ pointerEvents: "auto"
6518
+ },
6519
+ children: /* @__PURE__ */ jsx(ExploreBreadcrumb, {})
6520
+ }
6521
+ ) : null,
6522
+ !renderGraph || renderGraph.nodes.length === 0 ? /* @__PURE__ */ jsx(
6523
+ EmptyState,
6524
+ {
6525
+ icon: IconTopologyStar3,
6526
+ title: "No graph elements match the current filters",
6527
+ description: lens === "command-view" && visibilityProjection.hiddenResourceCount > 0 ? "Reveal resources or adjust filters to bring graph elements back into view." : "Adjust the graph controls to bring semantic nodes, topology nodes, or relationship matches back into view."
6528
+ }
6529
+ ) : null
6530
+ ]
4951
6531
  }
4952
6532
  ),
4953
6533
  /* @__PURE__ */ jsx(Box, { p: "xs", children: /* @__PURE__ */ jsxs(Group, { justify: "space-between", align: "center", wrap: "wrap", children: [
@@ -4957,7 +6537,8 @@ function OrganizationGraphPage({ lens = "default", timeRange = "30d" }) {
4957
6537
  lens === "command-view" ? null : /* @__PURE__ */ jsx(Tabs.Tab, { value: "controls", children: "Controls" }),
4958
6538
  /* @__PURE__ */ jsx(Tabs.Tab, { value: "trace", children: "Trace" }),
4959
6539
  /* @__PURE__ */ jsx(Tabs.Tab, { value: "details", children: "Details" }),
4960
- /* @__PURE__ */ jsx(Tabs.Tab, { value: "runtime", children: "Runtime" })
6540
+ /* @__PURE__ */ jsx(Tabs.Tab, { value: "runtime", children: "Runtime" }),
6541
+ lens === "command-view" ? /* @__PURE__ */ jsx(Tabs.Tab, { value: "filters", children: "Filters" }) : null
4961
6542
  ] })
4962
6543
  ] }),
4963
6544
  /* @__PURE__ */ jsxs(Group, { gap: "xs", wrap: "wrap", children: [
@@ -5167,10 +6748,16 @@ function OrganizationGraphPage({ lens = "default", timeRange = "30d" }) {
5167
6748
  onClear: clearExpandAroundState
5168
6749
  }
5169
6750
  ) : null,
6751
+ commandViewData,
5170
6752
  onClearSelection: () => {
5171
6753
  setSelectedElement(null);
5172
6754
  clearRevealedIds();
5173
6755
  clearExpandAroundState();
6756
+ },
6757
+ onSelectNode: (nodeId) => {
6758
+ setMode("map");
6759
+ setSelectedElement({ type: "node", id: nodeId });
6760
+ focusGraphNode(nodeId);
5174
6761
  }
5175
6762
  }
5176
6763
  ) }),
@@ -5218,7 +6805,28 @@ function OrganizationGraphPage({ lens = "default", timeRange = "30d" }) {
5218
6805
  "Topology bridge error: ",
5219
6806
  error.message
5220
6807
  ] }) : null
5221
- ] }) })
6808
+ ] }) }),
6809
+ lens === "command-view" ? /* @__PURE__ */ jsx(Tabs.Panel, { value: "filters", pt: 0, children: /* @__PURE__ */ jsx(
6810
+ FilterPanel,
6811
+ {
6812
+ filters,
6813
+ onChangeFilters: updateFilters,
6814
+ resetValue: toolbarResetValue,
6815
+ disabled: !baseGraph,
6816
+ commandViewData,
6817
+ lens: "command-view",
6818
+ resourcesHidden,
6819
+ diagnosticsHidden,
6820
+ onResourcesHiddenChange: handleResourcesHiddenChange,
6821
+ onDiagnosticsHiddenChange: handleDiagnosticsHiddenChange,
6822
+ onRevealResources: revealAllResources,
6823
+ onResetFilters: handleResetFilters,
6824
+ visibilityCounts: visibilityProjection,
6825
+ graph: visibleGraph,
6826
+ baseGraph,
6827
+ layout: "stack"
6828
+ }
6829
+ ) }) : null
5222
6830
  ]
5223
6831
  }
5224
6832
  )
@@ -6351,37 +7959,9 @@ function AgentListItem({ agent, isSelected, onAgentClick }) {
6351
7959
  }
6352
7960
  );
6353
7961
  }
7962
+
7963
+ // src/features/operations/sidebar/OperationsSidebarTop.tsx
6354
7964
  var OperationsSidebarTop = () => {
6355
- const { currentPath, navigate } = useRouterContext();
6356
- const theme = useMantineTheme();
6357
- const isResourcesSection = currentPath.startsWith("/operations/resources");
6358
- const isSessionsSection = currentPath.startsWith("/operations/sessions");
6359
- const isCommandViewSection = currentPath.startsWith("/operations/command-view");
6360
- if (isSessionsSection || isCommandViewSection) {
6361
- return null;
6362
- }
6363
- if (isResourcesSection) {
6364
- const isActive = currentPath === "/operations/resources";
6365
- return /* @__PURE__ */ jsx(
6366
- Box,
6367
- {
6368
- style: {
6369
- padding: theme.spacing.sm,
6370
- borderBottom: "1px solid var(--color-border)"
6371
- },
6372
- children: /* @__PURE__ */ jsx(
6373
- NavigationButton,
6374
- {
6375
- icon: IconApps,
6376
- label: "Resource Overview",
6377
- isActive,
6378
- hasActiveBackground: isActive,
6379
- onClick: () => navigate("/operations/resources")
6380
- }
6381
- )
6382
- }
6383
- );
6384
- }
6385
7965
  return null;
6386
7966
  };
6387
7967
 
@@ -6610,15 +8190,23 @@ var OperationsSidebar = () => {
6610
8190
  function CommandQueueShell({ children }) {
6611
8191
  return /* @__PURE__ */ jsx(SubshellContentContainer, { children });
6612
8192
  }
6613
- var commandViewSidebarWidth = 320;
8193
+ function OperationsSidebarRouter() {
8194
+ const { currentPath } = useRouterContext();
8195
+ if (currentPath.startsWith("/knowledge/command-view")) {
8196
+ return null;
8197
+ }
8198
+ return /* @__PURE__ */ jsx(OperationsSidebar, {});
8199
+ }
8200
+
8201
+ // src/features/operations/manifest.ts
6614
8202
  var defaultOperationsSidebarWidth = 250;
6615
8203
  var operationsManifest = {
6616
8204
  key: "operations",
6617
8205
  featureId: "operations",
6618
8206
  capabilityIds: ["knowledge.command-view"],
6619
8207
  icon: IconCode,
6620
- sidebar: OperationsSidebar,
6621
- sidebarWidth: ({ currentPath }) => currentPath.startsWith("/knowledge/command-view") ? commandViewSidebarWidth : defaultOperationsSidebarWidth
8208
+ sidebar: OperationsSidebarRouter,
8209
+ sidebarWidth: ({ currentPath }) => currentPath.startsWith("/knowledge/command-view") ? 0 : defaultOperationsSidebarWidth
6622
8210
  };
6623
8211
 
6624
8212
  export { ActionModal, AgentDefinitionDisplay, AgentExecutionLogs, AgentExecutionPanel, AgentSessionGroup, BaseExecutionLogs, BaseExecutionLogsHeader, BaseExecutionLogsStates, CheckpointGroup, CollapsibleJsonSection, CommandQueueDetailPage, CommandQueuePage, CommandQueueShell, CommandQueueSidebar, CommandQueueSidebarMiddle, CommandQueueSidebarTop, CommandQueueTaskRow, CommandViewPage, ConfigCard, ContentSections, ContextUsageBadge, ContractDisplay, ExecuteWorkflowModal, ExecutionErrorSection, ExecutionPanel, LogEntry, LogGroup, NewKnowledgeMapEdge, NewKnowledgeMapGraph, NewKnowledgeMapNode, OperationsSidebar, OperationsSidebarMiddle, OperationsSidebarTop, OrganizationGraphPage, ResourceDefinitionSection, ResourceDetailPage, ResourceErrorState, ResourceFilter, ResourceHeader, ResourceNotFoundState, ResourcesPage, ResourcesSidebar, SessionChatArea, SessionChatInterface, SessionChatPage, SessionDetailsSidebar, SessionExecutionLogs, SessionHeader, SessionListItem, SessionMemory, SessionsPage, SessionsSidebar, ToolsListDisplay, WorkflowDefinitionDisplay, WorkflowExecutionLogs, WorkflowExecutionPanel, getExecutionStatusConfig, getIcon, getLogLevelConfig, iconMap, operationsManifest, useNewKnowledgeMapLayout };