@elevasis/ui 2.22.0 → 2.24.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 (57) hide show
  1. package/dist/app/index.d.ts +2915 -0
  2. package/dist/app/index.js +5 -4
  3. package/dist/{chunk-3HEUGBOT.js → chunk-2WZ635SS.js} +2 -2
  4. package/dist/{chunk-GJ7EIABJ.js → chunk-4NWNS7TX.js} +1 -1
  5. package/dist/{chunk-LKVBEE63.js → chunk-FUEXGRFR.js} +2 -2
  6. package/dist/{chunk-6IA2OMAE.js → chunk-HC2KV6BU.js} +9 -0
  7. package/dist/{chunk-IBUYJXA3.js → chunk-KCJ6VATY.js} +1133 -578
  8. package/dist/{chunk-WWJ6S2HQ.js → chunk-KLFIJDTD.js} +1 -1
  9. package/dist/{chunk-COG4ABRI.js → chunk-M2HWJY6O.js} +704 -375
  10. package/dist/{chunk-LVJGPE6H.js → chunk-MTR6AN2C.js} +3 -12
  11. package/dist/chunk-OWHQ65EQ.js +211 -0
  12. package/dist/{chunk-XOTJNW4Q.js → chunk-QIW6OCEI.js} +18 -1
  13. package/dist/{chunk-B4FHWKEF.js → chunk-QULLZ5PE.js} +193 -10
  14. package/dist/{chunk-QZJM3RYI.js → chunk-SNHGSCKH.js} +1 -1
  15. package/dist/{chunk-LVUCBY7X.js → chunk-UDJE54WN.js} +85 -3
  16. package/dist/{chunk-IOTLB6ND.js → chunk-VGNAV3TH.js} +406 -195
  17. package/dist/{chunk-BSZRKBAW.js → chunk-YBZT7MJR.js} +3 -3
  18. package/dist/{chunk-SQ5JGELM.js → chunk-ZDKQNQ4X.js} +19 -1
  19. package/dist/components/index.d.ts +500 -466
  20. package/dist/components/index.js +75 -32
  21. package/dist/components/navigation/index.js +2 -2
  22. package/dist/features/auth/index.d.ts +472 -389
  23. package/dist/features/crm/index.d.ts +468 -391
  24. package/dist/features/crm/index.js +8 -8
  25. package/dist/features/dashboard/index.js +8 -8
  26. package/dist/features/delivery/index.d.ts +466 -383
  27. package/dist/features/delivery/index.js +8 -8
  28. package/dist/features/lead-gen/index.d.ts +213 -65
  29. package/dist/features/lead-gen/index.js +9 -8
  30. package/dist/features/monitoring/index.js +9 -9
  31. package/dist/features/monitoring/requests/index.js +7 -7
  32. package/dist/features/operations/index.js +11 -10
  33. package/dist/features/settings/index.d.ts +472 -389
  34. package/dist/features/settings/index.js +9 -9
  35. package/dist/hooks/delivery/index.d.ts +466 -383
  36. package/dist/hooks/index.d.ts +967 -744
  37. package/dist/hooks/index.js +7 -7
  38. package/dist/hooks/published.d.ts +967 -744
  39. package/dist/hooks/published.js +7 -7
  40. package/dist/index.d.ts +1360 -1069
  41. package/dist/index.js +8 -8
  42. package/dist/initialization/index.d.ts +472 -389
  43. package/dist/organization/index.d.ts +11 -1
  44. package/dist/organization/index.js +2 -2
  45. package/dist/profile/index.d.ts +472 -389
  46. package/dist/provider/index.d.ts +3132 -169
  47. package/dist/provider/index.js +6 -6
  48. package/dist/provider/published.d.ts +3098 -168
  49. package/dist/provider/published.js +3 -3
  50. package/dist/supabase/index.d.ts +577 -413
  51. package/dist/test-utils/index.d.ts +21 -1
  52. package/dist/test-utils/index.js +13 -4
  53. package/dist/theme/index.js +2 -2
  54. package/dist/types/index.d.ts +472 -389
  55. package/package.json +2 -2
  56. package/src/test-utils/README.md +2 -0
  57. /package/dist/{chunk-ZBCTB5CA.js → chunk-EIOJNUPL.js} +0 -0
@@ -1,14 +1,15 @@
1
1
  import { ChatHeader, ChatSidebar } from './chunk-ROSMICXG.js';
2
2
  import { SubshellSidebarLoader, SubshellLoader } from './chunk-3JCMO7SD.js';
3
- import { BaseNode, useGraphTheme, BaseEdge, FormFieldRenderer, ExecutionStats, UnifiedWorkflowGraph, WorkflowExecutionTimeline, AgentExecutionVisualizer, AgentExecutionTimeline, GraphBackground, GraphFitViewButton, GraphFitViewHandler } from './chunk-LKVBEE63.js';
4
- import { ResourceHealthPanel } from './chunk-GJ7EIABJ.js';
3
+ import { BaseNode, useGraphTheme, BaseEdge, FormFieldRenderer, ExecutionStats, UnifiedWorkflowGraph, WorkflowExecutionTimeline, AgentExecutionVisualizer, AgentExecutionTimeline, GraphBackground, GraphFitViewButton, GraphFitViewHandler } from './chunk-FUEXGRFR.js';
4
+ import { LabelSchema, ColorTokenSchema, IconNameSchema, PathSchema, DescriptionSchema, DisplayMetadataSchema, ModelIdSchema, ReferenceIdsSchema, OrganizationModelSalesSchema } from './chunk-OWHQ65EQ.js';
5
+ import { ResourceHealthPanel } from './chunk-4NWNS7TX.js';
5
6
  import { useCyberColors, CyberDonut } from './chunk-TSSKOQBX.js';
6
7
  import { AppShellLoader } from './chunk-M25JL54Z.js';
7
8
  import { PageContainer } from './chunk-BZZCNLT6.js';
8
9
  import { SubshellSidebarSection } from './chunk-IIMU5YAJ.js';
9
10
  import { CustomModal, ConfirmationModal } from './chunk-KVJ3LFH2.js';
10
11
  import { getResourceStatusColor, useTimelineData, useAgentIterationData, getStatusIcon } from './chunk-E4WQGJNS.js';
11
- import { useErrorDetail, useExecution, useArchivedLogs, useDeleteExecution, useRetryExecution, useCancelExecution, useCommandQueueTotals, useStatusFilter, useResourceSearch, useResourcesDomainFilters, usePaginationState, useResources, useRecentExecutionsByResource, filterByDomainFilters, collectResourceFilterFacets, useExecuteAsync, useResourceDefinition, isSessionCapable, useDeleteTask, useCommandQueue, useSubmitAction, useCommandViewData, useCommandViewStats, useCommandViewStore, useResourceExecutions, useCheckpointTasks, COMMAND_VIEW_VISUALIZATION_MODES, useExecutionPanelState, useDeleteSession, useCreateSession, useSessions, useSessionExecutions, useSession, useBulkDeleteExecutions, getCommandViewGraphPositions } from './chunk-B4FHWKEF.js';
12
+ import { useErrorDetail, useExecution, useArchivedLogs, useDeleteExecution, useRetryExecution, useCancelExecution, useCommandQueueTotals, useStatusFilter, useResourceSearch, useResourcesDomainFilters, usePaginationState, useResources, useRecentExecutionsByResource, filterByDomainFilters, collectResourceFilterFacets, useExecuteAsync, useResourceDefinition, isSessionCapable, useDeleteTask, useCommandQueue, useSubmitAction, useCommandViewData, useCommandViewStats, useCommandViewStore, useResourceExecutions, useCheckpointTasks, COMMAND_VIEW_VISUALIZATION_MODES, useExecutionPanelState, useDeleteSession, useCreateSession, useSessions, useSessionExecutions, useSession, useBulkDeleteExecutions, getCommandViewGraphPositions } from './chunk-QULLZ5PE.js';
12
13
  import { showApiErrorNotification, showSuccessNotification } from './chunk-Z6FAH4XV.js';
13
14
  import { useGraphHighlighting, calculateGraphHeight, Graph_module_css_default, GRAPH_CONSTANTS } from './chunk-22UVE3RA.js';
14
15
  import { useMergedExecution } from './chunk-3ZMAGTWF.js';
@@ -25,7 +26,7 @@ import { ResourceStatusColors, toWorkflowLogMessages } from './chunk-KRWALB24.js
25
26
  import { useInitialization } from './chunk-DK2HVHCY.js';
26
27
  import { useOrganization } from './chunk-DD3CCMCZ.js';
27
28
  import { z } from 'zod';
28
- 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, Modal, LoadingOverlay, Loader, SegmentedControl, ScrollArea, Checkbox, MultiSelect, Switch } from '@mantine/core';
29
+ 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, Modal, LoadingOverlay, Loader, SegmentedControl, ScrollArea, Checkbox, NumberInput, MultiSelect, Switch } from '@mantine/core';
29
30
  import { IconBrain, IconFileText, IconMail, IconSend, IconClock, IconArrowUp, IconMessageCircle, IconRocket, IconEye, IconEdit, IconAlertTriangle, IconRefresh, IconX, IconCheck, IconCode, IconAlertCircle, IconChevronRight, IconTool, IconSettings, IconCpu, IconClockHour4, IconVersions, IconPlayerPlay, IconNetwork, IconSitemap, IconCopy, IconPlayerStop, IconReload, IconTrash, IconTerminal2, IconBug, IconChevronDown, 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, IconShare2, IconHistory } from '@tabler/icons-react';
30
31
  import { useForm } from '@mantine/form';
31
32
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
@@ -40,51 +41,6 @@ import cytoscape from 'cytoscape';
40
41
  import { create } from 'zustand';
41
42
  import { formatDistanceToNow } from 'date-fns';
42
43
 
43
- var ModelIdSchema = z.string().trim().min(1).max(100).regex(/^[a-z0-9]+(?:[-._][a-z0-9]+)*$/, "IDs must be lowercase and use -, _, or . separators");
44
- var LabelSchema = z.string().trim().min(1).max(120);
45
- var DescriptionSchema = z.string().trim().min(1).max(2e3);
46
- var ColorTokenSchema = z.string().trim().min(1).max(50);
47
- var IconNameSchema = z.string().trim().min(1).max(80);
48
- var PathSchema = z.string().trim().startsWith("/").max(300);
49
- var ReferenceIdsSchema = z.array(ModelIdSchema).default([]);
50
- var DisplayMetadataSchema = z.object({
51
- label: LabelSchema,
52
- description: DescriptionSchema.optional(),
53
- color: ColorTokenSchema.optional(),
54
- icon: IconNameSchema.optional()
55
- });
56
- var TechStackEntrySchema = z.object({
57
- /** Name of the external platform (e.g. "HubSpot", "Stripe", "Notion"). */
58
- platform: z.string().trim().min(1).max(200),
59
- /** Free-form description of what this integration is used for. */
60
- purpose: z.string().trim().min(1).max(500),
61
- /**
62
- * Health of the credential backing this integration.
63
- * - configured: credential present and valid
64
- * - pending: not yet set up
65
- * - expired: credential existed but has lapsed
66
- * - missing: expected but not present
67
- */
68
- credentialStatus: z.enum(["configured", "pending", "expired", "missing"]),
69
- /**
70
- * Whether this integration is the primary system of record for its domain
71
- * (e.g. HubSpot is SoR for contacts). Defaults to false.
72
- */
73
- isSystemOfRecord: z.boolean().default(false)
74
- });
75
- DisplayMetadataSchema.extend({
76
- id: ModelIdSchema,
77
- resourceId: z.string().trim().min(1).max(255),
78
- resourceType: z.enum(["workflow", "agent", "trigger", "integration", "external", "human_checkpoint"]),
79
- featureIds: ReferenceIdsSchema,
80
- entityIds: ReferenceIdsSchema,
81
- surfaceIds: ReferenceIdsSchema,
82
- capabilityIds: ReferenceIdsSchema,
83
- /** Optional tech-stack metadata for external-SaaS integrations. */
84
- techStack: TechStackEntrySchema.optional()
85
- });
86
-
87
- // ../core/src/organization-model/domains/features.ts
88
44
  var NodeIdPathSchema = z.string().trim().min(1).max(100).regex(/^([a-z0-9-]+)(\.[a-z0-9-]+)*$/, "Node IDs must be lowercase dotted paths");
89
45
  z.string().trim().min(1).max(200).regex(/^[a-z]+:([a-z0-9-]+)(\.[a-z0-9-]+)*$/, "Node references must use kind:dotted-path");
90
46
  var UiPositionSchema = z.enum(["sidebar-primary", "sidebar-bottom"]);
@@ -110,26 +66,6 @@ var OrganizationModelBrandingSchema = z.object({
110
66
  dark: z.string().trim().min(1).max(2048).optional()
111
67
  }).default({})
112
68
  });
113
- var SalesStageSemanticClassSchema = z.enum(["open", "active", "nurturing", "closed_won", "closed_lost"]);
114
- var SalesStageSchema = DisplayMetadataSchema.extend({
115
- id: ModelIdSchema,
116
- order: z.number().int().min(0),
117
- semanticClass: SalesStageSemanticClassSchema,
118
- surfaceIds: ReferenceIdsSchema,
119
- resourceIds: ReferenceIdsSchema
120
- });
121
- var SalesPipelineSchema = z.object({
122
- id: ModelIdSchema,
123
- label: z.string().trim().min(1).max(120),
124
- description: DescriptionSchema.optional(),
125
- entityId: ModelIdSchema,
126
- stages: z.array(SalesStageSchema).min(1)
127
- });
128
- var OrganizationModelSalesSchema = z.object({
129
- entityId: ModelIdSchema,
130
- defaultPipelineId: ModelIdSchema,
131
- pipelines: z.array(SalesPipelineSchema).min(1)
132
- });
133
69
  var ProjectsDomainStateSchema = DisplayMetadataSchema.extend({
134
70
  id: ModelIdSchema,
135
71
  order: z.number().int().min(0)
@@ -4684,7 +4620,7 @@ function titleCase2(value) {
4684
4620
  return value.charAt(0).toUpperCase() + value.slice(1);
4685
4621
  }
4686
4622
  function MetricCard({ metric }) {
4687
- return /* @__PURE__ */ jsx(Paper, { withBorder: true, radius: "md", p: "sm", style: { background: "var(--mantine-color-body)" }, children: /* @__PURE__ */ jsxs(Stack, { gap: 2, children: [
4623
+ return /* @__PURE__ */ jsx(Card, { withBorder: true, radius: "md", p: "sm", children: /* @__PURE__ */ jsxs(Stack, { gap: 2, children: [
4688
4624
  /* @__PURE__ */ jsx(Text, { size: "xs", tt: "uppercase", fw: 700, c: "dimmed", children: metric.label }),
4689
4625
  /* @__PURE__ */ jsx(Text, { size: "lg", fw: 800, children: metric.value }),
4690
4626
  metric.hint ? /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: metric.hint }) : null
@@ -4769,6 +4705,7 @@ function OrganizationGraphDetailPanel({
4769
4705
  selectedElement,
4770
4706
  supplementalSummary,
4771
4707
  followUpSections,
4708
+ expandAroundPanel,
4772
4709
  onClearSelection,
4773
4710
  className
4774
4711
  }) {
@@ -4806,6 +4743,7 @@ function OrganizationGraphDetailPanel({
4806
4743
  onClearSelection ? /* @__PURE__ */ jsx(Button, { size: "xs", variant: "subtle", onClick: onClearSelection, children: "Clear" }) : null
4807
4744
  ] }),
4808
4745
  /* @__PURE__ */ jsx(SimpleGrid, { cols: { base: 2, md: 4 }, spacing: "sm", children: state.metrics.map((metric) => /* @__PURE__ */ jsx(MetricCard, { metric }, metric.label)) }),
4746
+ expandAroundPanel,
4809
4747
  supplementalSummary && (supplementalSummary.metrics.length > 0 || supplementalSummary.metadata.length > 0) ? /* @__PURE__ */ jsxs(Fragment, { children: [
4810
4748
  /* @__PURE__ */ jsx(
4811
4749
  Divider,
@@ -5833,184 +5771,895 @@ function buildCommandViewDrillDownSections({
5833
5771
  }
5834
5772
  return sections;
5835
5773
  }
5836
- var cellStyle = {
5837
- padding: "var(--mantine-spacing-sm) var(--mantine-spacing-md)",
5838
- display: "flex",
5839
- alignItems: "center",
5840
- gap: "var(--mantine-spacing-sm)",
5841
- minWidth: 0
5842
- };
5843
- function CommandViewHealthStrip({
5844
- overview,
5845
- hotspots,
5846
- visibleResources,
5847
- hiddenResources = 0,
5848
- resourcesHidden,
5849
- selectedLabel,
5850
- onFocusHotspot,
5851
- onResourcesHiddenChange,
5852
- onResetFocus
5853
- }) {
5854
- return /* @__PURE__ */ jsxs("div", { style: { display: "flex", justifyContent: "space-between" }, children: [
5855
- /* @__PURE__ */ jsxs("div", { style: cellStyle, children: [
5856
- /* @__PURE__ */ jsx(IconActivityHeartbeat, { size: 16, style: { flexShrink: 0, color: "var(--color-text-dimmed)" } }),
5857
- /* @__PURE__ */ jsx(Text, { size: "sm", fw: 700, style: { flexShrink: 0 }, children: overview?.successRate == null ? "N/A" : `${Math.round(overview.successRate)}%` }),
5858
- /* @__PURE__ */ jsxs(Group, { gap: 4, wrap: "nowrap", children: [
5859
- /* @__PURE__ */ jsx(Badge, { variant: "light", color: "green", size: "xs", children: overview?.successCount ?? 0 }),
5860
- /* @__PURE__ */ jsx(Badge, { variant: "light", color: "red", size: "xs", children: overview?.failureCount ?? 0 }),
5861
- /* @__PURE__ */ jsx(Badge, { variant: "light", color: "yellow", size: "xs", children: overview?.warningCount ?? 0 })
5862
- ] }),
5863
- /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", truncate: true, style: { flexShrink: 1, minWidth: 0 }, children: overview ? `${overview.totalRuns} runs / ${overview.trackedResources} resources` : "" })
5864
- ] }),
5865
- /* @__PURE__ */ jsxs("div", { style: cellStyle, children: [
5866
- /* @__PURE__ */ jsx(IconAlertTriangle, { size: 16, style: { flexShrink: 0, color: "var(--color-text-dimmed)" } }),
5867
- hotspots.length > 0 ? /* @__PURE__ */ jsx(Group, { gap: "xs", wrap: "nowrap", style: { minWidth: 0, overflow: "hidden" }, children: hotspots.map((hotspot) => /* @__PURE__ */ jsx(Group, { gap: 4, wrap: "nowrap", children: /* @__PURE__ */ jsx(
5868
- Badge,
5869
- {
5870
- variant: "light",
5871
- color: "red",
5872
- size: "xs",
5873
- style: { cursor: "pointer", flexShrink: 0 },
5874
- onClick: () => onFocusHotspot(hotspot.resourceId),
5875
- children: hotspot.label
5876
- }
5877
- ) }, hotspot.nodeId)) }) : /* @__PURE__ */ jsx(Badge, { variant: "light", color: "green", size: "xs", leftSection: /* @__PURE__ */ jsx(IconCircleCheck, { size: 10 }), children: "No hotspots" })
5878
- ] }),
5879
- /* @__PURE__ */ jsxs("div", { style: cellStyle, children: [
5880
- /* @__PURE__ */ jsx(IconClockPause, { size: 16, style: { flexShrink: 0, color: "var(--color-text-dimmed)" } }),
5881
- /* @__PURE__ */ jsx(Text, { size: "sm", fw: 700, style: { flexShrink: 0 }, children: overview?.pendingApprovals ?? 0 }),
5882
- /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", truncate: true, children: [
5883
- "pending / ",
5884
- overview?.activeHumanCheckpoints ?? 0,
5885
- " queues"
5886
- ] })
5887
- ] }),
5888
- /* @__PURE__ */ jsxs("div", { style: cellStyle, children: [
5889
- /* @__PURE__ */ jsx(IconTopologyStar3, { size: 16, style: { flexShrink: 0, color: "var(--color-text-dimmed)" } }),
5890
- /* @__PURE__ */ jsx(Text, { size: "xs", fw: 600, truncate: true, style: { minWidth: 0 }, children: selectedLabel ?? `${visibleResources} visible / ${hiddenResources} hidden` }),
5891
- /* @__PURE__ */ jsx(
5892
- Button,
5893
- {
5894
- size: "compact-xs",
5895
- variant: resourcesHidden ? "light" : "subtle",
5896
- onClick: () => onResourcesHiddenChange(!resourcesHidden),
5897
- style: { flexShrink: 0 },
5898
- children: resourcesHidden ? "Show resources" : "Hide resources"
5899
- }
5900
- ),
5901
- /* @__PURE__ */ jsx(Button, { size: "compact-xs", variant: "subtle", onClick: onResetFocus, style: { flexShrink: 0 }, children: "Reset" })
5902
- ] })
5903
- ] });
5904
- }
5905
- var FILTER_STATE_ICONS2 = {
5906
- neutral: IconCircleDashed,
5907
- include: IconCircleCheck,
5908
- exclude: IconCircleX
5909
- };
5910
- var FILTER_STATE_COLORS2 = {
5911
- neutral: "var(--color-text-dimmed)",
5912
- include: "var(--mantine-color-green-6)",
5913
- exclude: "var(--mantine-color-red-6)"
5774
+
5775
+ // src/features/operations/organization-graph/path-tracing/trace.ts
5776
+ var NODE_KIND_ORDER = {
5777
+ organization: 0,
5778
+ feature: 1,
5779
+ surface: 2,
5780
+ capability: 3,
5781
+ entity: 4,
5782
+ resource: 5
5914
5783
  };
5915
- var FILTER_STATE_LABELS2 = {
5916
- neutral: "Not filtered",
5917
- include: "Include only",
5918
- exclude: "Exclude"
5784
+ var NODE_KIND_LABEL = {
5785
+ organization: "Organization",
5786
+ feature: "Feature",
5787
+ surface: "Surface",
5788
+ capability: "Capability",
5789
+ entity: "Entity",
5790
+ resource: "Resource"
5919
5791
  };
5920
- function getCommandViewResources(commandViewData) {
5921
- if (!commandViewData) {
5922
- return [];
5792
+ function getNodeLabel(node) {
5793
+ return node.label || node.sourceId || node.id;
5794
+ }
5795
+ function compareTraceNodes(a, b) {
5796
+ const kindDelta = NODE_KIND_ORDER[a.kind] - NODE_KIND_ORDER[b.kind];
5797
+ if (kindDelta !== 0) {
5798
+ return kindDelta;
5923
5799
  }
5924
- return [
5925
- ...commandViewData.agents,
5926
- ...commandViewData.workflows,
5927
- ...commandViewData.triggers,
5928
- ...commandViewData.integrations,
5929
- ...commandViewData.externalResources,
5930
- ...commandViewData.humanCheckpoints
5931
- ];
5800
+ const labelDelta = getNodeLabel(a).localeCompare(getNodeLabel(b));
5801
+ if (labelDelta !== 0) {
5802
+ return labelDelta;
5803
+ }
5804
+ return a.id.localeCompare(b.id);
5932
5805
  }
5933
- function nextDomainFilterState(current) {
5934
- if (current === "neutral") return "include";
5935
- if (current === "include") return "exclude";
5936
- return "neutral";
5806
+ function buildMissingNodeMessage(selection, missingNodeIds) {
5807
+ if (missingNodeIds.length === 0) {
5808
+ return "The selected trace is not available.";
5809
+ }
5810
+ if (missingNodeIds.length === 1) {
5811
+ const missingId = missingNodeIds[0];
5812
+ return `Graph node "${missingId}" is not available.`;
5813
+ }
5814
+ return `Graph nodes "${selection.sourceId}" and "${selection.targetId}" are not available.`;
5937
5815
  }
5938
- function FilterPanel({
5939
- filters,
5940
- onChangeFilters,
5941
- resetValue,
5942
- disabled = false,
5943
- commandViewData,
5944
- lens,
5945
- resourcesHidden,
5946
- diagnosticsHidden,
5947
- onResourcesHiddenChange,
5948
- onDiagnosticsHiddenChange,
5949
- onRevealResources,
5950
- onResetFilters,
5951
- visibilityCounts,
5952
- graph,
5953
- baseGraph,
5954
- layout = "grid"
5955
- }) {
5956
- const resourceFacets = collectResourceFilterFacets(getCommandViewResources(commandViewData));
5957
- const Section = ({ children }) => layout === "stack" ? /* @__PURE__ */ jsx(
5958
- Stack,
5959
- {
5960
- gap: "sm",
5961
- pb: "sm",
5962
- style: {
5963
- borderBottom: "1px solid var(--color-border)"
5964
- },
5965
- children
5816
+ function buildPathNotFoundMessage(source, target) {
5817
+ return `No directed path found from "${source.label}" to "${target.label}".`;
5818
+ }
5819
+ function buildOrganizationGraphTraceIndex(graph) {
5820
+ const nodesById = /* @__PURE__ */ new Map();
5821
+ const edgesById = /* @__PURE__ */ new Map();
5822
+ const outgoingEdgesByNodeId = /* @__PURE__ */ new Map();
5823
+ const incomingEdgesByNodeId = /* @__PURE__ */ new Map();
5824
+ for (const node of graph.nodes) {
5825
+ nodesById.set(node.id, node);
5826
+ outgoingEdgesByNodeId.set(node.id, []);
5827
+ incomingEdgesByNodeId.set(node.id, []);
5828
+ }
5829
+ for (const edge of graph.edges) {
5830
+ edgesById.set(edge.id, edge);
5831
+ const outgoingEdges = outgoingEdgesByNodeId.get(edge.sourceId);
5832
+ if (outgoingEdges) {
5833
+ outgoingEdges.push(edge);
5966
5834
  }
5967
- ) : /* @__PURE__ */ jsx(Paper, { withBorder: true, p: "md", radius: "md", style: { background: "var(--color-surface)" }, children });
5968
- const updateDomainFilter = (domainId) => {
5969
- const current = filters.domainFilters[domainId] ?? "neutral";
5970
- onChangeFilters({
5971
- ...filters,
5972
- domainFilters: {
5973
- ...filters.domainFilters,
5974
- [domainId]: nextDomainFilterState(current)
5975
- }
5976
- });
5977
- };
5978
- const handleResourceVisibilityAction = () => {
5979
- if (resourcesHidden) {
5980
- onRevealResources();
5981
- return;
5835
+ const incomingEdges = incomingEdgesByNodeId.get(edge.targetId);
5836
+ if (incomingEdges) {
5837
+ incomingEdges.push(edge);
5982
5838
  }
5983
- onResourcesHiddenChange(true);
5839
+ }
5840
+ return {
5841
+ nodesById,
5842
+ edgesById,
5843
+ outgoingEdgesByNodeId,
5844
+ incomingEdgesByNodeId
5984
5845
  };
5985
- const content = /* @__PURE__ */ jsxs(Fragment, { children: [
5986
- /* @__PURE__ */ jsx(Section, { children: /* @__PURE__ */ jsxs(Stack, { gap: "sm", children: [
5987
- /* @__PURE__ */ jsx(Text, { size: "sm", fw: 700, children: "Filters" }),
5988
- /* @__PURE__ */ jsx(
5989
- OrganizationGraphFilterToolbar,
5990
- {
5991
- value: filters,
5992
- onChange: onChangeFilters,
5993
- disabled,
5994
- resetValue
5995
- }
5996
- )
5997
- ] }) }),
5998
- /* @__PURE__ */ jsx(Section, { children: /* @__PURE__ */ jsxs(Stack, { gap: "sm", children: [
5999
- /* @__PURE__ */ jsxs(Group, { justify: "space-between", align: "center", children: [
6000
- /* @__PURE__ */ jsx(Text, { size: "sm", fw: 700, children: "Visibility" }),
6001
- /* @__PURE__ */ jsxs(Badge, { variant: "light", children: [
6002
- visibilityCounts.visibleResourceCount,
6003
- "/",
6004
- visibilityCounts.totalResourceCount,
6005
- " resources"
6006
- ] })
6007
- ] }),
6008
- lens === "command-view" ? /* @__PURE__ */ jsxs(Fragment, { children: [
6009
- /* @__PURE__ */ jsx(
6010
- Checkbox,
6011
- {
6012
- label: "Hide resource nodes",
6013
- description: "Keep the organization structure readable and reveal resource nodes as needed.",
5846
+ }
5847
+ function getOrganizationGraphTraceNodeKindLabel(kind) {
5848
+ return NODE_KIND_LABEL[kind];
5849
+ }
5850
+ function formatOrganizationGraphTraceNodeOptionLabel(option) {
5851
+ return `${option.label} \xB7 ${getOrganizationGraphTraceNodeKindLabel(option.kind)}`;
5852
+ }
5853
+ function getOrganizationGraphTraceNodeOptions(graph) {
5854
+ return [...graph.nodes].sort(compareTraceNodes).map((node) => ({
5855
+ id: node.id,
5856
+ label: getNodeLabel(node),
5857
+ kind: node.kind,
5858
+ sourceId: node.sourceId,
5859
+ description: node.description,
5860
+ enabled: node.enabled
5861
+ }));
5862
+ }
5863
+ function resolveOrganizationGraphPathTrace(graph, selection) {
5864
+ const index = buildOrganizationGraphTraceIndex(graph);
5865
+ const source = selection.sourceId ? index.nodesById.get(selection.sourceId) ?? null : null;
5866
+ const target = selection.targetId ? index.nodesById.get(selection.targetId) ?? null : null;
5867
+ const missingNodeIds = [
5868
+ selection.sourceId && !source ? selection.sourceId : null,
5869
+ selection.targetId && !target ? selection.targetId : null
5870
+ ].filter((value) => Boolean(value));
5871
+ if (!selection.sourceId && !selection.targetId) {
5872
+ return {
5873
+ status: "idle",
5874
+ selection,
5875
+ source,
5876
+ target,
5877
+ missingNodeIds: [],
5878
+ pathNodes: [],
5879
+ pathEdges: [],
5880
+ highlightNodeIds: [],
5881
+ highlightEdgeIds: [],
5882
+ distance: 0,
5883
+ message: "Select a source and target to trace a path."
5884
+ };
5885
+ }
5886
+ if (!selection.sourceId || !selection.targetId) {
5887
+ return {
5888
+ status: "incomplete",
5889
+ selection,
5890
+ source,
5891
+ target,
5892
+ missingNodeIds,
5893
+ pathNodes: [],
5894
+ pathEdges: [],
5895
+ highlightNodeIds: [],
5896
+ highlightEdgeIds: [],
5897
+ distance: 0,
5898
+ message: "Select both a source and a target to resolve a path."
5899
+ };
5900
+ }
5901
+ if (missingNodeIds.length > 0 || !source || !target) {
5902
+ return {
5903
+ status: "missing-node",
5904
+ selection,
5905
+ source,
5906
+ target,
5907
+ missingNodeIds,
5908
+ pathNodes: [],
5909
+ pathEdges: [],
5910
+ highlightNodeIds: [],
5911
+ highlightEdgeIds: [],
5912
+ distance: 0,
5913
+ message: buildMissingNodeMessage(selection, missingNodeIds)
5914
+ };
5915
+ }
5916
+ if (source.id === target.id) {
5917
+ return {
5918
+ status: "found",
5919
+ selection,
5920
+ source,
5921
+ target,
5922
+ missingNodeIds: [],
5923
+ pathNodes: [source],
5924
+ pathEdges: [],
5925
+ highlightNodeIds: [source.id],
5926
+ highlightEdgeIds: [],
5927
+ distance: 0,
5928
+ message: `Source and target already point to "${source.label}".`
5929
+ };
5930
+ }
5931
+ const visitedNodeIds = /* @__PURE__ */ new Set([source.id]);
5932
+ const predecessorByNodeId = /* @__PURE__ */ new Map();
5933
+ const queue = [source.id];
5934
+ while (queue.length > 0) {
5935
+ const currentNodeId = queue.shift();
5936
+ if (!currentNodeId) {
5937
+ continue;
5938
+ }
5939
+ const outgoingEdges = index.outgoingEdgesByNodeId.get(currentNodeId) ?? [];
5940
+ for (const edge of outgoingEdges) {
5941
+ if (visitedNodeIds.has(edge.targetId)) {
5942
+ continue;
5943
+ }
5944
+ visitedNodeIds.add(edge.targetId);
5945
+ predecessorByNodeId.set(edge.targetId, {
5946
+ nodeId: currentNodeId,
5947
+ edgeId: edge.id
5948
+ });
5949
+ if (edge.targetId === target.id) {
5950
+ queue.length = 0;
5951
+ break;
5952
+ }
5953
+ queue.push(edge.targetId);
5954
+ }
5955
+ }
5956
+ if (!predecessorByNodeId.has(target.id)) {
5957
+ return {
5958
+ status: "not-found",
5959
+ selection,
5960
+ source,
5961
+ target,
5962
+ missingNodeIds: [],
5963
+ pathNodes: [],
5964
+ pathEdges: [],
5965
+ highlightNodeIds: [],
5966
+ highlightEdgeIds: [],
5967
+ distance: 0,
5968
+ message: buildPathNotFoundMessage(source, target)
5969
+ };
5970
+ }
5971
+ const pathNodes = [target];
5972
+ const pathEdges = [];
5973
+ let cursorNodeId = target.id;
5974
+ while (cursorNodeId !== source.id) {
5975
+ const predecessor = predecessorByNodeId.get(cursorNodeId);
5976
+ if (!predecessor) {
5977
+ break;
5978
+ }
5979
+ const edge = index.edgesById.get(predecessor.edgeId);
5980
+ const previousNode = index.nodesById.get(predecessor.nodeId);
5981
+ if (edge) {
5982
+ pathEdges.push(edge);
5983
+ }
5984
+ if (previousNode) {
5985
+ pathNodes.push(previousNode);
5986
+ }
5987
+ cursorNodeId = predecessor.nodeId;
5988
+ }
5989
+ pathNodes.reverse();
5990
+ pathEdges.reverse();
5991
+ return {
5992
+ status: "found",
5993
+ selection,
5994
+ source,
5995
+ target,
5996
+ missingNodeIds: [],
5997
+ pathNodes,
5998
+ pathEdges,
5999
+ highlightNodeIds: pathNodes.map((node) => node.id),
6000
+ highlightEdgeIds: pathEdges.map((edge) => edge.id),
6001
+ distance: pathEdges.length,
6002
+ message: `Path found from "${source.label}" to "${target.label}" across ${pathEdges.length} edge${pathEdges.length === 1 ? "" : "s"}.`
6003
+ };
6004
+ }
6005
+
6006
+ // src/features/operations/organization-graph/expand-around/expandAroundGraph.ts
6007
+ var DEFAULT_MAX_DEPTH = 1;
6008
+ var DEFAULT_MAX_RESULTS = 25;
6009
+ var ORG_MODEL_ROOT_KINDS = /* @__PURE__ */ new Set([
6010
+ "feature",
6011
+ "surface",
6012
+ "entity",
6013
+ "capability"
6014
+ ]);
6015
+ var PRESET_EDGE_KINDS = {
6016
+ coverage: ["contains", "exposes", "operates-on", "maps_to", "references"],
6017
+ "operational-dependencies": ["references"],
6018
+ "org-context": ["contains", "exposes", "operates-on", "maps_to"],
6019
+ "impact-path": ["references"]
6020
+ };
6021
+ var PRESET_RELATIONSHIP_TYPES = {
6022
+ "operational-dependencies": ["triggers", "uses", "approval"],
6023
+ "impact-path": ["triggers", "uses", "approval"]
6024
+ };
6025
+ var PRESET_DIRECTIONS = {
6026
+ coverage: "both",
6027
+ "operational-dependencies": "both",
6028
+ "org-context": "both",
6029
+ "impact-path": "both"
6030
+ };
6031
+ function toSet(values) {
6032
+ if (!values || values.length === 0) {
6033
+ return null;
6034
+ }
6035
+ return new Set(values);
6036
+ }
6037
+ function clampMaxDepth(maxDepth) {
6038
+ if (maxDepth === 2 || maxDepth === 3) {
6039
+ return maxDepth;
6040
+ }
6041
+ return 1;
6042
+ }
6043
+ function resolvePreset(rootNode, preset) {
6044
+ if (preset) {
6045
+ return preset;
6046
+ }
6047
+ if (!rootNode) {
6048
+ return null;
6049
+ }
6050
+ if (ORG_MODEL_ROOT_KINDS.has(rootNode.kind)) {
6051
+ return "coverage";
6052
+ }
6053
+ if (rootNode.kind === "resource") {
6054
+ return "operational-dependencies";
6055
+ }
6056
+ return null;
6057
+ }
6058
+ function resolveRequest(rootNode, request) {
6059
+ const preset = resolvePreset(rootNode, request.preset);
6060
+ const maxResults = request.maxResults === void 0 || !Number.isFinite(request.maxResults) ? DEFAULT_MAX_RESULTS : Math.max(0, request.maxResults);
6061
+ return {
6062
+ rootNodeId: request.rootNodeId,
6063
+ direction: request.direction ?? (preset ? PRESET_DIRECTIONS[preset] : "both"),
6064
+ maxDepth: clampMaxDepth(request.maxDepth ?? DEFAULT_MAX_DEPTH),
6065
+ maxResults,
6066
+ edgeKinds: toSet(request.edgeKinds ?? (preset ? PRESET_EDGE_KINDS[preset] : void 0)),
6067
+ relationshipTypes: toSet(
6068
+ request.relationshipTypes ?? (preset ? PRESET_RELATIONSHIP_TYPES[preset] : void 0)
6069
+ ),
6070
+ preset,
6071
+ nodeKinds: toSet(request.nodeKinds),
6072
+ resourceTypes: toSet(request.resourceTypes),
6073
+ includeHiddenResources: request.includeHiddenResources ?? false
6074
+ };
6075
+ }
6076
+ function getOppositeNodeId(edge, currentNodeId) {
6077
+ if (edge.sourceId === currentNodeId) {
6078
+ return edge.targetId;
6079
+ }
6080
+ if (edge.targetId === currentNodeId) {
6081
+ return edge.sourceId;
6082
+ }
6083
+ return null;
6084
+ }
6085
+ function getCandidateEdges(index, nodeId2, direction) {
6086
+ if (direction === "outgoing") {
6087
+ return index.outgoingEdgesByNodeId.get(nodeId2) ?? [];
6088
+ }
6089
+ if (direction === "incoming") {
6090
+ return index.incomingEdgesByNodeId.get(nodeId2) ?? [];
6091
+ }
6092
+ const seenEdgeIds = /* @__PURE__ */ new Set();
6093
+ const edges = [];
6094
+ for (const edge of [
6095
+ ...index.outgoingEdgesByNodeId.get(nodeId2) ?? [],
6096
+ ...index.incomingEdgesByNodeId.get(nodeId2) ?? []
6097
+ ]) {
6098
+ if (seenEdgeIds.has(edge.id)) {
6099
+ continue;
6100
+ }
6101
+ seenEdgeIds.add(edge.id);
6102
+ edges.push(edge);
6103
+ }
6104
+ return edges;
6105
+ }
6106
+ function matchesPresetTraversalDirection(edge, currentNodeId, request) {
6107
+ if (request.preset === "coverage" && (edge.kind === "contains" || edge.kind === "exposes")) {
6108
+ return edge.sourceId === currentNodeId;
6109
+ }
6110
+ return true;
6111
+ }
6112
+ function matchesEdgeFilters(edge, currentNodeId, request) {
6113
+ if (request.edgeKinds && !request.edgeKinds.has(edge.kind)) {
6114
+ return false;
6115
+ }
6116
+ if (request.relationshipTypes && (!edge.relationshipType || !request.relationshipTypes.has(edge.relationshipType))) {
6117
+ return false;
6118
+ }
6119
+ return matchesPresetTraversalDirection(edge, currentNodeId, request);
6120
+ }
6121
+ function matchesNodeFilters(node, request) {
6122
+ if (request.nodeKinds && !request.nodeKinds.has(node.kind)) {
6123
+ return false;
6124
+ }
6125
+ if (request.resourceTypes) {
6126
+ if (node.kind !== "resource") {
6127
+ return false;
6128
+ }
6129
+ if (!node.resourceType || !request.resourceTypes.has(node.resourceType)) {
6130
+ return false;
6131
+ }
6132
+ }
6133
+ return true;
6134
+ }
6135
+ function pluralize(count, singular, plural = `${singular}s`) {
6136
+ return `${count} ${count === 1 ? singular : plural}`;
6137
+ }
6138
+ function getNodeDisplayName(node) {
6139
+ return node.label || node.sourceId || node.id;
6140
+ }
6141
+ function buildSummaryMessages(input) {
6142
+ const { rootNode, preset, expandedNodes, counts, truncated } = input;
6143
+ if (!rootNode) {
6144
+ return ["Select an available node to expand around."];
6145
+ }
6146
+ if (!preset && rootNode.kind === "organization") {
6147
+ return ["Choose a feature, surface, capability, entity, or resource before expanding."];
6148
+ }
6149
+ if (expandedNodes.length === 0) {
6150
+ return [`No matching graph context found around "${getNodeDisplayName(rootNode)}".`];
6151
+ }
6152
+ const resourceCount = expandedNodes.filter((node) => node.kind === "resource").length;
6153
+ const semanticContextCount = expandedNodes.length - resourceCount;
6154
+ const rootLabel = getNodeDisplayName(rootNode);
6155
+ const messages = [];
6156
+ if (preset === "coverage" && ORG_MODEL_ROOT_KINDS.has(rootNode.kind)) {
6157
+ if (resourceCount > 0 && semanticContextCount > 0) {
6158
+ messages.push(
6159
+ `${pluralize(resourceCount, "resource")} and ${pluralize(
6160
+ semanticContextCount,
6161
+ "semantic context node"
6162
+ )} support "${rootLabel}".`
6163
+ );
6164
+ } else if (resourceCount > 0) {
6165
+ messages.push(`${pluralize(resourceCount, "resource")} support "${rootLabel}".`);
6166
+ } else {
6167
+ messages.push(
6168
+ `${pluralize(semanticContextCount, "semantic context node")} found around "${rootLabel}"; no mapped resources matched this coverage preset.`
6169
+ );
6170
+ }
6171
+ } else if (preset === "operational-dependencies") {
6172
+ messages.push(`${pluralize(resourceCount, "operational dependency resource")} found around "${rootLabel}".`);
6173
+ } else if (preset === "org-context") {
6174
+ messages.push(`${pluralize(expandedNodes.length, "semantic context node")} found around "${rootLabel}".`);
6175
+ } else if (preset === "impact-path") {
6176
+ messages.push(`${pluralize(resourceCount, "resource")} can call or be affected by "${rootLabel}".`);
6177
+ } else {
6178
+ messages.push(`${pluralize(expandedNodes.length, "node")} found around "${rootLabel}".`);
6179
+ }
6180
+ if (counts.hiddenResourceNodes > 0) {
6181
+ messages.push(`${pluralize(counts.hiddenResourceNodes, "hidden resource")} matched current visibility settings.`);
6182
+ }
6183
+ if (counts.alreadyVisibleNodes > 0) {
6184
+ messages.push(`${pluralize(counts.alreadyVisibleNodes, "node")} already visible in the graph.`);
6185
+ }
6186
+ if (truncated) {
6187
+ messages.push(`Results were limited to ${pluralize(expandedNodes.length, "node")}.`);
6188
+ }
6189
+ return messages;
6190
+ }
6191
+ function expandAroundGraph(graph, request, options = {}) {
6192
+ const index = buildOrganizationGraphTraceIndex(graph);
6193
+ const rootNode = index.nodesById.get(request.rootNodeId) ?? null;
6194
+ const resolved = resolveRequest(rootNode, request);
6195
+ const alreadyVisibleNodeIds = new Set(options.alreadyVisibleNodeIds ?? []);
6196
+ const hiddenResourceNodeIds = new Set(options.hiddenResourceNodeIds ?? []);
6197
+ const expandedNodeIds = /* @__PURE__ */ new Set();
6198
+ const expandedEdgeIds = /* @__PURE__ */ new Set();
6199
+ const frontierNodeIds = /* @__PURE__ */ new Set();
6200
+ const visitedDepthByNodeId = /* @__PURE__ */ new Map([[resolved.rootNodeId, 0]]);
6201
+ const hasExplicitTraversalFilter = Boolean(
6202
+ request.preset || request.edgeKinds?.length || request.relationshipTypes?.length || request.nodeKinds?.length || request.resourceTypes?.length
6203
+ );
6204
+ const queue = rootNode && (resolved.preset || hasExplicitTraversalFilter) ? [{ nodeId: rootNode.id, depth: 0 }] : [];
6205
+ let truncated = false;
6206
+ for (let queueIndex = 0; queueIndex < queue.length; queueIndex += 1) {
6207
+ const current = queue[queueIndex];
6208
+ if (current.depth >= resolved.maxDepth) {
6209
+ if (current.nodeId !== resolved.rootNodeId) {
6210
+ frontierNodeIds.add(current.nodeId);
6211
+ }
6212
+ continue;
6213
+ }
6214
+ for (const edge of getCandidateEdges(index, current.nodeId, resolved.direction)) {
6215
+ if (!matchesEdgeFilters(edge, current.nodeId, resolved)) {
6216
+ continue;
6217
+ }
6218
+ const nextNodeId = getOppositeNodeId(edge, current.nodeId);
6219
+ if (!nextNodeId) {
6220
+ continue;
6221
+ }
6222
+ const nextNode = index.nodesById.get(nextNodeId);
6223
+ if (!nextNode || !matchesNodeFilters(nextNode, resolved)) {
6224
+ continue;
6225
+ }
6226
+ if (!resolved.includeHiddenResources && hiddenResourceNodeIds.has(nextNode.id)) {
6227
+ continue;
6228
+ }
6229
+ const nextDepth = current.depth + 1;
6230
+ const previousDepth = visitedDepthByNodeId.get(nextNode.id);
6231
+ if (previousDepth !== void 0) {
6232
+ if (nextNode.id === resolved.rootNodeId || expandedNodeIds.has(nextNode.id)) {
6233
+ expandedEdgeIds.add(edge.id);
6234
+ }
6235
+ continue;
6236
+ }
6237
+ if (expandedNodeIds.size >= resolved.maxResults) {
6238
+ truncated = true;
6239
+ frontierNodeIds.add(nextNode.id);
6240
+ continue;
6241
+ }
6242
+ visitedDepthByNodeId.set(nextNode.id, nextDepth);
6243
+ expandedNodeIds.add(nextNode.id);
6244
+ expandedEdgeIds.add(edge.id);
6245
+ if (nextDepth >= resolved.maxDepth) {
6246
+ frontierNodeIds.add(nextNode.id);
6247
+ continue;
6248
+ }
6249
+ queue.push({ nodeId: nextNode.id, depth: nextDepth });
6250
+ }
6251
+ }
6252
+ const expandedNodes = [...expandedNodeIds].map((nodeId2) => index.nodesById.get(nodeId2)).filter((node) => Boolean(node));
6253
+ const hiddenResourceNodes = expandedNodes.filter((node) => hiddenResourceNodeIds.has(node.id)).length;
6254
+ const alreadyVisibleNodes = expandedNodes.filter((node) => alreadyVisibleNodeIds.has(node.id)).length;
6255
+ const newNodes = expandedNodes.filter(
6256
+ (node) => !hiddenResourceNodeIds.has(node.id) && !alreadyVisibleNodeIds.has(node.id)
6257
+ ).length;
6258
+ const counts = {
6259
+ newNodes,
6260
+ newEdges: expandedEdgeIds.size,
6261
+ alreadyVisibleNodes,
6262
+ hiddenResourceNodes
6263
+ };
6264
+ const summaryMessages = buildSummaryMessages({
6265
+ rootNode,
6266
+ preset: resolved.preset,
6267
+ expandedNodes,
6268
+ counts,
6269
+ truncated
6270
+ });
6271
+ return {
6272
+ rootNodeId: request.rootNodeId,
6273
+ rootNode,
6274
+ preset: resolved.preset,
6275
+ direction: resolved.direction,
6276
+ maxDepth: resolved.maxDepth,
6277
+ maxResults: resolved.maxResults,
6278
+ expandedNodeIds: [...expandedNodeIds],
6279
+ expandedEdgeIds: [...expandedEdgeIds],
6280
+ frontierNodeIds: [...frontierNodeIds].filter((nodeId2) => nodeId2 !== resolved.rootNodeId),
6281
+ truncated,
6282
+ counts,
6283
+ summaryMessages,
6284
+ message: summaryMessages[0] ?? ""
6285
+ };
6286
+ }
6287
+ var PRESET_OPTIONS = [
6288
+ { value: "coverage", label: "Coverage" },
6289
+ { value: "operational-dependencies", label: "Operational dependencies" },
6290
+ { value: "org-context", label: "Org context" },
6291
+ { value: "impact-path", label: "Impact path" }
6292
+ ];
6293
+ var DIRECTION_OPTIONS = [
6294
+ { value: "both", label: "Both" },
6295
+ { value: "outgoing", label: "Outgoing" },
6296
+ { value: "incoming", label: "Incoming" }
6297
+ ];
6298
+ var EDGE_KIND_OPTIONS = [
6299
+ { value: "contains", label: "Containment" },
6300
+ { value: "references", label: "References" },
6301
+ { value: "exposes", label: "Exposes" },
6302
+ { value: "maps_to", label: "Maps to" },
6303
+ { value: "operates-on", label: "Operates on" },
6304
+ { value: "uses", label: "Uses" }
6305
+ ];
6306
+ var RELATIONSHIP_OPTIONS = [
6307
+ { value: "triggers", label: "Triggers" },
6308
+ { value: "uses", label: "Uses" },
6309
+ { value: "approval", label: "Approval" }
6310
+ ];
6311
+ var RESOURCE_TYPE_OPTIONS = [
6312
+ { value: "workflow", label: "Workflow" },
6313
+ { value: "agent", label: "Agent" },
6314
+ { value: "trigger", label: "Trigger" },
6315
+ { value: "integration", label: "Integration" },
6316
+ { value: "external", label: "External" },
6317
+ { value: "human_checkpoint", label: "Human checkpoint" }
6318
+ ];
6319
+ function updateValue(value, key, nextValue, onChange) {
6320
+ onChange({
6321
+ ...value,
6322
+ [key]: nextValue
6323
+ });
6324
+ }
6325
+ function toOptionalArray(values) {
6326
+ return values.length > 0 ? values : void 0;
6327
+ }
6328
+ function toMaxDepth(value) {
6329
+ const numeric = typeof value === "number" ? value : Number.parseInt(value, 10);
6330
+ if (numeric === 3) return 3;
6331
+ if (numeric === 2) return 2;
6332
+ return 1;
6333
+ }
6334
+ function ExpandAroundPanel({
6335
+ selectedNode,
6336
+ value,
6337
+ result,
6338
+ appliedNodeCount,
6339
+ appliedEdgeCount,
6340
+ onChange,
6341
+ onPreview,
6342
+ onApply,
6343
+ onClear
6344
+ }) {
6345
+ const disabled = !selectedNode;
6346
+ const canApply = Boolean(result && result.expandedNodeIds.length > 0);
6347
+ const rootIsTooBroad = selectedNode?.kind === "organization" && !value.preset;
6348
+ return /* @__PURE__ */ jsx(Paper, { withBorder: true, radius: "md", p: "sm", children: /* @__PURE__ */ jsxs(Stack, { gap: "sm", children: [
6349
+ /* @__PURE__ */ jsxs(Group, { justify: "space-between", align: "flex-start", wrap: "wrap", children: [
6350
+ /* @__PURE__ */ jsxs(Stack, { gap: 2, children: [
6351
+ /* @__PURE__ */ jsx(Text, { size: "sm", fw: 800, children: "Expand Around" }),
6352
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: selectedNode ? `Preview typed graph context around ${selectedNode.label}.` : "Select a graph node to preview nearby operational context." })
6353
+ ] }),
6354
+ appliedNodeCount > 0 || appliedEdgeCount > 0 ? /* @__PURE__ */ jsxs(Badge, { variant: "light", color: "blue", children: [
6355
+ appliedNodeCount,
6356
+ " nodes / ",
6357
+ appliedEdgeCount,
6358
+ " edges applied"
6359
+ ] }) : null
6360
+ ] }),
6361
+ /* @__PURE__ */ jsxs(SimpleGrid, { cols: { base: 1, sm: 2 }, spacing: "sm", children: [
6362
+ /* @__PURE__ */ jsx(
6363
+ Select,
6364
+ {
6365
+ label: "Preset",
6366
+ placeholder: "Auto",
6367
+ data: PRESET_OPTIONS,
6368
+ value: value.preset ?? null,
6369
+ onChange: (preset) => updateValue(value, "preset", preset ?? void 0, onChange),
6370
+ disabled,
6371
+ clearable: true
6372
+ }
6373
+ ),
6374
+ /* @__PURE__ */ jsx(
6375
+ Select,
6376
+ {
6377
+ label: "Direction",
6378
+ data: DIRECTION_OPTIONS,
6379
+ value: value.direction ?? "both",
6380
+ onChange: (direction) => updateValue(value, "direction", direction ?? "both", onChange),
6381
+ disabled
6382
+ }
6383
+ ),
6384
+ /* @__PURE__ */ jsx(
6385
+ NumberInput,
6386
+ {
6387
+ label: "Depth",
6388
+ min: 1,
6389
+ max: 3,
6390
+ value: value.maxDepth ?? 1,
6391
+ onChange: (maxDepth) => updateValue(value, "maxDepth", toMaxDepth(maxDepth), onChange),
6392
+ disabled
6393
+ }
6394
+ ),
6395
+ /* @__PURE__ */ jsx(
6396
+ NumberInput,
6397
+ {
6398
+ label: "Max results",
6399
+ min: 1,
6400
+ max: 100,
6401
+ value: value.maxResults ?? 25,
6402
+ onChange: (maxResults) => updateValue(
6403
+ value,
6404
+ "maxResults",
6405
+ Math.max(1, typeof maxResults === "number" ? maxResults : Number.parseInt(maxResults, 10) || 25),
6406
+ onChange
6407
+ ),
6408
+ disabled
6409
+ }
6410
+ )
6411
+ ] }),
6412
+ /* @__PURE__ */ jsx(
6413
+ MultiSelect,
6414
+ {
6415
+ label: "Relationship filters",
6416
+ placeholder: "Preset defaults",
6417
+ data: RELATIONSHIP_OPTIONS,
6418
+ value: value.relationshipTypes ?? [],
6419
+ onChange: (relationshipTypes) => updateValue(value, "relationshipTypes", toOptionalArray(relationshipTypes), onChange),
6420
+ disabled,
6421
+ clearable: true
6422
+ }
6423
+ ),
6424
+ /* @__PURE__ */ jsx(
6425
+ MultiSelect,
6426
+ {
6427
+ label: "Edge kind filters",
6428
+ placeholder: "Preset defaults",
6429
+ data: EDGE_KIND_OPTIONS,
6430
+ value: value.edgeKinds ?? [],
6431
+ onChange: (edgeKinds) => updateValue(value, "edgeKinds", toOptionalArray(edgeKinds), onChange),
6432
+ disabled,
6433
+ clearable: true
6434
+ }
6435
+ ),
6436
+ /* @__PURE__ */ jsx(
6437
+ MultiSelect,
6438
+ {
6439
+ label: "Resource type filters",
6440
+ placeholder: "All resource types",
6441
+ data: RESOURCE_TYPE_OPTIONS,
6442
+ value: value.resourceTypes ?? [],
6443
+ onChange: (resourceTypes) => updateValue(value, "resourceTypes", toOptionalArray(resourceTypes), onChange),
6444
+ disabled,
6445
+ clearable: true
6446
+ }
6447
+ ),
6448
+ /* @__PURE__ */ jsx(
6449
+ Switch,
6450
+ {
6451
+ label: "Include hidden resources",
6452
+ checked: value.includeHiddenResources ?? true,
6453
+ onChange: (event) => updateValue(value, "includeHiddenResources", event.currentTarget.checked, onChange),
6454
+ disabled
6455
+ }
6456
+ ),
6457
+ rootIsTooBroad ? /* @__PURE__ */ jsx(Text, { size: "xs", c: "orange", children: "The organization root is broad. Choose a narrower node or a semantic preset before previewing." }) : null,
6458
+ result ? /* @__PURE__ */ jsx(Card, { withBorder: true, radius: "md", p: "sm", children: /* @__PURE__ */ jsxs(Stack, { gap: 6, children: [
6459
+ /* @__PURE__ */ jsxs(Group, { gap: "xs", wrap: "wrap", children: [
6460
+ /* @__PURE__ */ jsxs(Badge, { variant: "light", children: [
6461
+ result.counts.newNodes,
6462
+ " new"
6463
+ ] }),
6464
+ /* @__PURE__ */ jsxs(Badge, { variant: "light", children: [
6465
+ result.counts.alreadyVisibleNodes,
6466
+ " visible"
6467
+ ] }),
6468
+ /* @__PURE__ */ jsxs(Badge, { variant: "light", color: result.counts.hiddenResourceNodes > 0 ? "orange" : "gray", children: [
6469
+ result.counts.hiddenResourceNodes,
6470
+ " hidden"
6471
+ ] }),
6472
+ result.truncated ? /* @__PURE__ */ jsx(Badge, { variant: "light", color: "yellow", children: "Truncated" }) : null
6473
+ ] }),
6474
+ result.summaryMessages.map((message) => /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: message }, message))
6475
+ ] }) }) : null,
6476
+ /* @__PURE__ */ jsxs(Group, { gap: "xs", justify: "space-between", wrap: "wrap", children: [
6477
+ /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
6478
+ /* @__PURE__ */ jsx(Button, { size: "xs", variant: "light", onClick: onPreview, disabled: disabled || rootIsTooBroad, children: "Preview" }),
6479
+ /* @__PURE__ */ jsx(Button, { size: "xs", onClick: onApply, disabled: !canApply, children: "Apply" })
6480
+ ] }),
6481
+ /* @__PURE__ */ jsx(Button, { size: "xs", variant: "subtle", onClick: onClear, disabled: !result && appliedNodeCount === 0, children: "Clear expansion" })
6482
+ ] })
6483
+ ] }) });
6484
+ }
6485
+ var cellStyle = {
6486
+ padding: "var(--mantine-spacing-sm) var(--mantine-spacing-md)",
6487
+ display: "flex",
6488
+ alignItems: "center",
6489
+ gap: "var(--mantine-spacing-sm)",
6490
+ minWidth: 0
6491
+ };
6492
+ function CommandViewHealthStrip({
6493
+ overview,
6494
+ hotspots,
6495
+ visibleResources,
6496
+ hiddenResources = 0,
6497
+ resourcesHidden,
6498
+ selectedLabel,
6499
+ onFocusHotspot,
6500
+ onResourcesHiddenChange,
6501
+ onResetFocus
6502
+ }) {
6503
+ return /* @__PURE__ */ jsxs("div", { style: { display: "flex", justifyContent: "space-between" }, children: [
6504
+ /* @__PURE__ */ jsxs("div", { style: cellStyle, children: [
6505
+ /* @__PURE__ */ jsx(IconActivityHeartbeat, { size: 16, style: { flexShrink: 0, color: "var(--color-text-dimmed)" } }),
6506
+ /* @__PURE__ */ jsx(Text, { size: "sm", fw: 700, style: { flexShrink: 0 }, children: overview?.successRate == null ? "N/A" : `${Math.round(overview.successRate)}%` }),
6507
+ /* @__PURE__ */ jsxs(Group, { gap: 4, wrap: "nowrap", children: [
6508
+ /* @__PURE__ */ jsx(Badge, { variant: "light", color: "green", size: "xs", children: overview?.successCount ?? 0 }),
6509
+ /* @__PURE__ */ jsx(Badge, { variant: "light", color: "red", size: "xs", children: overview?.failureCount ?? 0 }),
6510
+ /* @__PURE__ */ jsx(Badge, { variant: "light", color: "yellow", size: "xs", children: overview?.warningCount ?? 0 })
6511
+ ] }),
6512
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", truncate: true, style: { flexShrink: 1, minWidth: 0 }, children: overview ? `${overview.totalRuns} runs / ${overview.trackedResources} resources` : "" })
6513
+ ] }),
6514
+ /* @__PURE__ */ jsxs("div", { style: cellStyle, children: [
6515
+ /* @__PURE__ */ jsx(IconAlertTriangle, { size: 16, style: { flexShrink: 0, color: "var(--color-text-dimmed)" } }),
6516
+ hotspots.length > 0 ? /* @__PURE__ */ jsx(Group, { gap: "xs", wrap: "nowrap", style: { minWidth: 0, overflow: "hidden" }, children: hotspots.map((hotspot) => /* @__PURE__ */ jsx(Group, { gap: 4, wrap: "nowrap", children: /* @__PURE__ */ jsx(
6517
+ Badge,
6518
+ {
6519
+ variant: "light",
6520
+ color: "red",
6521
+ size: "xs",
6522
+ style: { cursor: "pointer", flexShrink: 0 },
6523
+ onClick: () => onFocusHotspot(hotspot.resourceId),
6524
+ children: hotspot.label
6525
+ }
6526
+ ) }, hotspot.nodeId)) }) : /* @__PURE__ */ jsx(Badge, { variant: "light", color: "green", size: "xs", leftSection: /* @__PURE__ */ jsx(IconCircleCheck, { size: 10 }), children: "No hotspots" })
6527
+ ] }),
6528
+ /* @__PURE__ */ jsxs("div", { style: cellStyle, children: [
6529
+ /* @__PURE__ */ jsx(IconClockPause, { size: 16, style: { flexShrink: 0, color: "var(--color-text-dimmed)" } }),
6530
+ /* @__PURE__ */ jsx(Text, { size: "sm", fw: 700, style: { flexShrink: 0 }, children: overview?.pendingApprovals ?? 0 }),
6531
+ /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", truncate: true, children: [
6532
+ "pending / ",
6533
+ overview?.activeHumanCheckpoints ?? 0,
6534
+ " queues"
6535
+ ] })
6536
+ ] }),
6537
+ /* @__PURE__ */ jsxs("div", { style: cellStyle, children: [
6538
+ /* @__PURE__ */ jsx(IconTopologyStar3, { size: 16, style: { flexShrink: 0, color: "var(--color-text-dimmed)" } }),
6539
+ /* @__PURE__ */ jsx(Text, { size: "xs", fw: 600, truncate: true, style: { minWidth: 0 }, children: selectedLabel ?? `${visibleResources} visible / ${hiddenResources} hidden` }),
6540
+ /* @__PURE__ */ jsx(
6541
+ Button,
6542
+ {
6543
+ size: "compact-xs",
6544
+ variant: resourcesHidden ? "light" : "subtle",
6545
+ onClick: () => onResourcesHiddenChange(!resourcesHidden),
6546
+ style: { flexShrink: 0 },
6547
+ children: resourcesHidden ? "Show resources" : "Hide resources"
6548
+ }
6549
+ ),
6550
+ /* @__PURE__ */ jsx(Button, { size: "compact-xs", variant: "subtle", onClick: onResetFocus, style: { flexShrink: 0 }, children: "Reset" })
6551
+ ] })
6552
+ ] });
6553
+ }
6554
+ var FILTER_STATE_ICONS2 = {
6555
+ neutral: IconCircleDashed,
6556
+ include: IconCircleCheck,
6557
+ exclude: IconCircleX
6558
+ };
6559
+ var FILTER_STATE_COLORS2 = {
6560
+ neutral: "var(--color-text-dimmed)",
6561
+ include: "var(--mantine-color-green-6)",
6562
+ exclude: "var(--mantine-color-red-6)"
6563
+ };
6564
+ var FILTER_STATE_LABELS2 = {
6565
+ neutral: "Not filtered",
6566
+ include: "Include only",
6567
+ exclude: "Exclude"
6568
+ };
6569
+ function getCommandViewResources(commandViewData) {
6570
+ if (!commandViewData) {
6571
+ return [];
6572
+ }
6573
+ return [
6574
+ ...commandViewData.agents,
6575
+ ...commandViewData.workflows,
6576
+ ...commandViewData.triggers,
6577
+ ...commandViewData.integrations,
6578
+ ...commandViewData.externalResources,
6579
+ ...commandViewData.humanCheckpoints
6580
+ ];
6581
+ }
6582
+ function nextDomainFilterState(current) {
6583
+ if (current === "neutral") return "include";
6584
+ if (current === "include") return "exclude";
6585
+ return "neutral";
6586
+ }
6587
+ function FilterPanel({
6588
+ filters,
6589
+ onChangeFilters,
6590
+ resetValue,
6591
+ disabled = false,
6592
+ commandViewData,
6593
+ lens,
6594
+ resourcesHidden,
6595
+ diagnosticsHidden,
6596
+ onResourcesHiddenChange,
6597
+ onDiagnosticsHiddenChange,
6598
+ onRevealResources,
6599
+ onResetFilters,
6600
+ visibilityCounts,
6601
+ graph,
6602
+ baseGraph,
6603
+ layout = "grid"
6604
+ }) {
6605
+ const resourceFacets = collectResourceFilterFacets(getCommandViewResources(commandViewData));
6606
+ const Section = ({ children }) => layout === "stack" ? /* @__PURE__ */ jsx(
6607
+ Stack,
6608
+ {
6609
+ gap: "sm",
6610
+ pb: "sm",
6611
+ style: {
6612
+ borderBottom: "1px solid var(--color-border)"
6613
+ },
6614
+ children
6615
+ }
6616
+ ) : /* @__PURE__ */ jsx(Paper, { withBorder: true, p: "md", radius: "md", style: { background: "var(--color-surface)" }, children });
6617
+ const updateDomainFilter = (domainId) => {
6618
+ const current = filters.domainFilters[domainId] ?? "neutral";
6619
+ onChangeFilters({
6620
+ ...filters,
6621
+ domainFilters: {
6622
+ ...filters.domainFilters,
6623
+ [domainId]: nextDomainFilterState(current)
6624
+ }
6625
+ });
6626
+ };
6627
+ const handleResourceVisibilityAction = () => {
6628
+ if (resourcesHidden) {
6629
+ onRevealResources();
6630
+ return;
6631
+ }
6632
+ onResourcesHiddenChange(true);
6633
+ };
6634
+ const content = /* @__PURE__ */ jsxs(Fragment, { children: [
6635
+ /* @__PURE__ */ jsx(Section, { children: /* @__PURE__ */ jsxs(Stack, { gap: "sm", children: [
6636
+ /* @__PURE__ */ jsx(Text, { size: "sm", fw: 700, children: "Filters" }),
6637
+ /* @__PURE__ */ jsx(
6638
+ OrganizationGraphFilterToolbar,
6639
+ {
6640
+ value: filters,
6641
+ onChange: onChangeFilters,
6642
+ disabled,
6643
+ resetValue
6644
+ }
6645
+ )
6646
+ ] }) }),
6647
+ /* @__PURE__ */ jsx(Section, { children: /* @__PURE__ */ jsxs(Stack, { gap: "sm", children: [
6648
+ /* @__PURE__ */ jsxs(Group, { justify: "space-between", align: "center", children: [
6649
+ /* @__PURE__ */ jsx(Text, { size: "sm", fw: 700, children: "Visibility" }),
6650
+ /* @__PURE__ */ jsxs(Badge, { variant: "light", children: [
6651
+ visibilityCounts.visibleResourceCount,
6652
+ "/",
6653
+ visibilityCounts.totalResourceCount,
6654
+ " resources"
6655
+ ] })
6656
+ ] }),
6657
+ lens === "command-view" ? /* @__PURE__ */ jsxs(Fragment, { children: [
6658
+ /* @__PURE__ */ jsx(
6659
+ Checkbox,
6660
+ {
6661
+ label: "Hide resource nodes",
6662
+ description: "Keep the organization structure readable and reveal resource nodes as needed.",
6014
6663
  checked: resourcesHidden,
6015
6664
  onChange: (event) => onResourcesHiddenChange(event.currentTarget.checked),
6016
6665
  disabled
@@ -6125,345 +6774,114 @@ function toResourceHealth(node, stats) {
6125
6774
  const summaryLabel2 = checkpointStats.pendingCount > 0 ? `${checkpointStats.pendingCount} pending` : checkpointStats.expiredCount > 0 ? `${checkpointStats.expiredCount} expired` : checkpointStats.completedCount > 0 ? `${checkpointStats.completedCount} completed` : "No queue activity";
6126
6775
  const detailLabel = checkpointStats.pendingCount > 0 ? `${checkpointStats.completedCount} resolved` : totalQueueActivity > 0 ? "Queue clear" : "No approvals in range";
6127
6776
  return {
6128
- nodeId: node.id,
6129
- resourceId,
6130
- label: node.label,
6131
- resourceType: node.resourceType,
6132
- healthLevel: healthLevel2,
6133
- summaryLabel: summaryLabel2,
6134
- detailLabel,
6135
- successRate: null,
6136
- totalRuns: 0,
6137
- successCount: 0,
6138
- failureCount: 0,
6139
- warningCount: 0,
6140
- pendingCount: checkpointStats.pendingCount,
6141
- completedCount: checkpointStats.completedCount,
6142
- expiredCount: checkpointStats.expiredCount,
6143
- lastActivityAt: checkpointStats.lastDecisionAt,
6144
- sortScore: checkpointStats.pendingCount * 100 + checkpointStats.expiredCount * 25 + checkpointStats.completedCount
6145
- };
6146
- }
6147
- const resourceStats = stats.resources[resourceId];
6148
- if (!resourceStats || resourceStats.totalRuns <= 0) {
6149
- return {
6150
- nodeId: node.id,
6151
- resourceId,
6152
- label: node.label,
6153
- resourceType: node.resourceType,
6154
- healthLevel: "inactive",
6155
- summaryLabel: "No runs",
6156
- detailLabel: "No executions in range",
6157
- successRate: null,
6158
- totalRuns: 0,
6159
- successCount: 0,
6160
- failureCount: 0,
6161
- warningCount: 0,
6162
- pendingCount: 0,
6163
- completedCount: 0,
6164
- expiredCount: 0,
6165
- lastActivityAt: resourceStats?.lastRunAt ?? null,
6166
- sortScore: 0
6167
- };
6168
- }
6169
- const successfulOutcomes = resourceStats.successCount + resourceStats.warningCount;
6170
- const successRate = successfulOutcomes / resourceStats.totalRuns * 100;
6171
- const healthLevel = successRate >= 95 ? "healthy" : successRate >= 80 ? "warning" : "critical";
6172
- const summaryLabel = resourceStats.failureCount > 0 ? `${resourceStats.failureCount} failed` : resourceStats.warningCount > 0 ? `${resourceStats.warningCount} warnings` : `${resourceStats.totalRuns} runs`;
6173
- return {
6174
- nodeId: node.id,
6175
- resourceId,
6176
- label: node.label,
6177
- resourceType: node.resourceType,
6178
- healthLevel,
6179
- summaryLabel,
6180
- detailLabel: `${Math.round(successRate)}% healthy`,
6181
- successRate,
6182
- totalRuns: resourceStats.totalRuns,
6183
- successCount: resourceStats.successCount,
6184
- failureCount: resourceStats.failureCount,
6185
- warningCount: resourceStats.warningCount,
6186
- pendingCount: 0,
6187
- completedCount: 0,
6188
- expiredCount: 0,
6189
- lastActivityAt: resourceStats.lastRunAt,
6190
- sortScore: resourceStats.failureCount * 100 + resourceStats.warningCount * 25 + resourceStats.totalRuns
6191
- };
6192
- }
6193
- function getCommandViewGraphNodeHealth(node, stats) {
6194
- if (node.kind !== "resource" || !stats) {
6195
- return null;
6196
- }
6197
- return toResourceHealth(node, stats);
6198
- }
6199
- function getCommandViewGraphHealthMap(graph, stats) {
6200
- const healthByNodeId = /* @__PURE__ */ new Map();
6201
- if (!graph || !stats) {
6202
- return healthByNodeId;
6203
- }
6204
- for (const node of graph.nodes) {
6205
- const health = getCommandViewGraphNodeHealth(node, stats);
6206
- if (health) {
6207
- healthByNodeId.set(node.id, health);
6208
- }
6209
- }
6210
- return healthByNodeId;
6211
- }
6212
- function getHealthRank(level) {
6213
- switch (level) {
6214
- case "critical":
6215
- return 0;
6216
- case "warning":
6217
- return 1;
6218
- case "healthy":
6219
- return 2;
6220
- case "inactive":
6221
- default:
6222
- return 3;
6223
- }
6224
- }
6225
- function rankCommandViewHotspots(entries, limit = 3) {
6226
- return [...entries].filter((entry) => entry.healthLevel === "critical" || entry.healthLevel === "warning").sort((left, right) => {
6227
- const healthRankDelta = getHealthRank(left.healthLevel) - getHealthRank(right.healthLevel);
6228
- if (healthRankDelta !== 0) {
6229
- return healthRankDelta;
6230
- }
6231
- if (right.sortScore !== left.sortScore) {
6232
- return right.sortScore - left.sortScore;
6233
- }
6234
- return left.label.localeCompare(right.label);
6235
- }).slice(0, limit);
6236
- }
6237
-
6238
- // src/features/operations/organization-graph/path-tracing/trace.ts
6239
- var NODE_KIND_ORDER = {
6240
- organization: 0,
6241
- feature: 1,
6242
- surface: 2,
6243
- capability: 3,
6244
- entity: 4,
6245
- resource: 5
6246
- };
6247
- var NODE_KIND_LABEL = {
6248
- organization: "Organization",
6249
- feature: "Feature",
6250
- surface: "Surface",
6251
- capability: "Capability",
6252
- entity: "Entity",
6253
- resource: "Resource"
6254
- };
6255
- function getNodeLabel(node) {
6256
- return node.label || node.sourceId || node.id;
6257
- }
6258
- function compareTraceNodes(a, b) {
6259
- const kindDelta = NODE_KIND_ORDER[a.kind] - NODE_KIND_ORDER[b.kind];
6260
- if (kindDelta !== 0) {
6261
- return kindDelta;
6262
- }
6263
- const labelDelta = getNodeLabel(a).localeCompare(getNodeLabel(b));
6264
- if (labelDelta !== 0) {
6265
- return labelDelta;
6266
- }
6267
- return a.id.localeCompare(b.id);
6268
- }
6269
- function buildMissingNodeMessage(selection, missingNodeIds) {
6270
- if (missingNodeIds.length === 0) {
6271
- return "The selected trace is not available.";
6272
- }
6273
- if (missingNodeIds.length === 1) {
6274
- const missingId = missingNodeIds[0];
6275
- return `Graph node "${missingId}" is not available.`;
6276
- }
6277
- return `Graph nodes "${selection.sourceId}" and "${selection.targetId}" are not available.`;
6278
- }
6279
- function buildPathNotFoundMessage(source, target) {
6280
- return `No directed path found from "${source.label}" to "${target.label}".`;
6281
- }
6282
- function buildOrganizationGraphTraceIndex(graph) {
6283
- const nodesById = /* @__PURE__ */ new Map();
6284
- const edgesById = /* @__PURE__ */ new Map();
6285
- const outgoingEdgesByNodeId = /* @__PURE__ */ new Map();
6286
- const incomingEdgesByNodeId = /* @__PURE__ */ new Map();
6287
- for (const node of graph.nodes) {
6288
- nodesById.set(node.id, node);
6289
- outgoingEdgesByNodeId.set(node.id, []);
6290
- incomingEdgesByNodeId.set(node.id, []);
6291
- }
6292
- for (const edge of graph.edges) {
6293
- edgesById.set(edge.id, edge);
6294
- const outgoingEdges = outgoingEdgesByNodeId.get(edge.sourceId);
6295
- if (outgoingEdges) {
6296
- outgoingEdges.push(edge);
6297
- }
6298
- const incomingEdges = incomingEdgesByNodeId.get(edge.targetId);
6299
- if (incomingEdges) {
6300
- incomingEdges.push(edge);
6301
- }
6302
- }
6303
- return {
6304
- nodesById,
6305
- edgesById,
6306
- outgoingEdgesByNodeId,
6307
- incomingEdgesByNodeId
6308
- };
6309
- }
6310
- function getOrganizationGraphTraceNodeKindLabel(kind) {
6311
- return NODE_KIND_LABEL[kind];
6312
- }
6313
- function formatOrganizationGraphTraceNodeOptionLabel(option) {
6314
- return `${option.label} \xB7 ${getOrganizationGraphTraceNodeKindLabel(option.kind)}`;
6315
- }
6316
- function getOrganizationGraphTraceNodeOptions(graph) {
6317
- return [...graph.nodes].sort(compareTraceNodes).map((node) => ({
6318
- id: node.id,
6319
- label: getNodeLabel(node),
6320
- kind: node.kind,
6321
- sourceId: node.sourceId,
6322
- description: node.description,
6323
- enabled: node.enabled
6324
- }));
6325
- }
6326
- function resolveOrganizationGraphPathTrace(graph, selection) {
6327
- const index = buildOrganizationGraphTraceIndex(graph);
6328
- const source = selection.sourceId ? index.nodesById.get(selection.sourceId) ?? null : null;
6329
- const target = selection.targetId ? index.nodesById.get(selection.targetId) ?? null : null;
6330
- const missingNodeIds = [
6331
- selection.sourceId && !source ? selection.sourceId : null,
6332
- selection.targetId && !target ? selection.targetId : null
6333
- ].filter((value) => Boolean(value));
6334
- if (!selection.sourceId && !selection.targetId) {
6335
- return {
6336
- status: "idle",
6337
- selection,
6338
- source,
6339
- target,
6340
- missingNodeIds: [],
6341
- pathNodes: [],
6342
- pathEdges: [],
6343
- highlightNodeIds: [],
6344
- highlightEdgeIds: [],
6345
- distance: 0,
6346
- message: "Select a source and target to trace a path."
6347
- };
6348
- }
6349
- if (!selection.sourceId || !selection.targetId) {
6350
- return {
6351
- status: "incomplete",
6352
- selection,
6353
- source,
6354
- target,
6355
- missingNodeIds,
6356
- pathNodes: [],
6357
- pathEdges: [],
6358
- highlightNodeIds: [],
6359
- highlightEdgeIds: [],
6360
- distance: 0,
6361
- message: "Select both a source and a target to resolve a path."
6777
+ nodeId: node.id,
6778
+ resourceId,
6779
+ label: node.label,
6780
+ resourceType: node.resourceType,
6781
+ healthLevel: healthLevel2,
6782
+ summaryLabel: summaryLabel2,
6783
+ detailLabel,
6784
+ successRate: null,
6785
+ totalRuns: 0,
6786
+ successCount: 0,
6787
+ failureCount: 0,
6788
+ warningCount: 0,
6789
+ pendingCount: checkpointStats.pendingCount,
6790
+ completedCount: checkpointStats.completedCount,
6791
+ expiredCount: checkpointStats.expiredCount,
6792
+ lastActivityAt: checkpointStats.lastDecisionAt,
6793
+ sortScore: checkpointStats.pendingCount * 100 + checkpointStats.expiredCount * 25 + checkpointStats.completedCount
6362
6794
  };
6363
6795
  }
6364
- if (missingNodeIds.length > 0 || !source || !target) {
6796
+ const resourceStats = stats.resources[resourceId];
6797
+ if (!resourceStats || resourceStats.totalRuns <= 0) {
6365
6798
  return {
6366
- status: "missing-node",
6367
- selection,
6368
- source,
6369
- target,
6370
- missingNodeIds,
6371
- pathNodes: [],
6372
- pathEdges: [],
6373
- highlightNodeIds: [],
6374
- highlightEdgeIds: [],
6375
- distance: 0,
6376
- message: buildMissingNodeMessage(selection, missingNodeIds)
6799
+ nodeId: node.id,
6800
+ resourceId,
6801
+ label: node.label,
6802
+ resourceType: node.resourceType,
6803
+ healthLevel: "inactive",
6804
+ summaryLabel: "No runs",
6805
+ detailLabel: "No executions in range",
6806
+ successRate: null,
6807
+ totalRuns: 0,
6808
+ successCount: 0,
6809
+ failureCount: 0,
6810
+ warningCount: 0,
6811
+ pendingCount: 0,
6812
+ completedCount: 0,
6813
+ expiredCount: 0,
6814
+ lastActivityAt: resourceStats?.lastRunAt ?? null,
6815
+ sortScore: 0
6377
6816
  };
6378
6817
  }
6379
- if (source.id === target.id) {
6380
- return {
6381
- status: "found",
6382
- selection,
6383
- source,
6384
- target,
6385
- missingNodeIds: [],
6386
- pathNodes: [source],
6387
- pathEdges: [],
6388
- highlightNodeIds: [source.id],
6389
- highlightEdgeIds: [],
6390
- distance: 0,
6391
- message: `Source and target already point to "${source.label}".`
6392
- };
6818
+ const successfulOutcomes = resourceStats.successCount + resourceStats.warningCount;
6819
+ const successRate = successfulOutcomes / resourceStats.totalRuns * 100;
6820
+ const healthLevel = successRate >= 95 ? "healthy" : successRate >= 80 ? "warning" : "critical";
6821
+ const summaryLabel = resourceStats.failureCount > 0 ? `${resourceStats.failureCount} failed` : resourceStats.warningCount > 0 ? `${resourceStats.warningCount} warnings` : `${resourceStats.totalRuns} runs`;
6822
+ return {
6823
+ nodeId: node.id,
6824
+ resourceId,
6825
+ label: node.label,
6826
+ resourceType: node.resourceType,
6827
+ healthLevel,
6828
+ summaryLabel,
6829
+ detailLabel: `${Math.round(successRate)}% healthy`,
6830
+ successRate,
6831
+ totalRuns: resourceStats.totalRuns,
6832
+ successCount: resourceStats.successCount,
6833
+ failureCount: resourceStats.failureCount,
6834
+ warningCount: resourceStats.warningCount,
6835
+ pendingCount: 0,
6836
+ completedCount: 0,
6837
+ expiredCount: 0,
6838
+ lastActivityAt: resourceStats.lastRunAt,
6839
+ sortScore: resourceStats.failureCount * 100 + resourceStats.warningCount * 25 + resourceStats.totalRuns
6840
+ };
6841
+ }
6842
+ function getCommandViewGraphNodeHealth(node, stats) {
6843
+ if (node.kind !== "resource" || !stats) {
6844
+ return null;
6393
6845
  }
6394
- const visitedNodeIds = /* @__PURE__ */ new Set([source.id]);
6395
- const predecessorByNodeId = /* @__PURE__ */ new Map();
6396
- const queue = [source.id];
6397
- while (queue.length > 0) {
6398
- const currentNodeId = queue.shift();
6399
- if (!currentNodeId) {
6400
- continue;
6401
- }
6402
- const outgoingEdges = index.outgoingEdgesByNodeId.get(currentNodeId) ?? [];
6403
- for (const edge of outgoingEdges) {
6404
- if (visitedNodeIds.has(edge.targetId)) {
6405
- continue;
6406
- }
6407
- visitedNodeIds.add(edge.targetId);
6408
- predecessorByNodeId.set(edge.targetId, {
6409
- nodeId: currentNodeId,
6410
- edgeId: edge.id
6411
- });
6412
- if (edge.targetId === target.id) {
6413
- queue.length = 0;
6414
- break;
6415
- }
6416
- queue.push(edge.targetId);
6846
+ return toResourceHealth(node, stats);
6847
+ }
6848
+ function getCommandViewGraphHealthMap(graph, stats) {
6849
+ const healthByNodeId = /* @__PURE__ */ new Map();
6850
+ if (!graph || !stats) {
6851
+ return healthByNodeId;
6852
+ }
6853
+ for (const node of graph.nodes) {
6854
+ const health = getCommandViewGraphNodeHealth(node, stats);
6855
+ if (health) {
6856
+ healthByNodeId.set(node.id, health);
6417
6857
  }
6418
6858
  }
6419
- if (!predecessorByNodeId.has(target.id)) {
6420
- return {
6421
- status: "not-found",
6422
- selection,
6423
- source,
6424
- target,
6425
- missingNodeIds: [],
6426
- pathNodes: [],
6427
- pathEdges: [],
6428
- highlightNodeIds: [],
6429
- highlightEdgeIds: [],
6430
- distance: 0,
6431
- message: buildPathNotFoundMessage(source, target)
6432
- };
6859
+ return healthByNodeId;
6860
+ }
6861
+ function getHealthRank(level) {
6862
+ switch (level) {
6863
+ case "critical":
6864
+ return 0;
6865
+ case "warning":
6866
+ return 1;
6867
+ case "healthy":
6868
+ return 2;
6869
+ case "inactive":
6870
+ default:
6871
+ return 3;
6433
6872
  }
6434
- const pathNodes = [target];
6435
- const pathEdges = [];
6436
- let cursorNodeId = target.id;
6437
- while (cursorNodeId !== source.id) {
6438
- const predecessor = predecessorByNodeId.get(cursorNodeId);
6439
- if (!predecessor) {
6440
- break;
6441
- }
6442
- const edge = index.edgesById.get(predecessor.edgeId);
6443
- const previousNode = index.nodesById.get(predecessor.nodeId);
6444
- if (edge) {
6445
- pathEdges.push(edge);
6873
+ }
6874
+ function rankCommandViewHotspots(entries, limit = 3) {
6875
+ return [...entries].filter((entry) => entry.healthLevel === "critical" || entry.healthLevel === "warning").sort((left, right) => {
6876
+ const healthRankDelta = getHealthRank(left.healthLevel) - getHealthRank(right.healthLevel);
6877
+ if (healthRankDelta !== 0) {
6878
+ return healthRankDelta;
6446
6879
  }
6447
- if (previousNode) {
6448
- pathNodes.push(previousNode);
6880
+ if (right.sortScore !== left.sortScore) {
6881
+ return right.sortScore - left.sortScore;
6449
6882
  }
6450
- cursorNodeId = predecessor.nodeId;
6451
- }
6452
- pathNodes.reverse();
6453
- pathEdges.reverse();
6454
- return {
6455
- status: "found",
6456
- selection,
6457
- source,
6458
- target,
6459
- missingNodeIds: [],
6460
- pathNodes,
6461
- pathEdges,
6462
- highlightNodeIds: pathNodes.map((node) => node.id),
6463
- highlightEdgeIds: pathEdges.map((edge) => edge.id),
6464
- distance: pathEdges.length,
6465
- message: `Path found from "${source.label}" to "${target.label}" across ${pathEdges.length} edge${pathEdges.length === 1 ? "" : "s"}.`
6466
- };
6883
+ return left.label.localeCompare(right.label);
6884
+ }).slice(0, limit);
6467
6885
  }
6468
6886
  var STATUS_LABELS = {
6469
6887
  idle: "Idle",
@@ -6587,6 +7005,14 @@ var EMPTY_TRACE_SELECTION = {
6587
7005
  };
6588
7006
  var EMPTY_GRAPH_HIDDEN_IDS = /* @__PURE__ */ new Set();
6589
7007
  var EMPTY_GRAPH_HIDDEN_EDGE_IDS = /* @__PURE__ */ new Set();
7008
+ var EMPTY_EXPANDED_NODE_IDS = /* @__PURE__ */ new Set();
7009
+ var EMPTY_EXPANDED_EDGE_IDS = /* @__PURE__ */ new Set();
7010
+ var DEFAULT_EXPAND_AROUND_VALUE = {
7011
+ direction: "both",
7012
+ maxDepth: 1,
7013
+ maxResults: 25,
7014
+ includeHiddenResources: true
7015
+ };
6590
7016
  var FALLBACK_GRAPH_THEME = {
6591
7017
  primary: "#4a6e8e",
6592
7018
  background: "#030507",
@@ -6994,13 +7420,31 @@ function createCytoscapeStyle(tokens) {
6994
7420
  "target-arrow-color": traceBorder,
6995
7421
  "line-style": "solid"
6996
7422
  }
7423
+ },
7424
+ {
7425
+ selector: "node.is-expanded-node",
7426
+ style: {
7427
+ opacity: 1,
7428
+ "border-color": mixColors(tokens.primary, tokens.warning, 0.72),
7429
+ "border-width": 3.4,
7430
+ "background-opacity": 1
7431
+ }
7432
+ },
7433
+ {
7434
+ selector: "edge.is-expanded-edge",
7435
+ style: {
7436
+ opacity: 1,
7437
+ width: 3.4,
7438
+ "line-opacity": 0.94,
7439
+ label: "data(label)"
7440
+ }
6997
7441
  }
6998
7442
  ];
6999
7443
  }
7000
- function syncGraphClasses(cy, selectedElement, traceResult, hiddenIds, hiddenEdgeIds) {
7444
+ function syncGraphClasses(cy, selectedElement, traceResult, hiddenIds, hiddenEdgeIds, expandedNodeIds, expandedEdgeIds) {
7001
7445
  cy.batch(() => {
7002
7446
  cy.elements().removeClass(
7003
- "is-hidden is-faded is-context is-selected is-connected is-trace-node is-trace-edge is-trace-endpoint"
7447
+ "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"
7004
7448
  );
7005
7449
  for (const nodeId2 of hiddenIds) {
7006
7450
  const node = cy.getElementById(nodeId2);
@@ -7018,9 +7462,33 @@ function syncGraphClasses(cy, selectedElement, traceResult, hiddenIds, hiddenEdg
7018
7462
  traceResult && (traceResult.highlightNodeIds.length > 0 || traceResult.highlightEdgeIds.length > 0)
7019
7463
  );
7020
7464
  if (!selectedElement && !hasTrace) {
7465
+ for (const nodeId2 of expandedNodeIds) {
7466
+ const node = cy.getElementById(nodeId2);
7467
+ if (!node.empty()) {
7468
+ node.addClass("is-expanded-node");
7469
+ }
7470
+ }
7471
+ for (const edgeId2 of expandedEdgeIds) {
7472
+ const edge2 = cy.getElementById(edgeId2);
7473
+ if (!edge2.empty()) {
7474
+ edge2.addClass("is-expanded-edge");
7475
+ }
7476
+ }
7021
7477
  return;
7022
7478
  }
7023
7479
  cy.elements().addClass("is-faded");
7480
+ for (const nodeId2 of expandedNodeIds) {
7481
+ const node = cy.getElementById(nodeId2);
7482
+ if (!node.empty()) {
7483
+ node.removeClass("is-faded").addClass("is-context is-expanded-node");
7484
+ }
7485
+ }
7486
+ for (const edgeId2 of expandedEdgeIds) {
7487
+ const edge2 = cy.getElementById(edgeId2);
7488
+ if (!edge2.empty()) {
7489
+ edge2.removeClass("is-faded").addClass("is-connected is-expanded-edge");
7490
+ }
7491
+ }
7024
7492
  if (traceResult) {
7025
7493
  for (const nodeId2 of traceResult.highlightNodeIds) {
7026
7494
  const node = cy.getElementById(nodeId2);
@@ -7097,6 +7565,8 @@ function OrganizationGraphCanvas({
7097
7565
  traceResult,
7098
7566
  hiddenIds,
7099
7567
  hiddenEdgeIds,
7568
+ expandedNodeIds,
7569
+ expandedEdgeIds,
7100
7570
  themeTokens,
7101
7571
  commandViewHealthByNodeId,
7102
7572
  focusRequest,
@@ -7158,7 +7628,7 @@ function OrganizationGraphCanvas({
7158
7628
  });
7159
7629
  });
7160
7630
  resizeObserver.observe(containerRef.current);
7161
- syncGraphClasses(cy, selectedElement, traceResult, hiddenIds, hiddenEdgeIds);
7631
+ syncGraphClasses(cy, selectedElement, traceResult, hiddenIds, hiddenEdgeIds, expandedNodeIds, expandedEdgeIds);
7162
7632
  return () => {
7163
7633
  resizeObserver.disconnect();
7164
7634
  cy.destroy();
@@ -7172,7 +7642,7 @@ function OrganizationGraphCanvas({
7172
7642
  }
7173
7643
  syncCytoscapeElements(cy, elements);
7174
7644
  cy.layout(getLayoutOptions(mode, selectedElement, traceResult)).run();
7175
- syncGraphClasses(cy, selectedElement, traceResult, hiddenIds, hiddenEdgeIds);
7645
+ syncGraphClasses(cy, selectedElement, traceResult, hiddenIds, hiddenEdgeIds, expandedNodeIds, expandedEdgeIds);
7176
7646
  }, [elements]);
7177
7647
  useEffect(() => {
7178
7648
  const cy = cytoscapeRef.current;
@@ -7191,7 +7661,7 @@ function OrganizationGraphCanvas({
7191
7661
  if (!cy) {
7192
7662
  return;
7193
7663
  }
7194
- syncGraphClasses(cy, selectedElement, traceResult, hiddenIds, hiddenEdgeIds);
7664
+ syncGraphClasses(cy, selectedElement, traceResult, hiddenIds, hiddenEdgeIds, expandedNodeIds, expandedEdgeIds);
7195
7665
  if (mode === "map" && selectedElement?.type === "node") {
7196
7666
  const node = cy.getElementById(selectedElement.id);
7197
7667
  if (!node.empty()) {
@@ -7211,7 +7681,7 @@ function OrganizationGraphCanvas({
7211
7681
  );
7212
7682
  }
7213
7683
  }
7214
- }, [hiddenEdgeIds, hiddenIds, mode, selectedElement, traceResult]);
7684
+ }, [expandedEdgeIds, expandedNodeIds, hiddenEdgeIds, hiddenIds, mode, selectedElement, traceResult]);
7215
7685
  useEffect(() => {
7216
7686
  const cy = cytoscapeRef.current;
7217
7687
  if (!cy || !focusRequest) {
@@ -7354,6 +7824,10 @@ function OrganizationGraphPage({ lens = "default", timeRange = "30d" }) {
7354
7824
  const [selectedElement, setSelectedElement] = useState(null);
7355
7825
  const [focusRequest, setFocusRequest] = useState(null);
7356
7826
  const [pathTraceSelection, setPathTraceSelection] = useState(EMPTY_TRACE_SELECTION);
7827
+ const [expandAroundValue, setExpandAroundValue] = useState(DEFAULT_EXPAND_AROUND_VALUE);
7828
+ const [expandAroundPreview, setExpandAroundPreview] = useState(null);
7829
+ const [appliedExpandAroundNodeIds, setAppliedExpandAroundNodeIds] = useState(EMPTY_EXPANDED_NODE_IDS);
7830
+ const [appliedExpandAroundEdgeIds, setAppliedExpandAroundEdgeIds] = useState(EMPTY_EXPANDED_EDGE_IDS);
7357
7831
  const { filters, resetFilters, updateFilters } = useOrganizationGraphFilters(lensConfig.initialFilters);
7358
7832
  const toolbarResetValue = useMemo(
7359
7833
  () => createOrganizationGraphFilters(lensConfig.initialFilters),
@@ -7385,6 +7859,12 @@ function OrganizationGraphPage({ lens = "default", timeRange = "30d" }) {
7385
7859
  commandViewData
7386
7860
  });
7387
7861
  }, [baseGraph, commandViewData, deferredFilters]);
7862
+ const commandViewRevealIds = useMemo(() => {
7863
+ if (appliedExpandAroundNodeIds.size === 0) {
7864
+ return revealedIds;
7865
+ }
7866
+ return /* @__PURE__ */ new Set([...revealedIds, ...appliedExpandAroundNodeIds]);
7867
+ }, [appliedExpandAroundNodeIds, revealedIds]);
7388
7868
  const visibilityProjection = useMemo(() => {
7389
7869
  if (!graph || lens !== "command-view") {
7390
7870
  const resourceCount = getGraphCountByKind(graph, "resource");
@@ -7403,10 +7883,10 @@ function OrganizationGraphPage({ lens = "default", timeRange = "30d" }) {
7403
7883
  resourcesHidden,
7404
7884
  diagnosticsHidden,
7405
7885
  diagnosticCategories,
7406
- revealedIds,
7886
+ revealedIds: commandViewRevealIds,
7407
7887
  mode
7408
7888
  });
7409
- }, [commandViewData, diagnosticCategories, diagnosticsHidden, graph, lens, mode, resourcesHidden, revealedIds]);
7889
+ }, [commandViewData, commandViewRevealIds, diagnosticCategories, diagnosticsHidden, graph, lens, mode, resourcesHidden]);
7410
7890
  const visibleGraph = useMemo(() => {
7411
7891
  if (!graph || visibilityProjection.hiddenIds.size === 0) {
7412
7892
  return graph;
@@ -7489,6 +7969,43 @@ function OrganizationGraphPage({ lens = "default", timeRange = "30d" }) {
7489
7969
  nonce: Date.now()
7490
7970
  });
7491
7971
  });
7972
+ const clearExpandAroundState = useEffectEvent(() => {
7973
+ setExpandAroundPreview(null);
7974
+ setAppliedExpandAroundNodeIds(EMPTY_EXPANDED_NODE_IDS);
7975
+ setAppliedExpandAroundEdgeIds(EMPTY_EXPANDED_EDGE_IDS);
7976
+ });
7977
+ const previewExpandAround = useEffectEvent(() => {
7978
+ if (lens !== "command-view" || !graph || selectedElement?.type !== "node") {
7979
+ setExpandAroundPreview(null);
7980
+ return;
7981
+ }
7982
+ setExpandAroundPreview(
7983
+ expandAroundGraph(
7984
+ graph,
7985
+ {
7986
+ rootNodeId: selectedElement.id,
7987
+ ...expandAroundValue
7988
+ },
7989
+ {
7990
+ alreadyVisibleNodeIds: visibleGraph?.nodes.map((node) => node.id) ?? [],
7991
+ hiddenResourceNodeIds: visibilityProjection.hiddenIds
7992
+ }
7993
+ )
7994
+ );
7995
+ });
7996
+ const applyExpandAroundPreview = useEffectEvent(() => {
7997
+ if (!expandAroundPreview || selectedElement?.type !== "node") {
7998
+ return;
7999
+ }
8000
+ setAppliedExpandAroundNodeIds(new Set(expandAroundPreview.expandedNodeIds));
8001
+ setAppliedExpandAroundEdgeIds(new Set(expandAroundPreview.expandedEdgeIds));
8002
+ setMode("map");
8003
+ setSelectedElement({ type: "node", id: selectedElement.id });
8004
+ focusGraphNode(selectedElement.id);
8005
+ if (expandAroundPreview.counts.hiddenResourceNodes > 0) {
8006
+ markVisibilityInteraction();
8007
+ }
8008
+ });
7492
8009
  const focusCommandViewResource = useEffectEvent((resourceId) => {
7493
8010
  const focusNode = visibleGraph?.nodes.find((node) => node.kind === "resource" && node.sourceId === resourceId);
7494
8011
  if (!focusNode) {
@@ -7501,6 +8018,7 @@ function OrganizationGraphPage({ lens = "default", timeRange = "30d" }) {
7501
8018
  const handleResetFilters = useEffectEvent(() => {
7502
8019
  resetFilters();
7503
8020
  clearRevealedIds();
8021
+ clearExpandAroundState();
7504
8022
  setSelectedElement(null);
7505
8023
  setPathTraceSelection(EMPTY_TRACE_SELECTION);
7506
8024
  });
@@ -7508,6 +8026,7 @@ function OrganizationGraphPage({ lens = "default", timeRange = "30d" }) {
7508
8026
  setResourcesHidden(value);
7509
8027
  if (value) {
7510
8028
  clearRevealedIds();
8029
+ clearExpandAroundState();
7511
8030
  setSelectedElement(null);
7512
8031
  }
7513
8032
  });
@@ -7515,20 +8034,23 @@ function OrganizationGraphPage({ lens = "default", timeRange = "30d" }) {
7515
8034
  setDiagnosticsHidden(value);
7516
8035
  if (value) {
7517
8036
  clearRevealedIds();
8037
+ clearExpandAroundState();
7518
8038
  }
7519
8039
  });
7520
8040
  const revealAllResources = useEffectEvent(() => {
7521
8041
  setResourcesHidden(false);
7522
8042
  setDiagnosticsHidden(false);
7523
8043
  clearRevealedIds();
8044
+ clearExpandAroundState();
7524
8045
  });
7525
8046
  const handleSelectElement = useEffectEvent((element) => {
7526
8047
  if (!element) {
7527
- if (!selectedElement && revealedIds.size === 0) {
8048
+ if (!selectedElement && revealedIds.size === 0 && appliedExpandAroundNodeIds.size === 0) {
7528
8049
  return;
7529
8050
  }
7530
8051
  setSelectedElement(null);
7531
8052
  clearRevealedIds();
8053
+ clearExpandAroundState();
7532
8054
  return;
7533
8055
  }
7534
8056
  if (mode === "map" && element.type === "node" && selectedElement?.type === "node" && selectedElement.id === element.id) {
@@ -7543,7 +8065,9 @@ function OrganizationGraphPage({ lens = "default", timeRange = "30d" }) {
7543
8065
  }
7544
8066
  } else {
7545
8067
  clearRevealedIds();
8068
+ clearExpandAroundState();
7546
8069
  }
8070
+ setExpandAroundPreview(null);
7547
8071
  setSelectedElement(element);
7548
8072
  });
7549
8073
  useEffect(() => {
@@ -7554,8 +8078,19 @@ function OrganizationGraphPage({ lens = "default", timeRange = "30d" }) {
7554
8078
  if (!elementExists) {
7555
8079
  setSelectedElement(null);
7556
8080
  clearRevealedIds();
8081
+ clearExpandAroundState();
7557
8082
  }
7558
- }, [clearRevealedIds, selectedElement, visibleGraph]);
8083
+ }, [clearExpandAroundState, clearRevealedIds, selectedElement, visibleGraph]);
8084
+ useEffect(() => {
8085
+ if (lens !== "command-view" || !graph || appliedExpandAroundNodeIds.size === 0) {
8086
+ return;
8087
+ }
8088
+ const graphNodeIds = new Set(graph.nodes.map((node) => node.id));
8089
+ const expansionStillExists = [...appliedExpandAroundNodeIds].some((nodeId2) => graphNodeIds.has(nodeId2));
8090
+ if (!expansionStillExists) {
8091
+ clearExpandAroundState();
8092
+ }
8093
+ }, [appliedExpandAroundNodeIds, clearExpandAroundState, graph, lens]);
7559
8094
  useEffect(() => {
7560
8095
  if (lens !== "command-view") {
7561
8096
  return;
@@ -7658,6 +8193,8 @@ function OrganizationGraphPage({ lens = "default", timeRange = "30d" }) {
7658
8193
  traceResult: activeTraceResult,
7659
8194
  hiddenIds: EMPTY_GRAPH_HIDDEN_IDS,
7660
8195
  hiddenEdgeIds: EMPTY_GRAPH_HIDDEN_EDGE_IDS,
8196
+ expandedNodeIds: lens === "command-view" ? appliedExpandAroundNodeIds : EMPTY_EXPANDED_NODE_IDS,
8197
+ expandedEdgeIds: lens === "command-view" ? appliedExpandAroundEdgeIds : EMPTY_EXPANDED_EDGE_IDS,
7661
8198
  themeTokens,
7662
8199
  commandViewHealthByNodeId,
7663
8200
  focusRequest,
@@ -7897,9 +8434,27 @@ function OrganizationGraphPage({ lens = "default", timeRange = "30d" }) {
7897
8434
  selectedElement,
7898
8435
  supplementalSummary: selectionOperationalSummary,
7899
8436
  followUpSections,
8437
+ expandAroundPanel: lens === "command-view" ? /* @__PURE__ */ jsx(
8438
+ ExpandAroundPanel,
8439
+ {
8440
+ selectedNode: selectedGraphNode,
8441
+ value: expandAroundValue,
8442
+ result: expandAroundPreview,
8443
+ appliedNodeCount: appliedExpandAroundNodeIds.size,
8444
+ appliedEdgeCount: appliedExpandAroundEdgeIds.size,
8445
+ onChange: (next) => {
8446
+ setExpandAroundValue(next);
8447
+ setExpandAroundPreview(null);
8448
+ },
8449
+ onPreview: previewExpandAround,
8450
+ onApply: applyExpandAroundPreview,
8451
+ onClear: clearExpandAroundState
8452
+ }
8453
+ ) : null,
7900
8454
  onClearSelection: () => {
7901
8455
  setSelectedElement(null);
7902
8456
  clearRevealedIds();
8457
+ clearExpandAroundState();
7903
8458
  }
7904
8459
  }
7905
8460
  ) }),