@anlyx/ui 0.1.1 → 0.1.3

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 (126) hide show
  1. package/dist/components/AnalysisEvidenceList.d.ts +5 -0
  2. package/dist/components/AnalysisEvidenceList.js +61 -0
  3. package/dist/components/AnlyxAppShell.d.ts +1 -1
  4. package/dist/components/AnlyxAppShell.js +159 -15
  5. package/dist/components/ApiCallList.d.ts +3 -3
  6. package/dist/components/ApiCallList.js +12 -3
  7. package/dist/components/CanvasPlaceholder.d.ts +0 -1
  8. package/dist/components/CanvasPlaceholder.js +0 -1
  9. package/dist/components/CaptureStatusEmptyState.d.ts +0 -1
  10. package/dist/components/CaptureStatusEmptyState.js +5 -4
  11. package/dist/components/EndpointList.d.ts +0 -1
  12. package/dist/components/EndpointList.js +1 -2
  13. package/dist/components/EndpointMapCanvas.d.ts +5 -2
  14. package/dist/components/EndpointMapCanvas.js +93 -13
  15. package/dist/components/FlowLegend.d.ts +4 -2
  16. package/dist/components/FlowLegend.js +4 -2
  17. package/dist/components/FlowNodeCard.d.ts +1 -2
  18. package/dist/components/FlowNodeCard.js +25 -3
  19. package/dist/components/FlowStoryView.d.ts +22 -0
  20. package/dist/components/FlowStoryView.js +117 -0
  21. package/dist/components/InspectorPanel.d.ts +8 -3
  22. package/dist/components/InspectorPanel.js +36 -3
  23. package/dist/components/PageList.d.ts +0 -1
  24. package/dist/components/PageList.js +1 -2
  25. package/dist/components/PageStoryboardCard.d.ts +0 -1
  26. package/dist/components/PageStoryboardCard.js +4 -2
  27. package/dist/components/PageStoryboardView.d.ts +4 -3
  28. package/dist/components/PageStoryboardView.js +13 -3
  29. package/dist/components/ProcessFlowView.d.ts +21 -0
  30. package/dist/components/ProcessFlowView.js +16 -0
  31. package/dist/components/ProcessTimeline.d.ts +9 -0
  32. package/dist/components/ProcessTimeline.js +46 -0
  33. package/dist/components/ReplayControls.d.ts +6 -2
  34. package/dist/components/ReplayControls.js +31 -3
  35. package/dist/components/ScreenshotSegmentCard.d.ts +0 -1
  36. package/dist/components/ScreenshotSegmentCard.js +0 -1
  37. package/dist/components/Sidebar.d.ts +5 -4
  38. package/dist/components/Sidebar.js +19 -4
  39. package/dist/components/StatusBadge.d.ts +0 -1
  40. package/dist/components/StatusBadge.js +0 -1
  41. package/dist/flow/build-react-flow-model.d.ts +6 -2
  42. package/dist/flow/build-react-flow-model.js +15 -18
  43. package/dist/flow/layout/elk-layout.d.ts +7 -0
  44. package/dist/flow/layout/elk-layout.js +74 -0
  45. package/dist/index.d.ts +0 -1
  46. package/dist/index.js +0 -1
  47. package/dist/mock-data.d.ts +0 -1
  48. package/dist/mock-data.js +50 -5
  49. package/dist/overlay/AnlyxFlowEdge.d.ts +2 -0
  50. package/dist/overlay/AnlyxFlowEdge.js +15 -0
  51. package/dist/overlay/AnlyxFlowNode.d.ts +13 -0
  52. package/dist/overlay/AnlyxFlowNode.js +28 -0
  53. package/dist/overlay/FlowDrawer.d.ts +2 -0
  54. package/dist/overlay/FlowDrawer.js +59 -0
  55. package/dist/overlay/MainFlowCanvas.d.ts +20 -0
  56. package/dist/overlay/MainFlowCanvas.js +285 -0
  57. package/dist/overlay/RecentApiEventsTable.d.ts +5 -0
  58. package/dist/overlay/RecentApiEventsTable.js +19 -0
  59. package/dist/overlay/overlay-entry.d.ts +8 -0
  60. package/dist/overlay/overlay-entry.js +14 -0
  61. package/dist/overlay/overlay-ui.css +2 -0
  62. package/dist/overlay/overlay-ui.js +14 -0
  63. package/dist/overlay/types.d.ts +38 -0
  64. package/dist/overlay/types.js +1 -0
  65. package/dist/overlay/ui.d.ts +18 -0
  66. package/dist/overlay/ui.js +13 -0
  67. package/dist/readme-demo/ReadmeDemoApp.d.ts +4 -0
  68. package/dist/readme-demo/ReadmeDemoApp.js +126 -0
  69. package/dist/readme-demo/readme-demo-entry.d.ts +1 -0
  70. package/dist/readme-demo/readme-demo-entry.js +8 -0
  71. package/dist/replay/build-replay-steps.d.ts +0 -1
  72. package/dist/replay/build-replay-steps.js +0 -1
  73. package/dist/replay/use-replay-lite.d.ts +2 -2
  74. package/dist/replay/use-replay-lite.js +1 -1
  75. package/dist/styles.css +2018 -178
  76. package/dist/viewer/ViewerApp.d.ts +0 -1
  77. package/dist/viewer/ViewerApp.js +13 -7
  78. package/dist/viewer/viewer-entry.d.ts +1 -1
  79. package/dist/viewer/viewer-entry.js +1 -1
  80. package/package.json +7 -3
  81. package/dist/components/AnlyxAppShell.d.ts.map +0 -1
  82. package/dist/components/AnlyxAppShell.js.map +0 -1
  83. package/dist/components/ApiCallList.d.ts.map +0 -1
  84. package/dist/components/ApiCallList.js.map +0 -1
  85. package/dist/components/CanvasPlaceholder.d.ts.map +0 -1
  86. package/dist/components/CanvasPlaceholder.js.map +0 -1
  87. package/dist/components/CaptureStatusEmptyState.d.ts.map +0 -1
  88. package/dist/components/CaptureStatusEmptyState.js.map +0 -1
  89. package/dist/components/EndpointList.d.ts.map +0 -1
  90. package/dist/components/EndpointList.js.map +0 -1
  91. package/dist/components/EndpointMapCanvas.d.ts.map +0 -1
  92. package/dist/components/EndpointMapCanvas.js.map +0 -1
  93. package/dist/components/FlowLegend.d.ts.map +0 -1
  94. package/dist/components/FlowLegend.js.map +0 -1
  95. package/dist/components/FlowNodeCard.d.ts.map +0 -1
  96. package/dist/components/FlowNodeCard.js.map +0 -1
  97. package/dist/components/InspectorPanel.d.ts.map +0 -1
  98. package/dist/components/InspectorPanel.js.map +0 -1
  99. package/dist/components/PageList.d.ts.map +0 -1
  100. package/dist/components/PageList.js.map +0 -1
  101. package/dist/components/PageStoryboardCard.d.ts.map +0 -1
  102. package/dist/components/PageStoryboardCard.js.map +0 -1
  103. package/dist/components/PageStoryboardView.d.ts.map +0 -1
  104. package/dist/components/PageStoryboardView.js.map +0 -1
  105. package/dist/components/ReplayControls.d.ts.map +0 -1
  106. package/dist/components/ReplayControls.js.map +0 -1
  107. package/dist/components/ScreenshotSegmentCard.d.ts.map +0 -1
  108. package/dist/components/ScreenshotSegmentCard.js.map +0 -1
  109. package/dist/components/Sidebar.d.ts.map +0 -1
  110. package/dist/components/Sidebar.js.map +0 -1
  111. package/dist/components/StatusBadge.d.ts.map +0 -1
  112. package/dist/components/StatusBadge.js.map +0 -1
  113. package/dist/flow/build-react-flow-model.d.ts.map +0 -1
  114. package/dist/flow/build-react-flow-model.js.map +0 -1
  115. package/dist/index.d.ts.map +0 -1
  116. package/dist/index.js.map +0 -1
  117. package/dist/mock-data.d.ts.map +0 -1
  118. package/dist/mock-data.js.map +0 -1
  119. package/dist/replay/build-replay-steps.d.ts.map +0 -1
  120. package/dist/replay/build-replay-steps.js.map +0 -1
  121. package/dist/replay/use-replay-lite.d.ts.map +0 -1
  122. package/dist/replay/use-replay-lite.js.map +0 -1
  123. package/dist/viewer/ViewerApp.d.ts.map +0 -1
  124. package/dist/viewer/ViewerApp.js.map +0 -1
  125. package/dist/viewer/viewer-entry.d.ts.map +0 -1
  126. package/dist/viewer/viewer-entry.js.map +0 -1
@@ -1,5 +1,6 @@
1
1
  import type { ConfidenceLevel, EndpointFlow, FlowEdge, FlowNode } from "@anlyx/core";
2
2
  import type { Edge, Node } from "@xyflow/react";
3
+ import { type FlowLayoutVariant } from "./layout/elk-layout.js";
3
4
  export type AnlyxFlowRole = "main" | "sub" | "secondary";
4
5
  export type AnlyxFlowNodeData = {
5
6
  node: FlowNode;
@@ -16,6 +17,7 @@ export type AnlyxFlowEdgeData = {
16
17
  flowRole: AnlyxFlowRole;
17
18
  confidence?: ConfidenceLevel;
18
19
  isReplayActive?: boolean;
20
+ replayPhase?: "request" | "response" | "idle" | "complete";
19
21
  };
20
22
  export type AnlyxReactFlowNode = Node<AnlyxFlowNodeData, "anlyxNode">;
21
23
  export type AnlyxReactFlowEdge = Edge<AnlyxFlowEdgeData>;
@@ -23,5 +25,7 @@ export type ReactFlowModel = {
23
25
  nodes: AnlyxReactFlowNode[];
24
26
  edges: AnlyxReactFlowEdge[];
25
27
  };
26
- export declare function buildReactFlowModel(flow: EndpointFlow): ReactFlowModel;
27
- //# sourceMappingURL=build-react-flow-model.d.ts.map
28
+ export type BuildReactFlowModelOptions = {
29
+ variant?: FlowLayoutVariant;
30
+ };
31
+ export declare function buildReactFlowModel(flow: EndpointFlow, options?: BuildReactFlowModelOptions): ReactFlowModel;
@@ -1,41 +1,39 @@
1
- const MAIN_X_SPACING = 220;
2
- const SECONDARY_Y_OFFSET = 120;
3
- const SUB_X_SPACING = 180;
4
- const SUB_Y_OFFSET = 180;
5
- export function buildReactFlowModel(flow) {
1
+ import { layoutWithFallback } from "./layout/elk-layout.js";
2
+ export function buildReactFlowModel(flow, options = {}) {
3
+ const variant = options.variant ?? "structure";
6
4
  const mainPathSet = new Set(flow.mainPath);
7
5
  const nodesById = new Map(flow.nodes.map((node) => [node.id, node]));
8
6
  const positionedNodes = new Map();
9
7
  flow.mainPath.forEach((nodeId, index) => {
10
8
  const node = nodesById.get(nodeId);
11
9
  if (node) {
12
- positionedNodes.set(node.id, createNode(node, "main", { x: index * MAIN_X_SPACING, y: 0 }));
10
+ positionedNodes.set(node.id, createNode(node, "main", { x: index * 220, y: 0 }));
13
11
  }
14
12
  });
15
13
  flow.nodes
16
14
  .filter((node) => !mainPathSet.has(node.id))
17
15
  .forEach((node, index) => {
18
- positionedNodes.set(node.id, createNode(node, "secondary", { x: index * MAIN_X_SPACING, y: SECONDARY_Y_OFFSET }));
16
+ positionedNodes.set(node.id, createNode(node, "secondary", { x: index * 220, y: 120 }));
19
17
  });
20
18
  flow.subFlows.forEach((subFlow, subFlowIndex) => {
21
19
  const parentPosition = positionedNodes.get(subFlow.parentNodeId)?.position ?? {
22
- x: subFlowIndex * MAIN_X_SPACING,
20
+ x: subFlowIndex * 220,
23
21
  y: 0
24
22
  };
25
23
  subFlow.nodes.forEach((node, index) => {
26
24
  positionedNodes.set(node.id, createNode(node, "sub", {
27
- x: parentPosition.x + index * SUB_X_SPACING,
28
- y: parentPosition.y + SUB_Y_OFFSET + subFlowIndex * 36
25
+ x: parentPosition.x + index * 180,
26
+ y: parentPosition.y + 180 + subFlowIndex * 36
29
27
  }, subFlow.id));
30
28
  });
31
29
  });
32
- return {
30
+ return layoutWithFallback({
33
31
  nodes: [...positionedNodes.values()],
34
32
  edges: [
35
- ...flow.edges.map((edge) => createEdge(edge, getEdgeRole(edge, mainPathSet))),
36
- ...flow.subFlows.flatMap((subFlow) => subFlow.edges.map((edge) => createEdge(edge, "sub")))
33
+ ...flow.edges.map((edge, index) => createEdge(edge, getEdgeRole(edge, mainPathSet), index)),
34
+ ...flow.subFlows.flatMap((subFlow, subFlowIndex) => subFlow.edges.map((edge, edgeIndex) => createEdge(edge, "sub", `${subFlowIndex}:${edgeIndex}`)))
37
35
  ]
38
- };
36
+ }, flow.mainPath, { variant });
39
37
  }
40
38
  function createNode(node, flowRole, position, subFlowId) {
41
39
  return {
@@ -52,12 +50,12 @@ function createNode(node, flowRole, position, subFlowId) {
52
50
  }
53
51
  };
54
52
  }
55
- function createEdge(edge, flowRole) {
53
+ function createEdge(edge, flowRole, stableSuffix) {
56
54
  return {
57
- id: edge.id,
55
+ id: `${edge.id}:${stableSuffix}`,
58
56
  source: edge.from,
59
57
  target: edge.to,
60
- type: "smoothstep",
58
+ type: "anlyxEdge",
61
59
  animated: false,
62
60
  className: `anlyx-flow-edge anlyx-flow-edge--${flowRole}`,
63
61
  data: {
@@ -76,4 +74,3 @@ function getEdgeRole(edge, mainPathSet) {
76
74
  }
77
75
  return "secondary";
78
76
  }
79
- //# sourceMappingURL=build-react-flow-model.js.map
@@ -0,0 +1,7 @@
1
+ import type { ReactFlowModel } from "../build-react-flow-model.js";
2
+ export type FlowLayoutVariant = "structure" | "process";
3
+ export type LayoutOptions = {
4
+ variant: FlowLayoutVariant;
5
+ };
6
+ export declare function layoutWithElk(model: ReactFlowModel, options: LayoutOptions): Promise<ReactFlowModel>;
7
+ export declare function layoutWithFallback(model: ReactFlowModel, mainPath: string[], options: LayoutOptions): ReactFlowModel;
@@ -0,0 +1,74 @@
1
+ import ELKConstructor from "elkjs/lib/elk.bundled.js";
2
+ const Elk = ELKConstructor;
3
+ const elk = new Elk();
4
+ const NODE_WIDTH = 196;
5
+ const NODE_HEIGHT = 92;
6
+ const STRUCTURE_MAIN_Y = 0;
7
+ const STRUCTURE_SECONDARY_Y = 145;
8
+ const PROCESS_REQUEST_Y = 0;
9
+ const PROCESS_BRANCH_Y = 145;
10
+ const MAIN_X_SPACING = 232;
11
+ const BRANCH_X_SPACING = 190;
12
+ export async function layoutWithElk(model, options) {
13
+ if (model.nodes.length === 0) {
14
+ return model;
15
+ }
16
+ const graph = {
17
+ id: "anlyx-flow",
18
+ layoutOptions: {
19
+ "elk.algorithm": "layered",
20
+ "elk.direction": "RIGHT",
21
+ "elk.layered.spacing.nodeNodeBetweenLayers": options.variant === "process" ? "72" : "84",
22
+ "elk.spacing.nodeNode": options.variant === "process" ? "44" : "52",
23
+ "elk.layered.nodePlacement.strategy": "NETWORK_SIMPLEX",
24
+ "elk.layered.crossingMinimization.strategy": "LAYER_SWEEP"
25
+ },
26
+ children: model.nodes.map((node) => ({
27
+ id: node.id,
28
+ width: NODE_WIDTH,
29
+ height: NODE_HEIGHT
30
+ })),
31
+ edges: model.edges.map((edge) => ({
32
+ id: edge.id,
33
+ sources: [edge.source],
34
+ targets: [edge.target]
35
+ }))
36
+ };
37
+ const layoutedGraph = await elk.layout(graph);
38
+ const positions = new Map(layoutedGraph.children?.map((node) => [node.id, { x: node.x ?? 0, y: node.y ?? 0 }]) ?? []);
39
+ return {
40
+ nodes: model.nodes.map((node) => ({
41
+ ...node,
42
+ position: positions.get(node.id) ?? node.position
43
+ })),
44
+ edges: model.edges
45
+ };
46
+ }
47
+ export function layoutWithFallback(model, mainPath, options) {
48
+ const mainPathIndex = new Map(mainPath.map((nodeId, index) => [nodeId, index]));
49
+ let secondaryIndex = 0;
50
+ return {
51
+ nodes: model.nodes.map((node) => {
52
+ const pathIndex = mainPathIndex.get(node.id);
53
+ if (pathIndex !== undefined) {
54
+ return {
55
+ ...node,
56
+ position: {
57
+ x: pathIndex * MAIN_X_SPACING,
58
+ y: options.variant === "process" ? PROCESS_REQUEST_Y : STRUCTURE_MAIN_Y
59
+ }
60
+ };
61
+ }
62
+ const nextSecondaryIndex = secondaryIndex++;
63
+ return {
64
+ ...node,
65
+ position: {
66
+ x: Math.max(1, Math.floor(nextSecondaryIndex / 2) + 1) * BRANCH_X_SPACING,
67
+ y: (options.variant === "process" ? PROCESS_BRANCH_Y : STRUCTURE_SECONDARY_Y) +
68
+ (nextSecondaryIndex % 2) * 116
69
+ }
70
+ };
71
+ }),
72
+ edges: model.edges
73
+ };
74
+ }
package/dist/index.d.ts CHANGED
@@ -26,4 +26,3 @@ export { buildReactFlowModel } from "./flow/build-react-flow-model.js";
26
26
  export { mockScanResult } from "./mock-data.js";
27
27
  export { buildReplaySteps } from "./replay/build-replay-steps.js";
28
28
  export { useReplayLite } from "./replay/use-replay-lite.js";
29
- //# sourceMappingURL=index.d.ts.map
package/dist/index.js CHANGED
@@ -19,4 +19,3 @@ export { buildReactFlowModel } from "./flow/build-react-flow-model.js";
19
19
  export { mockScanResult } from "./mock-data.js";
20
20
  export { buildReplaySteps } from "./replay/build-replay-steps.js";
21
21
  export { useReplayLite } from "./replay/use-replay-lite.js";
22
- //# sourceMappingURL=index.js.map
@@ -1,3 +1,2 @@
1
1
  import { type ScanResult } from "@anlyx/core";
2
2
  export declare const mockScanResult: ScanResult;
3
- //# sourceMappingURL=mock-data.d.ts.map
package/dist/mock-data.js CHANGED
@@ -36,7 +36,15 @@ export const mockScanResult = scanResultSchema.parse({
36
36
  id: "endpoint:get:/api/public/benefits/{id}",
37
37
  type: "endpoint",
38
38
  label: "GET /api/public/benefits/{id}",
39
- confidence: "high"
39
+ confidence: "high",
40
+ evidence: [
41
+ {
42
+ label: "Matched Spring mapping",
43
+ detail: '@GetMapping("/api/public/benefits/{id}")',
44
+ source: "spring-endpoint-scanner",
45
+ confidence: "high"
46
+ }
47
+ ]
40
48
  },
41
49
  {
42
50
  id: "controller:PublicBenefitController#getDetail",
@@ -44,25 +52,63 @@ export const mockScanResult = scanResultSchema.parse({
44
52
  label: "PublicBenefitController#getDetail",
45
53
  filePath: "backend/src/main/java/com/zup/benefit/PublicBenefitController.java",
46
54
  lineNumber: 24,
47
- confidence: "unknown"
55
+ confidence: "unknown",
56
+ evidence: [
57
+ {
58
+ label: "Controller method detected",
59
+ detail: "PublicBenefitController#getDetail",
60
+ source: "spring-flow-scanner",
61
+ confidence: "unknown"
62
+ }
63
+ ]
48
64
  },
49
65
  {
50
66
  id: "service:PublicBenefitService#getBenefitDetail",
51
67
  type: "service",
52
68
  label: "PublicBenefitService#getBenefitDetail",
53
- confidence: "high"
69
+ confidence: "high",
70
+ evidence: [
71
+ {
72
+ label: "Matched from controller field call",
73
+ detail: "publicBenefitService.getBenefitDetail(...)",
74
+ source: "spring-flow-scanner",
75
+ confidence: "high"
76
+ },
77
+ {
78
+ label: "Resolved service class by field type",
79
+ detail: "PublicBenefitService",
80
+ source: "spring-flow-scanner",
81
+ confidence: "high"
82
+ }
83
+ ]
54
84
  },
55
85
  {
56
86
  id: "repository:BenefitRepository#findById",
57
87
  type: "repository",
58
88
  label: "BenefitRepository#findById",
59
- confidence: "high"
89
+ confidence: "high",
90
+ evidence: [
91
+ {
92
+ label: "Repository call detected in method body",
93
+ detail: "benefitRepository.findById(...)",
94
+ source: "spring-flow-scanner",
95
+ confidence: "high"
96
+ }
97
+ ]
60
98
  },
61
99
  {
62
100
  id: "database:benefits",
63
101
  type: "database",
64
102
  label: "benefits",
65
103
  confidence: "high",
104
+ evidence: [
105
+ {
106
+ label: "Resolved repository entity",
107
+ detail: "Benefit -> benefits",
108
+ source: "spring-flow-scanner",
109
+ confidence: "high"
110
+ }
111
+ ],
66
112
  metadata: {
67
113
  tableName: "benefits"
68
114
  }
@@ -219,4 +265,3 @@ export const mockScanResult = scanResultSchema.parse({
219
265
  }
220
266
  ]
221
267
  });
222
- //# sourceMappingURL=mock-data.js.map
@@ -0,0 +1,2 @@
1
+ import { type EdgeProps } from "@xyflow/react";
2
+ export declare function AnlyxFlowEdge({ id, sourceX, sourceY, targetX, targetY, sourcePosition, targetPosition, markerEnd, data }: EdgeProps): JSX.Element;
@@ -0,0 +1,15 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { BaseEdge, getSmoothStepPath } from "@xyflow/react";
3
+ export function AnlyxFlowEdge({ id, sourceX, sourceY, targetX, targetY, sourcePosition, targetPosition, markerEnd, data }) {
4
+ const [edgePath] = getSmoothStepPath({
5
+ sourceX,
6
+ sourceY,
7
+ sourcePosition,
8
+ targetX,
9
+ targetY,
10
+ targetPosition,
11
+ borderRadius: 14
12
+ });
13
+ const tone = (data?.tone ?? "blue").toLowerCase();
14
+ return (_jsx(BaseEdge, { id: id, className: `anlyx-flow-rf-edge anlyx-flow-rf-edge--${tone}`, path: edgePath, ...(markerEnd ? { markerEnd } : {}) }));
15
+ }
@@ -0,0 +1,13 @@
1
+ import { type NodeProps } from "@xyflow/react";
2
+ export type AnlyxFlowNodeData = {
3
+ kind: "api" | "controller" | "service" | "repository" | "database" | "auth" | "result";
4
+ label: string;
5
+ value: string;
6
+ sub?: string;
7
+ badge: string;
8
+ accent: "blue" | "green" | "amber" | "violet" | "gray";
9
+ fullValue?: string;
10
+ step?: string;
11
+ state?: "taken" | "blocked" | "scanned";
12
+ };
13
+ export declare function AnlyxFlowNode({ data }: NodeProps): JSX.Element;
@@ -0,0 +1,28 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Handle, Position } from "@xyflow/react";
3
+ import { Box, Code2, Database, Globe2, Layers3, LockKeyhole, ShieldCheck } from "lucide-react";
4
+ import { Badge, Card, Tooltip } from "./ui.js";
5
+ export function AnlyxFlowNode({ data }) {
6
+ const nodeData = data;
7
+ const Icon = getIcon(nodeData.kind);
8
+ const state = nodeData.state ?? "taken";
9
+ return (_jsxs(Card, { className: `anlyx-flow-rf-node anlyx-flow-rf-node--${nodeData.accent} anlyx-flow-rf-node--${state}`, children: [_jsx(Handle, { className: "anlyx-flow-rf-handle", position: Position.Left, type: "target" }), _jsxs("div", { className: "anlyx-flow-rf-node__top", children: [_jsx("span", { className: "anlyx-flow-rf-node__icon", "aria-hidden": "true", children: _jsx(Icon, { size: 14, strokeWidth: 2.25 }) }), _jsx("span", { className: "anlyx-flow-rf-node__label", children: nodeData.label }), nodeData.step ? _jsx("span", { className: "anlyx-flow-rf-node__step", children: nodeData.step }) : null] }), _jsx(Tooltip, { content: nodeData.fullValue ?? nodeData.value, children: _jsx("p", { className: "anlyx-flow-rf-node__value", children: nodeData.value }) }), nodeData.sub ? _jsx("p", { className: "anlyx-flow-rf-node__sub", children: nodeData.sub }) : null, _jsx(Badge, { tone: nodeData.accent === "violet" ? "violet" : nodeData.accent, children: nodeData.badge }), _jsx(Handle, { className: "anlyx-flow-rf-handle", position: Position.Right, type: "source" })] }));
10
+ }
11
+ function getIcon(kind) {
12
+ switch (kind) {
13
+ case "api":
14
+ return Globe2;
15
+ case "controller":
16
+ return Code2;
17
+ case "service":
18
+ return Layers3;
19
+ case "repository":
20
+ return Box;
21
+ case "database":
22
+ return Database;
23
+ case "auth":
24
+ return LockKeyhole;
25
+ case "result":
26
+ return ShieldCheck;
27
+ }
28
+ }
@@ -0,0 +1,2 @@
1
+ import type { FlowDrawerProps } from "./types.js";
2
+ export declare function FlowDrawer({ selectedEvent, events, latestAction, scannedHints, loadError }: FlowDrawerProps): JSX.Element;
@@ -0,0 +1,59 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Badge, Card } from "./ui.js";
3
+ import { MainFlowCanvas } from "./MainFlowCanvas.js";
4
+ import { RecentApiEventsTable } from "./RecentApiEventsTable.js";
5
+ export function FlowDrawer({ selectedEvent, events, latestAction, scannedHints = [], loadError }) {
6
+ if (loadError) {
7
+ return (_jsx("div", { className: "anlyx-flow-drawer-body", children: _jsxs(Card, { children: [_jsx("h3", { className: "anlyx-ov-section-title", children: "Report data" }), _jsx("div", { className: "anlyx-ov-empty", children: loadError })] }) }));
8
+ }
9
+ if (!selectedEvent) {
10
+ return (_jsxs("div", { className: "anlyx-flow-drawer-body", children: [latestAction ? _jsx(NoPrimaryRequest, { latestAction: latestAction }) : _jsx(WaitingForAction, {}), scannedHints.length > 0 ? _jsx(ScannedHints, { hints: scannedHints }) : null, _jsx(RecentApiEventsTable, { events: events, selectedEventId: null })] }));
11
+ }
12
+ return (_jsxs("div", { className: "anlyx-flow-drawer-body", children: [_jsx(CapturedRequest, { event: selectedEvent }), selectedEvent.matchedEndpoint ? (_jsx(MainFlowCanvas, { flow: selectedEvent.matchedFlow, method: selectedEvent.method, path: selectedEvent.path, status: selectedEvent.status, ...(selectedEvent.matchedEndpoint.confidence
13
+ ? { endpointConfidence: selectedEvent.matchedEndpoint.confidence }
14
+ : {}) })) : (_jsxs(Card, { className: "anlyx-unmatched-card", children: [_jsx("h3", { children: "No scanned endpoint matched" }), _jsx("p", { children: "Anlyx saw this browser request, but no scanned endpoint path matched it." })] })), _jsx(RecentApiEventsTable, { events: events, selectedEventId: selectedEvent.id })] }));
15
+ }
16
+ function ScannedHints({ hints }) {
17
+ return (_jsxs(Card, { className: "anlyx-scanned-hints", children: [_jsxs("div", { className: "anlyx-scanned-hints__head", children: [_jsxs("div", { children: [_jsx("h3", { children: "Scanned / inferred hints" }), _jsx("p", { children: "These are known page links, not browser-live requests." })] }), _jsx(Badge, { tone: "gray", children: "Not browser-live" })] }), _jsx("div", { className: "anlyx-scanned-hints__list", children: hints.slice(0, 4).map((hint, index) => (_jsxs("div", { className: "anlyx-scanned-hint", children: [_jsxs("div", { children: [_jsx(Badge, { tone: hint.evidence === "capture" ? "green" : "violet", children: hint.evidence === "capture" ? "captured page link" : "scanned page link" }), _jsx("strong", { title: hint.endpointLabel ?? `${hint.method} ${hint.path}`, children: hint.endpointLabel ?? `${hint.method} ${hint.path}` })] }), _jsx("p", { title: hint.pageFilePath ?? hint.pageRoute, children: hint.pageRoute })] }, `${hint.pageRoute}:${hint.method}:${hint.path}:${index}`))) })] }));
18
+ }
19
+ function WaitingForAction() {
20
+ return (_jsxs(Card, { children: [_jsx("h3", { className: "anlyx-ov-section-title", children: "Waiting" }), _jsx("div", { className: "anlyx-ov-empty", children: "Use the app normally. Anlyx opens the main flow for requests caused by your direct action. Background requests stay in Recent API events until you select them." })] }));
21
+ }
22
+ function NoPrimaryRequest({ latestAction }) {
23
+ return (_jsxs(Card, { className: "anlyx-no-primary-card", children: [_jsxs("div", { children: [_jsx(Badge, { tone: "amber", children: "No action API yet" }), _jsx("h3", { children: formatAction(latestAction) }), _jsx("p", { children: latestAction.selector ?? "No stable selector captured" })] }), _jsx("div", { className: "anlyx-no-primary-card__note", children: "Only background session checks were detected. They are hidden from the main flow." })] }));
24
+ }
25
+ function CapturedRequest({ event }) {
26
+ const matched = Boolean(event.matchedEndpoint);
27
+ return (_jsxs(Card, { className: "anlyx-captured-request", children: [_jsxs("div", { className: "anlyx-captured-request__top", children: [_jsxs("div", { children: [_jsx("h3", { children: "Captured request" }), _jsxs("div", { className: "anlyx-captured-request__path", children: [_jsx(Badge, { tone: "blue", children: event.method }), _jsx("strong", { children: event.path })] })] }), _jsx(Badge, { tone: matched ? "green" : "amber", children: matched ? "matched" : "unmatched" })] }), _jsxs("div", { className: "anlyx-captured-request__summary", children: [_jsxs("span", { children: [_jsx("b", { children: event.matchedEndpoint ? "endpoint" : "browser" }), " ", event.matchedEndpoint
28
+ ? `${event.matchedEndpoint.method} ${event.matchedEndpoint.path}`
29
+ : event.path] }), _jsx("span", { children: _jsx("b", { children: getStatusLabel(event.status) }) }), _jsxs("span", { children: [event.durationMs, "ms", event.count && event.count > 1 ? ` · seen x${event.count}` : ""] }), event.matchedEndpoint?.confidence ? (_jsxs("span", { children: ["confidence ", event.matchedEndpoint.confidence] })) : null, _jsx("span", { children: event.triggeredBy
30
+ ? "user action"
31
+ : event.source === "health"
32
+ ? "health/background"
33
+ : "background" })] }), _jsxs("div", { className: "anlyx-captured-request__steps", children: [_jsx(CapturedStep, { label: "Action", title: event.triggeredBy ? formatAction(event.triggeredBy) : "No user action captured", detail: event.triggeredBy?.selector ?? "Request may have fired on page load", tone: event.triggeredBy ? "blue" : "amber" }), _jsx(CapturedStep, { label: "Request", title: `${event.method} ${event.path}`, detail: matched ? "Matched scanned endpoint" : "No scanned endpoint matched", tone: matched ? "green" : "amber" }), _jsx(CapturedStep, { label: "Result", title: getStatusLabel(event.status), detail: `${event.durationMs}ms${event.count && event.count > 1 ? ` · seen x${event.count}` : ""}`, tone: Number(event.status) >= 400 ? "amber" : "green" })] })] }));
34
+ }
35
+ function CapturedStep({ label, title, detail, tone }) {
36
+ return (_jsxs("div", { className: `anlyx-captured-step anlyx-captured-step--${tone}`, children: [_jsx("span", { className: "anlyx-captured-step__dot" }), _jsxs("div", { children: [_jsx("p", { children: label }), _jsx("strong", { children: title }), _jsx("span", { children: detail })] })] }));
37
+ }
38
+ function formatAction(action) {
39
+ return `${action.type ?? "Action"} ${action.label ?? "unnamed element"}`;
40
+ }
41
+ function getStatusLabel(status) {
42
+ const numeric = Number(status);
43
+ if (numeric === 401) {
44
+ return "login required · 401";
45
+ }
46
+ if (numeric === 403) {
47
+ return "permission denied · 403";
48
+ }
49
+ if (numeric >= 500) {
50
+ return `server error · ${status}`;
51
+ }
52
+ if (numeric >= 400) {
53
+ return `client error · ${status}`;
54
+ }
55
+ if (numeric >= 200) {
56
+ return `success · ${status}`;
57
+ }
58
+ return `status ${status}`;
59
+ }
@@ -0,0 +1,20 @@
1
+ import type { EndpointFlow } from "@anlyx/core";
2
+ import { type Edge, type Node } from "@xyflow/react";
3
+ import { type AnlyxFlowNodeData } from "./AnlyxFlowNode.js";
4
+ export type MainFlowCanvasProps = {
5
+ flow: EndpointFlow | null | undefined;
6
+ method: string;
7
+ path: string;
8
+ status: string | number;
9
+ endpointConfidence?: string;
10
+ };
11
+ export declare function MainFlowCanvas({ flow, method, path, status, endpointConfidence }: MainFlowCanvasProps): JSX.Element;
12
+ export declare function buildDrawerFlowModel({ flow, method, path, status }: {
13
+ flow: EndpointFlow | null | undefined;
14
+ method: string;
15
+ path: string;
16
+ status: string | number;
17
+ }): {
18
+ nodes: Node<AnlyxFlowNodeData>[];
19
+ edges: Edge[];
20
+ };