@industry-theme/principal-view-panels 0.1.47 → 0.1.48

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.
@@ -16,6 +16,7 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
16
16
  import require$$0, { jsxs, jsx, Fragment } from "react/jsx-runtime";
17
17
  import React2, { createContext, useContext, forwardRef, createElement, useState, useCallback, useRef, useEffect } from "react";
18
18
  import require$$2 from "react-dom";
19
+ import { trace, SpanStatusCode } from "@opentelemetry/api";
19
20
  var ThemeContext;
20
21
  var getThemeContext = () => {
21
22
  if (typeof window !== "undefined") {
@@ -5662,6 +5663,623 @@ ${description}` : `# ${name}`;
5662
5663
  return canvas;
5663
5664
  }
5664
5665
  }
5666
+ class EventValidationError extends Error {
5667
+ constructor(eventName, nodeId, errors) {
5668
+ super(`Event validation failed for '${eventName}' on node '${nodeId}':
5669
+ ${errors.join("\n")}`);
5670
+ this.eventName = eventName;
5671
+ this.nodeId = nodeId;
5672
+ this.errors = errors;
5673
+ this.name = "EventValidationError";
5674
+ }
5675
+ }
5676
+ class EventValidator {
5677
+ constructor(canvas) {
5678
+ this.eventSchemas = /* @__PURE__ */ new Map();
5679
+ this.indexEventSchemas(canvas);
5680
+ }
5681
+ /**
5682
+ * Index all event schemas from canvas nodes
5683
+ */
5684
+ indexEventSchemas(canvas) {
5685
+ var _a;
5686
+ if (!canvas.nodes)
5687
+ return;
5688
+ for (const node of canvas.nodes) {
5689
+ if ((_a = node.pv) == null ? void 0 : _a.events) {
5690
+ this.eventSchemas.set(node.id, node.pv.events);
5691
+ }
5692
+ }
5693
+ }
5694
+ /**
5695
+ * Validate an event against the schema for a specific node
5696
+ */
5697
+ validate(nodeId, eventName, attributes) {
5698
+ const errors = [];
5699
+ const nodeSchema = this.eventSchemas.get(nodeId);
5700
+ if (!nodeSchema) {
5701
+ return { valid: true, errors: [] };
5702
+ }
5703
+ const eventSchema = nodeSchema[eventName];
5704
+ if (!eventSchema) {
5705
+ errors.push(`Event '${eventName}' is not defined in schema for node '${nodeId}'`);
5706
+ return { valid: false, errors };
5707
+ }
5708
+ for (const [fieldName, fieldSchema] of Object.entries(eventSchema.attributes)) {
5709
+ const value = attributes[fieldName];
5710
+ if (fieldSchema.required && value === void 0) {
5711
+ errors.push(`Required field '${fieldName}' is missing`);
5712
+ continue;
5713
+ }
5714
+ if (value === void 0) {
5715
+ continue;
5716
+ }
5717
+ const actualType = this.getValueType(value);
5718
+ if (actualType !== fieldSchema.type) {
5719
+ errors.push(`Field '${fieldName}' has type '${actualType}' but schema expects '${fieldSchema.type}'`);
5720
+ }
5721
+ }
5722
+ const schemaFields = new Set(Object.keys(eventSchema.attributes));
5723
+ for (const fieldName of Object.keys(attributes)) {
5724
+ if (fieldName.startsWith("code.") || fieldName === "description") {
5725
+ continue;
5726
+ }
5727
+ if (!schemaFields.has(fieldName)) ;
5728
+ }
5729
+ return {
5730
+ valid: errors.length === 0,
5731
+ errors
5732
+ };
5733
+ }
5734
+ /**
5735
+ * Get the type of a value
5736
+ */
5737
+ getValueType(value) {
5738
+ if (Array.isArray(value))
5739
+ return "array";
5740
+ if (value === null)
5741
+ return "object";
5742
+ return typeof value;
5743
+ }
5744
+ /**
5745
+ * Get all event schemas for a node
5746
+ */
5747
+ getNodeSchema(nodeId) {
5748
+ return this.eventSchemas.get(nodeId);
5749
+ }
5750
+ /**
5751
+ * Get all defined event names for a node
5752
+ */
5753
+ getNodeEventNames(nodeId) {
5754
+ const schema2 = this.eventSchemas.get(nodeId);
5755
+ return schema2 ? Object.keys(schema2) : [];
5756
+ }
5757
+ /**
5758
+ * Check if a node has event schema defined
5759
+ */
5760
+ hasSchema(nodeId) {
5761
+ return this.eventSchemas.has(nodeId);
5762
+ }
5763
+ }
5764
+ function createValidatedEmitter(validator, nodeId, addEventFn, options = {}) {
5765
+ return (eventName, attributes) => {
5766
+ const result = validator.validate(nodeId, eventName, attributes);
5767
+ if (!result.valid) {
5768
+ if (options.strict !== false) {
5769
+ throw new EventValidationError(eventName, nodeId, result.errors);
5770
+ } else {
5771
+ console.warn(`Event validation warnings for '${eventName}' on node '${nodeId}':`, result.errors);
5772
+ }
5773
+ }
5774
+ addEventFn(eventName, attributes);
5775
+ };
5776
+ }
5777
+ class TypeScriptGenerator {
5778
+ constructor() {
5779
+ this.language = "typescript";
5780
+ }
5781
+ generate(canvas, options) {
5782
+ var _a, _b, _c, _d, _e, _f, _g;
5783
+ const opts = {
5784
+ readonly: ((_a = options == null ? void 0 : options.style) == null ? void 0 : _a.readonly) ?? false,
5785
+ strictNullChecks: ((_b = options == null ? void 0 : options.style) == null ? void 0 : _b.strictNullChecks) ?? true,
5786
+ includeDocComments: ((_c = options == null ? void 0 : options.style) == null ? void 0 : _c.includeDocComments) ?? true,
5787
+ namespace: options == null ? void 0 : options.namespace
5788
+ };
5789
+ const lines = [];
5790
+ lines.push("/**");
5791
+ lines.push(` * Generated types from canvas: ${((_d = canvas.pv) == null ? void 0 : _d.name) || "Untitled"}`);
5792
+ lines.push(" *");
5793
+ lines.push(" * DO NOT EDIT MANUALLY - This file is auto-generated");
5794
+ lines.push(" * Generated by @principal-ai/principal-view-core");
5795
+ lines.push(" *");
5796
+ lines.push(` * Canvas: ${((_e = canvas.pv) == null ? void 0 : _e.description) || "No description"}`);
5797
+ lines.push(` * Version: ${((_f = canvas.pv) == null ? void 0 : _f.version) || "1.0.0"}`);
5798
+ lines.push(" */");
5799
+ lines.push("");
5800
+ if (opts.namespace) {
5801
+ lines.push(`export namespace ${opts.namespace} {`);
5802
+ }
5803
+ if (canvas.nodes) {
5804
+ for (const node of canvas.nodes) {
5805
+ if ((_g = node.pv) == null ? void 0 : _g.events) {
5806
+ lines.push(...this.generateNodeTypes(node.id, node.pv.events, opts));
5807
+ lines.push("");
5808
+ }
5809
+ }
5810
+ }
5811
+ lines.push(...this.generateEventNameUnion(canvas, opts));
5812
+ lines.push("");
5813
+ lines.push(...this.generateEmitterTypes(canvas, opts));
5814
+ if (opts.namespace) {
5815
+ lines.push("}");
5816
+ }
5817
+ const code = lines.join("\n");
5818
+ const filename = this.generateFilename(canvas);
5819
+ return {
5820
+ code,
5821
+ extension: "ts",
5822
+ filename
5823
+ };
5824
+ }
5825
+ generateNodeTypes(nodeId, events, opts) {
5826
+ const lines = [];
5827
+ const typeName = this.nodeIdToTypeName(nodeId);
5828
+ if (opts.includeDocComments) {
5829
+ lines.push(`/**`);
5830
+ lines.push(` * Event types for node: ${nodeId}`);
5831
+ lines.push(` */`);
5832
+ }
5833
+ lines.push(`export namespace ${typeName} {`);
5834
+ for (const [eventName, eventSchema] of Object.entries(events)) {
5835
+ lines.push(...this.generateEventInterface(eventName, eventSchema, opts));
5836
+ lines.push("");
5837
+ }
5838
+ const eventNames = Object.keys(events);
5839
+ const interfaceNames = eventNames.map((name) => this.eventNameToTypeName(name));
5840
+ lines.push(" /**");
5841
+ lines.push(` * Union of all event types for ${nodeId}`);
5842
+ lines.push(" */");
5843
+ lines.push(` export type Event = ${interfaceNames.join(" | ")};`);
5844
+ lines.push("");
5845
+ lines.push(" /**");
5846
+ lines.push(` * Literal union of event names for ${nodeId}`);
5847
+ lines.push(" */");
5848
+ lines.push(` export type EventName = ${eventNames.map((n) => `'${n}'`).join(" | ")};`);
5849
+ lines.push("}");
5850
+ return lines;
5851
+ }
5852
+ generateEventInterface(eventName, schema2, opts) {
5853
+ const lines = [];
5854
+ const interfaceName = this.eventNameToTypeName(eventName);
5855
+ const readonly = opts.readonly ? "readonly " : "";
5856
+ if (opts.includeDocComments) {
5857
+ lines.push(" /**");
5858
+ lines.push(` * ${schema2.description}`);
5859
+ lines.push(" */");
5860
+ }
5861
+ lines.push(` export interface ${interfaceName} {`);
5862
+ lines.push(` ${readonly}name: '${eventName}';`);
5863
+ lines.push(` ${readonly}attributes: {`);
5864
+ for (const [fieldName, fieldSchema] of Object.entries(schema2.attributes)) {
5865
+ const optional = !fieldSchema.required && opts.strictNullChecks ? "?" : "";
5866
+ const type2 = this.fieldTypeToTsType(fieldSchema);
5867
+ if (opts.includeDocComments && fieldSchema.description) {
5868
+ lines.push(` /** ${fieldSchema.description} */`);
5869
+ }
5870
+ lines.push(` ${readonly}'${fieldName}'${optional}: ${type2};`);
5871
+ }
5872
+ lines.push(" };");
5873
+ lines.push(" }");
5874
+ return lines;
5875
+ }
5876
+ generateEventNameUnion(canvas, opts) {
5877
+ var _a;
5878
+ const lines = [];
5879
+ const allEventNames = /* @__PURE__ */ new Set();
5880
+ if (canvas.nodes) {
5881
+ for (const node of canvas.nodes) {
5882
+ if ((_a = node.pv) == null ? void 0 : _a.events) {
5883
+ Object.keys(node.pv.events).forEach((name) => allEventNames.add(name));
5884
+ }
5885
+ }
5886
+ }
5887
+ if (allEventNames.size === 0) {
5888
+ return lines;
5889
+ }
5890
+ if (opts.includeDocComments) {
5891
+ lines.push("/**");
5892
+ lines.push(" * Union of all event names in the canvas");
5893
+ lines.push(" */");
5894
+ }
5895
+ lines.push(`export type AllEventNames = ${Array.from(allEventNames).map((n) => `'${n}'`).join(" | ")};`);
5896
+ return lines;
5897
+ }
5898
+ generateEmitterTypes(canvas, opts) {
5899
+ const lines = [];
5900
+ if (opts.includeDocComments) {
5901
+ lines.push("/**");
5902
+ lines.push(" * Type-safe event emitter for a specific node");
5903
+ lines.push(" *");
5904
+ lines.push(" * @example");
5905
+ lines.push(" * ```typescript");
5906
+ lines.push(" * const emit: NodeEmitter<GraphConverter.EventName, GraphConverter.Event> = ...");
5907
+ lines.push(" * emit('conversion.started', {");
5908
+ lines.push(" * name: 'conversion.started',");
5909
+ lines.push(" * attributes: { ... }");
5910
+ lines.push(" * });");
5911
+ lines.push(" * ```");
5912
+ lines.push(" */");
5913
+ }
5914
+ lines.push("export type NodeEmitter<");
5915
+ lines.push(" TEventName extends string,");
5916
+ lines.push(" TEvent extends { name: TEventName; attributes: Record<string, any> }");
5917
+ lines.push("> = (event: TEvent) => void;");
5918
+ lines.push("");
5919
+ if (opts.includeDocComments) {
5920
+ lines.push("/**");
5921
+ lines.push(" * Type-safe event emitter by event name");
5922
+ lines.push(" *");
5923
+ lines.push(" * @example");
5924
+ lines.push(" * ```typescript");
5925
+ lines.push(" * const emit: NodeEmitterByName<GraphConverter.Event> = ...");
5926
+ lines.push(" * emit('conversion.started', {");
5927
+ lines.push(" * 'config.nodeTypes': 2,");
5928
+ lines.push(" * 'config.edgeTypes': 1");
5929
+ lines.push(" * });");
5930
+ lines.push(" * ```");
5931
+ lines.push(" */");
5932
+ }
5933
+ lines.push("export type NodeEmitterByName<");
5934
+ lines.push(" TEvent extends { name: string; attributes: Record<string, any> }");
5935
+ lines.push("> = <TName extends TEvent['name']>(");
5936
+ lines.push(" eventName: TName,");
5937
+ lines.push(" attributes: Extract<TEvent, { name: TName }>['attributes']");
5938
+ lines.push(") => void;");
5939
+ return lines;
5940
+ }
5941
+ nodeIdToTypeName(nodeId) {
5942
+ return nodeId.split(/[-_]/).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
5943
+ }
5944
+ eventNameToTypeName(eventName) {
5945
+ return eventName.split(".").map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
5946
+ }
5947
+ fieldTypeToTsType(fieldSchema) {
5948
+ switch (fieldSchema.type) {
5949
+ case "string":
5950
+ return "string";
5951
+ case "number":
5952
+ return "number";
5953
+ case "boolean":
5954
+ return "boolean";
5955
+ case "array":
5956
+ return "any[]";
5957
+ case "object":
5958
+ return "Record<string, any>";
5959
+ default:
5960
+ return "any";
5961
+ }
5962
+ }
5963
+ generateFilename(canvas) {
5964
+ var _a;
5965
+ const name = ((_a = canvas.pv) == null ? void 0 : _a.name) || "canvas";
5966
+ const kebab = name.toLowerCase().replace(/\s+/g, "-").replace(/[^a-z0-9-]/g, "");
5967
+ return `${kebab}.types.ts`;
5968
+ }
5969
+ }
5970
+ function generateTypes(canvas, options = { language: "typescript" }) {
5971
+ const generator = getGenerator(options.language);
5972
+ return generator.generate(canvas, options);
5973
+ }
5974
+ function getGenerator(language) {
5975
+ switch (language) {
5976
+ case "typescript":
5977
+ return new TypeScriptGenerator();
5978
+ case "python":
5979
+ throw new Error("Python generator not yet implemented. See TypeScriptGenerator as reference.");
5980
+ case "go":
5981
+ throw new Error("Go generator not yet implemented. See TypeScriptGenerator as reference.");
5982
+ case "rust":
5983
+ throw new Error("Rust generator not yet implemented. See TypeScriptGenerator as reference.");
5984
+ default:
5985
+ throw new Error(`Unsupported language: ${language}`);
5986
+ }
5987
+ }
5988
+ const generatorRegistry = {
5989
+ generators: /* @__PURE__ */ new Map([
5990
+ ["typescript", new TypeScriptGenerator()]
5991
+ ]),
5992
+ register(generator) {
5993
+ this.generators.set(generator.language, generator);
5994
+ },
5995
+ get(language) {
5996
+ return this.generators.get(language);
5997
+ },
5998
+ list() {
5999
+ return Array.from(this.generators.keys());
6000
+ }
6001
+ };
6002
+ const DEFAULT_OPTIONS$4 = {
6003
+ groupByService: true,
6004
+ layout: "hierarchical",
6005
+ nodeWidth: 200,
6006
+ nodeHeight: 60,
6007
+ horizontalSpacing: 50,
6008
+ verticalSpacing: 80,
6009
+ includeAttributes: true,
6010
+ minDurationMs: 0,
6011
+ kindColors: {
6012
+ SERVER: "#4f46e5",
6013
+ // Indigo
6014
+ CLIENT: "#0891b2",
6015
+ // Cyan
6016
+ PRODUCER: "#059669",
6017
+ // Emerald
6018
+ CONSUMER: "#059669",
6019
+ // Emerald
6020
+ INTERNAL: "#6b7280"
6021
+ // Gray
6022
+ }
6023
+ };
6024
+ function kindToShape(kind) {
6025
+ const shapes = {
6026
+ SERVER: "hexagon",
6027
+ CLIENT: "diamond",
6028
+ PRODUCER: "rectangle",
6029
+ CONSUMER: "rectangle",
6030
+ INTERNAL: "circle"
6031
+ };
6032
+ return shapes[kind] ?? "rectangle";
6033
+ }
6034
+ function getServiceName(span) {
6035
+ var _a;
6036
+ return ((_a = span.resource) == null ? void 0 : _a["service.name"]) ?? "unknown";
6037
+ }
6038
+ function formatDuration(ms) {
6039
+ if (ms < 1)
6040
+ return `${(ms * 1e3).toFixed(0)}μs`;
6041
+ if (ms < 1e3)
6042
+ return `${ms.toFixed(2)}ms`;
6043
+ return `${(ms / 1e3).toFixed(2)}s`;
6044
+ }
6045
+ function groupByTraceId(spans) {
6046
+ const traces = /* @__PURE__ */ new Map();
6047
+ for (const span of spans) {
6048
+ const existing = traces.get(span.traceId) ?? [];
6049
+ existing.push(span);
6050
+ traces.set(span.traceId, existing);
6051
+ }
6052
+ return traces;
6053
+ }
6054
+ function buildSpanTree(spans) {
6055
+ const spanMap = new Map(spans.map((s) => [s.spanId, s]));
6056
+ const childMap = /* @__PURE__ */ new Map();
6057
+ for (const span of spans) {
6058
+ const parentId = span.parentSpanId ?? void 0;
6059
+ const siblings = childMap.get(parentId) ?? [];
6060
+ siblings.push(span);
6061
+ childMap.set(parentId, siblings);
6062
+ }
6063
+ function buildNode(span, depth, index2) {
6064
+ const children = (childMap.get(span.spanId) ?? []).sort((a, b) => a.startTime - b.startTime).map((child, i) => buildNode(child, depth + 1, i));
6065
+ return { span, children, depth, index: index2 };
6066
+ }
6067
+ const roots = spans.filter((s) => !s.parentSpanId || !spanMap.has(s.parentSpanId));
6068
+ return roots.sort((a, b) => a.startTime - b.startTime).map((root, i) => buildNode(root, 0, i));
6069
+ }
6070
+ function calculateHierarchicalLayout(trees, options) {
6071
+ const positions = /* @__PURE__ */ new Map();
6072
+ const { nodeWidth, nodeHeight, horizontalSpacing, verticalSpacing } = options;
6073
+ const depthWidths = /* @__PURE__ */ new Map();
6074
+ function layoutNode(node, offsetX2) {
6075
+ const { span, children, depth } = node;
6076
+ const currentX = depthWidths.get(depth) ?? offsetX2;
6077
+ const y = depth * (nodeHeight + verticalSpacing);
6078
+ if (children.length === 0) {
6079
+ positions.set(span.spanId, { x: currentX, y });
6080
+ depthWidths.set(depth, currentX + nodeWidth + horizontalSpacing);
6081
+ return currentX + nodeWidth / 2;
6082
+ }
6083
+ const childCenters = [];
6084
+ for (const child of children) {
6085
+ const center = layoutNode(child, currentX);
6086
+ childCenters.push(center);
6087
+ }
6088
+ const minCenter = Math.min(...childCenters);
6089
+ const maxCenter = Math.max(...childCenters);
6090
+ const parentCenter = (minCenter + maxCenter) / 2;
6091
+ const parentX = parentCenter - nodeWidth / 2;
6092
+ positions.set(span.spanId, { x: Math.max(currentX, parentX), y });
6093
+ const newWidth = Math.max(depthWidths.get(depth) ?? 0, parentX + nodeWidth + horizontalSpacing);
6094
+ depthWidths.set(depth, newWidth);
6095
+ return parentCenter;
6096
+ }
6097
+ let offsetX = 0;
6098
+ for (const tree of trees) {
6099
+ layoutNode(tree, offsetX);
6100
+ offsetX = Math.max(...Array.from(depthWidths.values())) + horizontalSpacing;
6101
+ }
6102
+ return positions;
6103
+ }
6104
+ function spanToNode(span, position, options) {
6105
+ var _a;
6106
+ const { nodeWidth, nodeHeight, kindColors, includeAttributes } = options;
6107
+ const statusCode = ((_a = span.status) == null ? void 0 : _a.code) ?? "UNSET";
6108
+ return {
6109
+ id: span.spanId,
6110
+ type: "text",
6111
+ x: position.x,
6112
+ y: position.y,
6113
+ width: nodeWidth,
6114
+ height: nodeHeight,
6115
+ text: span.name,
6116
+ color: statusCode === "ERROR" ? 1 : void 0,
6117
+ // Red for errors
6118
+ pv: {
6119
+ nodeType: "span",
6120
+ name: span.name,
6121
+ description: formatDuration(span.duration),
6122
+ shape: kindToShape(span.kind),
6123
+ fill: statusCode === "ERROR" ? "#ef4444" : kindColors[span.kind],
6124
+ states: {
6125
+ [statusCode]: {
6126
+ color: statusCode === "OK" ? "#22c55e" : statusCode === "ERROR" ? "#ef4444" : "#6b7280",
6127
+ label: statusCode
6128
+ }
6129
+ },
6130
+ otel: {
6131
+ kind: "instance",
6132
+ category: "span"
6133
+ },
6134
+ ...includeAttributes && span.attributes && Object.keys(span.attributes).length > 0 ? {
6135
+ dataSchema: Object.fromEntries(Object.entries(span.attributes).map(([key, value]) => [
6136
+ key,
6137
+ { type: typeof value, required: false }
6138
+ ]))
6139
+ } : {}
6140
+ }
6141
+ };
6142
+ }
6143
+ function createEdge(parentSpanId, childSpanId, isCrossService) {
6144
+ return {
6145
+ id: `edge-${parentSpanId}-${childSpanId}`,
6146
+ fromNode: parentSpanId,
6147
+ toNode: childSpanId,
6148
+ fromSide: "bottom",
6149
+ toSide: "top",
6150
+ toEnd: "arrow",
6151
+ pv: {
6152
+ edgeType: "span-child",
6153
+ style: isCrossService ? "dashed" : "solid",
6154
+ width: isCrossService ? 2 : 1
6155
+ }
6156
+ };
6157
+ }
6158
+ function createServiceGroup(serviceName, spans, positions, options) {
6159
+ const serviceSpans = spans.filter((s) => getServiceName(s) === serviceName);
6160
+ if (serviceSpans.length === 0)
6161
+ return null;
6162
+ const servicePositions = serviceSpans.map((s) => positions.get(s.spanId)).filter((p) => p !== void 0);
6163
+ if (servicePositions.length === 0)
6164
+ return null;
6165
+ const minX = Math.min(...servicePositions.map((p) => p.x));
6166
+ const maxX = Math.max(...servicePositions.map((p) => p.x));
6167
+ const minY = Math.min(...servicePositions.map((p) => p.y));
6168
+ const maxY = Math.max(...servicePositions.map((p) => p.y));
6169
+ const padding = 20;
6170
+ return {
6171
+ id: `service-${serviceName}`,
6172
+ type: "group",
6173
+ x: minX - padding,
6174
+ y: minY - padding - 30,
6175
+ // Extra space for label
6176
+ width: maxX - minX + options.nodeWidth + padding * 2,
6177
+ height: maxY - minY + options.nodeHeight + padding * 2 + 30,
6178
+ label: serviceName,
6179
+ pv: {
6180
+ nodeType: "service",
6181
+ name: serviceName,
6182
+ icon: "server"
6183
+ }
6184
+ };
6185
+ }
6186
+ function traceToCanvas(traceExport, options = {}) {
6187
+ const opts = { ...DEFAULT_OPTIONS$4, ...options };
6188
+ const spans = traceExport.spans.filter((s) => s.duration >= opts.minDurationMs);
6189
+ if (spans.length === 0) {
6190
+ return {
6191
+ canvas: {
6192
+ nodes: [],
6193
+ edges: [],
6194
+ pv: {
6195
+ version: "1.0.0",
6196
+ name: "Empty Trace"
6197
+ }
6198
+ },
6199
+ stats: {
6200
+ traceCount: 0,
6201
+ spanCount: 0,
6202
+ serviceCount: 0,
6203
+ maxDepth: 0
6204
+ }
6205
+ };
6206
+ }
6207
+ const traceGroups = groupByTraceId(spans);
6208
+ const allTrees = [];
6209
+ for (const traceSpans of traceGroups.values()) {
6210
+ allTrees.push(...buildSpanTree(traceSpans));
6211
+ }
6212
+ const positions = calculateHierarchicalLayout(allTrees, opts);
6213
+ const spanNodes = spans.map((span) => {
6214
+ const position = positions.get(span.spanId) ?? { x: 0, y: 0 };
6215
+ return spanToNode(span, position, opts);
6216
+ });
6217
+ const spanMap = new Map(spans.map((s) => [s.spanId, s]));
6218
+ const edges = spans.filter((span) => span.parentSpanId && spanMap.has(span.parentSpanId)).map((span) => {
6219
+ const parent = spanMap.get(span.parentSpanId);
6220
+ const isCrossService = getServiceName(span) !== getServiceName(parent);
6221
+ return createEdge(span.parentSpanId, span.spanId, isCrossService);
6222
+ });
6223
+ const serviceGroups = [];
6224
+ if (opts.groupByService) {
6225
+ const services = new Set(spans.map(getServiceName));
6226
+ for (const service of services) {
6227
+ const group = createServiceGroup(service, spans, positions, opts);
6228
+ if (group)
6229
+ serviceGroups.push(group);
6230
+ }
6231
+ }
6232
+ const maxDepth = Math.max(...allTrees.flatMap(function getDepths(tree) {
6233
+ return [tree.depth, ...tree.children.flatMap(getDepths)];
6234
+ }));
6235
+ const canvas = {
6236
+ nodes: [...serviceGroups, ...spanNodes],
6237
+ edges,
6238
+ pv: {
6239
+ version: "1.0.0",
6240
+ name: `Trace: ${traceExport.serviceName}`,
6241
+ description: `Exported at ${traceExport.exportedAt}`,
6242
+ nodeTypes: {
6243
+ span: {
6244
+ description: "OpenTelemetry span from trace",
6245
+ shape: "rectangle"
6246
+ },
6247
+ service: {
6248
+ description: "Service grouping",
6249
+ icon: "server",
6250
+ shape: "rectangle"
6251
+ }
6252
+ },
6253
+ edgeTypes: {
6254
+ "span-child": {
6255
+ label: "Child span",
6256
+ directed: true
6257
+ }
6258
+ },
6259
+ display: {
6260
+ layout: "manual",
6261
+ // We've already laid out the nodes
6262
+ animations: {
6263
+ enabled: true,
6264
+ speed: 1
6265
+ }
6266
+ }
6267
+ }
6268
+ };
6269
+ return {
6270
+ canvas,
6271
+ stats: {
6272
+ traceCount: traceGroups.size,
6273
+ spanCount: spans.length,
6274
+ serviceCount: new Set(spans.map(getServiceName)).size,
6275
+ maxDepth
6276
+ }
6277
+ };
6278
+ }
6279
+ function traceToCanvasJson(traceExport, options = {}) {
6280
+ const { canvas } = traceToCanvas(traceExport, options);
6281
+ return JSON.stringify(canvas, null, 2);
6282
+ }
5665
6283
  class SessionManager {
5666
6284
  constructor(config = {}) {
5667
6285
  this.sessions = /* @__PURE__ */ new Map();
@@ -9582,6 +10200,7 @@ function isRuleDisabled(severity) {
9582
10200
  }
9583
10201
  return false;
9584
10202
  }
10203
+ const tracer = trace.getTracer("principal-view-core");
9585
10204
  class GraphRulesEngine {
9586
10205
  constructor() {
9587
10206
  this.rules = /* @__PURE__ */ new Map();
@@ -9631,45 +10250,148 @@ class GraphRulesEngine {
9631
10250
  * Lint a configuration against all enabled rules
9632
10251
  */
9633
10252
  async lint(configuration, options) {
9634
- const violations = [];
9635
- for (const [ruleId, rule] of this.rules) {
9636
- if (!this.shouldRunRule(rule, options)) {
9637
- continue;
10253
+ const span = tracer.startSpan("rules.lint", {
10254
+ attributes: {
10255
+ "lint.config.path": (options == null ? void 0 : options.configPath) ?? "unknown",
10256
+ "lint.rules.total": this.rules.size,
10257
+ "lint.library.provided": !!(options == null ? void 0 : options.library)
9638
10258
  }
9639
- const context = this.buildRuleContext(configuration, rule, options);
9640
- const effectiveSeverity = this.getEffectiveSeverity(rule, options);
9641
- if (effectiveSeverity === "off") {
9642
- continue;
9643
- }
9644
- try {
9645
- const ruleViolations = await rule.check(context);
9646
- for (const violation of ruleViolations) {
9647
- violation.severity = effectiveSeverity;
9648
- violations.push(violation);
10259
+ });
10260
+ try {
10261
+ const violations = [];
10262
+ let rulesExecuted = 0;
10263
+ let rulesSkipped = 0;
10264
+ span.addEvent("lint.started", {
10265
+ "rules.registered": this.rules.size
10266
+ });
10267
+ for (const [ruleId, rule] of this.rules) {
10268
+ if (!this.shouldRunRule(rule, options)) {
10269
+ rulesSkipped++;
10270
+ continue;
9649
10271
  }
9650
- } catch (error) {
9651
- violations.push({
9652
- ruleId,
9653
- severity: "error",
9654
- message: `Rule "${ruleId}" threw an error: ${error instanceof Error ? error.message : String(error)}`,
9655
- impact: "Rule could not complete validation",
9656
- fixable: false
10272
+ const context = this.buildRuleContext(configuration, rule, options);
10273
+ const effectiveSeverity = this.getEffectiveSeverity(rule, options);
10274
+ if (effectiveSeverity === "off") {
10275
+ rulesSkipped++;
10276
+ continue;
10277
+ }
10278
+ const ruleSpan = tracer.startSpan("rule.execute", {
10279
+ attributes: {
10280
+ "rule.id": ruleId,
10281
+ "rule.category": rule.category,
10282
+ "rule.severity": effectiveSeverity,
10283
+ "rule.name": rule.name,
10284
+ "parent.span.id": span.spanContext().spanId
10285
+ }
9657
10286
  });
10287
+ try {
10288
+ const ruleStartTime = Date.now();
10289
+ const ruleViolations = await rule.check(context);
10290
+ const ruleDuration = Date.now() - ruleStartTime;
10291
+ rulesExecuted++;
10292
+ for (const violation of ruleViolations) {
10293
+ violation.severity = effectiveSeverity;
10294
+ violations.push(violation);
10295
+ }
10296
+ ruleSpan.addEvent("rule.completed", {
10297
+ "rule.violations.count": ruleViolations.length,
10298
+ "rule.duration.ms": ruleDuration
10299
+ });
10300
+ if (ruleViolations.length > 0) {
10301
+ span.addEvent("violations.detected", {
10302
+ "rule.id": ruleId,
10303
+ "rule.category": rule.category,
10304
+ "violations.count": ruleViolations.length,
10305
+ "violations.severity": effectiveSeverity
10306
+ });
10307
+ }
10308
+ ruleSpan.setStatus({ code: SpanStatusCode.OK });
10309
+ } catch (error) {
10310
+ const errorMessage = error instanceof Error ? error.message : String(error);
10311
+ violations.push({
10312
+ ruleId,
10313
+ severity: "error",
10314
+ message: `Rule "${ruleId}" threw an error: ${errorMessage}`,
10315
+ impact: "Rule could not complete validation",
10316
+ fixable: false
10317
+ });
10318
+ ruleSpan.addEvent("rule.error", {
10319
+ "error.message": errorMessage,
10320
+ "error.type": error instanceof Error ? error.constructor.name : "unknown"
10321
+ });
10322
+ ruleSpan.setStatus({
10323
+ code: SpanStatusCode.ERROR,
10324
+ message: errorMessage
10325
+ });
10326
+ span.addEvent("rule.execution.error", {
10327
+ "rule.id": ruleId,
10328
+ "error.message": errorMessage
10329
+ });
10330
+ } finally {
10331
+ ruleSpan.end();
10332
+ }
9658
10333
  }
10334
+ const result = this.buildResult(violations);
10335
+ span.addEvent("lint.completed", {
10336
+ "lint.rules.executed": rulesExecuted,
10337
+ "lint.rules.skipped": rulesSkipped,
10338
+ "lint.violations.total": violations.length,
10339
+ "lint.violations.errors": result.errorCount,
10340
+ "lint.violations.warnings": result.warningCount,
10341
+ "lint.violations.fixable": result.fixableCount
10342
+ });
10343
+ span.setStatus({ code: SpanStatusCode.OK });
10344
+ return result;
10345
+ } catch (error) {
10346
+ const errorMessage = error instanceof Error ? error.message : String(error);
10347
+ span.setStatus({
10348
+ code: SpanStatusCode.ERROR,
10349
+ message: errorMessage
10350
+ });
10351
+ span.recordException(error instanceof Error ? error : new Error(errorMessage));
10352
+ throw error;
10353
+ } finally {
10354
+ span.end();
9659
10355
  }
9660
- return this.buildResult(violations);
9661
10356
  }
9662
10357
  /**
9663
10358
  * Lint a configuration with VGC config file settings applied
9664
10359
  */
9665
10360
  async lintWithConfig(configuration, privuConfig, options) {
9666
- const { ruleOptions, severityOverrides, disabledRules } = this.parsePrivuConfigRules(privuConfig.rules);
9667
- return this.lint(configuration, {
9668
- ...options,
9669
- ruleOptions,
9670
- severityOverrides,
9671
- disabledRules: [...(options == null ? void 0 : options.disabledRules) ?? [], ...disabledRules]
10361
+ const span = tracer.startSpan("rules.lintWithConfig", {
10362
+ attributes: {
10363
+ "config.path": (options == null ? void 0 : options.configPath) ?? "unknown",
10364
+ "config.has.rules": !!privuConfig.rules,
10365
+ "config.has.library": !!privuConfig.library
10366
+ }
9672
10367
  });
10368
+ try {
10369
+ span.addEvent("config.parsing");
10370
+ const { ruleOptions, severityOverrides, disabledRules } = this.parsePrivuConfigRules(privuConfig.rules);
10371
+ span.addEvent("config.parsed", {
10372
+ "config.rule.options.count": ruleOptions.size,
10373
+ "config.severity.overrides.count": severityOverrides.size,
10374
+ "config.disabled.rules.count": disabledRules.length
10375
+ });
10376
+ const result = await this.lint(configuration, {
10377
+ ...options,
10378
+ ruleOptions,
10379
+ severityOverrides,
10380
+ disabledRules: [...(options == null ? void 0 : options.disabledRules) ?? [], ...disabledRules]
10381
+ });
10382
+ span.setStatus({ code: SpanStatusCode.OK });
10383
+ return result;
10384
+ } catch (error) {
10385
+ const errorMessage = error instanceof Error ? error.message : String(error);
10386
+ span.setStatus({
10387
+ code: SpanStatusCode.ERROR,
10388
+ message: errorMessage
10389
+ });
10390
+ span.recordException(error instanceof Error ? error : new Error(errorMessage));
10391
+ throw error;
10392
+ } finally {
10393
+ span.end();
10394
+ }
9673
10395
  }
9674
10396
  /**
9675
10397
  * Check if a rule should run based on options
@@ -11465,6 +12187,8 @@ const dist = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty
11465
12187
  DEFAULT_INCLUDE_PATTERNS,
11466
12188
  EventProcessor,
11467
12189
  EventRecorderService,
12190
+ EventValidationError,
12191
+ EventValidator,
11468
12192
  GraphConverter,
11469
12193
  GraphInstrumentationHelper,
11470
12194
  GraphRulesEngine,
@@ -11474,6 +12198,7 @@ const dist = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty
11474
12198
  PathBasedEventProcessor,
11475
12199
  PathMatcher,
11476
12200
  SessionManager,
12201
+ TypeScriptGenerator,
11477
12202
  VALID_RULE_IDS,
11478
12203
  VALID_SEVERITIES,
11479
12204
  ValidationEngine,
@@ -11482,8 +12207,11 @@ const dist = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty
11482
12207
  createDefaultRulesEngine,
11483
12208
  createResourceSignature,
11484
12209
  createRulesEngine,
12210
+ createValidatedEmitter,
11485
12211
  deadEndStates,
11486
12212
  formatConfigErrors,
12213
+ generateTypes,
12214
+ generatorRegistry,
11487
12215
  getConfigNameFromFilename: getConfigNameFromFilename$1,
11488
12216
  getDefaultConfig,
11489
12217
  hasPVExtension,
@@ -11515,6 +12243,8 @@ const dist = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty
11515
12243
  resolveCanvasColor,
11516
12244
  signatureToKey,
11517
12245
  stateTransitionReferences,
12246
+ traceToCanvas,
12247
+ traceToCanvasJson,
11518
12248
  unreachableStates,
11519
12249
  validActionPatterns,
11520
12250
  validColorFormat,
@@ -47123,7 +47853,7 @@ const lucideReact = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineP
47123
47853
  createLucideIcon: createLucideIcon$1,
47124
47854
  icons: index
47125
47855
  }, Symbol.toStringTag, { value: "Module" }));
47126
- const require$$1 = /* @__PURE__ */ getAugmentedNamespace(lucideReact);
47856
+ const require$$3 = /* @__PURE__ */ getAugmentedNamespace(lucideReact);
47127
47857
  var hasRequiredIconResolver;
47128
47858
  function requireIconResolver() {
47129
47859
  if (hasRequiredIconResolver) return iconResolver;
@@ -47169,7 +47899,7 @@ function requireIconResolver() {
47169
47899
  iconResolver.Icon = void 0;
47170
47900
  iconResolver.resolveIcon = resolveIcon;
47171
47901
  const jsx_runtime_1 = require$$0;
47172
- const LucideIcons = __importStar(require$$1);
47902
+ const LucideIcons = __importStar(require$$3);
47173
47903
  function resolveIcon(icon, size = 20, className) {
47174
47904
  if (!icon)
47175
47905
  return null;
@@ -47309,13 +48039,24 @@ function requireCustomNode() {
47309
48039
  const react_2 = /* @__PURE__ */ requireUmd();
47310
48040
  const iconResolver_1 = requireIconResolver();
47311
48041
  const NodeTooltip_1 = requireNodeTooltip();
48042
+ function hexToLightColor(hexColor, lightness = 0.88) {
48043
+ const hex = hexColor.replace("#", "");
48044
+ const r = parseInt(hex.substring(0, 2), 16);
48045
+ const g = parseInt(hex.substring(2, 4), 16);
48046
+ const b = parseInt(hex.substring(4, 6), 16);
48047
+ const newR = Math.round(r + (255 - r) * lightness);
48048
+ const newG = Math.round(g + (255 - g) * lightness);
48049
+ const newB = Math.round(b + (255 - b) * lightness);
48050
+ const toHex = (n) => n.toString(16).padStart(2, "0");
48051
+ return `#${toHex(newR)}${toHex(newG)}${toHex(newB)}`;
48052
+ }
47312
48053
  const CustomNode$1 = ({ data, selected, dragging }) => {
47313
48054
  var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o;
47314
48055
  const [isHovered, setIsHovered] = (0, react_1.useState)(false);
47315
48056
  const showTooltip = isHovered && !dragging;
47316
48057
  const nodeRef = (0, react_1.useRef)(null);
47317
48058
  const nodeProps = data;
47318
- const { typeDefinition, state, hasViolations, data: nodeData, animationType, animationDuration = 1e3, editable = false, tooltipsEnabled = true } = nodeProps;
48059
+ const { typeDefinition, state, hasViolations, data: nodeData, animationType, animationDuration = 1e3, editable = false, tooltipsEnabled = true, isHighlighted = false } = nodeProps;
47319
48060
  const otelInfo = nodeData == null ? void 0 : nodeData.otel;
47320
48061
  const description = nodeData == null ? void 0 : nodeData.description;
47321
48062
  const getOtelBadgeColor = (kind) => {
@@ -47388,7 +48129,7 @@ function requireCustomNode() {
47388
48129
  const getShapeStyles = () => {
47389
48130
  const baseStyles = {
47390
48131
  padding: "12px 16px",
47391
- backgroundColor: isGroup ? "rgba(255, 255, 255, 0.7)" : "white",
48132
+ backgroundColor: isGroup ? "rgba(255, 255, 255, 0.7)" : hexToLightColor(fillColor),
47392
48133
  color: "#000",
47393
48134
  border: `2px solid ${hasViolations ? "#D0021B" : strokeColor}`,
47394
48135
  fontSize: "12px",
@@ -47404,7 +48145,7 @@ function requireCustomNode() {
47404
48145
  alignItems: "center",
47405
48146
  justifyContent: isGroup ? "flex-start" : "center",
47406
48147
  gap: "4px",
47407
- boxShadow: selected ? `0 0 0 2px ${strokeColor}` : "0 2px 4px rgba(0,0,0,0.1)",
48148
+ boxShadow: isHighlighted ? `0 0 0 3px #3b82f6, 0 0 20px rgba(59, 130, 246, 0.5)` : selected ? `0 0 0 2px ${strokeColor}` : "0 2px 4px rgba(0,0,0,0.1)",
47408
48149
  transition: "box-shadow 0.2s ease",
47409
48150
  animationDuration: animationType ? `${animationDuration}ms` : void 0,
47410
48151
  boxSizing: "border-box"
@@ -47481,7 +48222,7 @@ function requireCustomNode() {
47481
48222
  right: hexagonBorderWidth,
47482
48223
  bottom: hexagonBorderWidth,
47483
48224
  clipPath: hexagonClipPath,
47484
- backgroundColor: "white",
48225
+ backgroundColor: hexToLightColor(fillColor),
47485
48226
  color: "#000",
47486
48227
  display: "flex",
47487
48228
  flexDirection: "column",
@@ -47513,7 +48254,7 @@ function requireCustomNode() {
47513
48254
  right: diamondBorderWidth,
47514
48255
  bottom: diamondBorderWidth,
47515
48256
  clipPath: diamondClipPath,
47516
- backgroundColor: "white",
48257
+ backgroundColor: hexToLightColor(fillColor),
47517
48258
  color: "#000",
47518
48259
  display: "flex",
47519
48260
  flexDirection: "column",
@@ -48613,7 +49354,7 @@ function requireGraphRenderer() {
48613
49354
  zIndex: 5
48614
49355
  }, children: [(0, jsx_runtime_1.jsx)("line", { x1: screenX, y1: screenY - size, x2: screenX, y2: screenY + size, stroke: color, strokeWidth, opacity: 0.7 }), (0, jsx_runtime_1.jsx)("line", { x1: screenX - size, y1: screenY, x2: screenX + size, y2: screenY, stroke: color, strokeWidth, opacity: 0.7 }), (0, jsx_runtime_1.jsx)("circle", { cx: screenX, cy: screenY, r: 3, fill: color, opacity: 0.7 })] });
48615
49356
  };
48616
- const GraphRendererInner = ({ configuration, nodes: propNodes, edges: propEdges, violations = [], configName: _configName, showMinimap = true, showControls = true, showBackground = true, backgroundVariant = "dots", backgroundGap, showCenterIndicator = false, showTooltips = true, fitViewDuration = 200, events = [], onEventProcessed, editable = false, onPendingChangesChange, onEditStateChange, editStateRef, onSourceClick }) => {
49357
+ const GraphRendererInner = ({ configuration, nodes: propNodes, edges: propEdges, violations = [], configName: _configName, showMinimap = false, showControls = true, showBackground = true, backgroundVariant = "dots", backgroundGap, showCenterIndicator = false, showTooltips = true, fitViewDuration = 200, highlightedNodeId, events = [], onEventProcessed, editable = false, onPendingChangesChange, onEditStateChange, editStateRef, onSourceClick }) => {
48617
49358
  const { fitView } = (0, react_2.useReactFlow)();
48618
49359
  const { theme } = (0, industry_theme_1.useTheme)();
48619
49360
  const [animationState, setAnimationState] = (0, react_1.useState)({
@@ -48831,7 +49572,7 @@ function requireGraphRenderer() {
48831
49572
  return;
48832
49573
  }
48833
49574
  if (uniqueTypes.length === 1) {
48834
- createEdge(connection.source, connection.target, uniqueTypes[0], connection.sourceHandle ?? void 0, connection.targetHandle ?? void 0);
49575
+ createEdge2(connection.source, connection.target, uniqueTypes[0], connection.sourceHandle ?? void 0, connection.targetHandle ?? void 0);
48835
49576
  } else {
48836
49577
  setPendingConnection({
48837
49578
  from: connection.source,
@@ -48842,7 +49583,7 @@ function requireGraphRenderer() {
48842
49583
  });
48843
49584
  }
48844
49585
  }, [editable, nodes, configuration.allowedConnections]);
48845
- const createEdge = (0, react_1.useCallback)((from, to, type2, sourceHandle, targetHandle) => {
49586
+ const createEdge2 = (0, react_1.useCallback)((from, to, type2, sourceHandle, targetHandle) => {
48846
49587
  const edgeId = `${from}-${to}-${type2}-${Date.now()}`;
48847
49588
  const newEdge = {
48848
49589
  id: edgeId,
@@ -48867,9 +49608,9 @@ function requireGraphRenderer() {
48867
49608
  const handleEdgeTypeSelect = (0, react_1.useCallback)((type2) => {
48868
49609
  if (!pendingConnection)
48869
49610
  return;
48870
- createEdge(pendingConnection.from, pendingConnection.to, type2, pendingConnection.sourceHandle, pendingConnection.targetHandle);
49611
+ createEdge2(pendingConnection.from, pendingConnection.to, type2, pendingConnection.sourceHandle, pendingConnection.targetHandle);
48871
49612
  setPendingConnection(null);
48872
- }, [pendingConnection, createEdge]);
49613
+ }, [pendingConnection, createEdge2]);
48873
49614
  const handleCancelEdgeTypePicker = (0, react_1.useCallback)(() => {
48874
49615
  setPendingConnection(null);
48875
49616
  }, []);
@@ -49064,7 +49805,7 @@ function requireGraphRenderer() {
49064
49805
  }
49065
49806
  }, [events, onEventProcessed]);
49066
49807
  const xyflowNodesBase = (0, react_1.useMemo)(() => {
49067
- const converted = (0, graphConverter_1.convertToXYFlowNodes)(nodes, configuration, violations);
49808
+ const converted = (0, graphConverter_1.convertToXYFlowNodes)(localNodes, configuration, violations);
49068
49809
  return converted.map((node) => {
49069
49810
  const animation = animationState.nodeAnimations[node.id];
49070
49811
  const pendingPosition = editStateRef.current.positionChanges.get(node.id);
@@ -49075,6 +49816,7 @@ function requireGraphRenderer() {
49075
49816
  ...node.data,
49076
49817
  editable,
49077
49818
  tooltipsEnabled: showTooltips,
49819
+ isHighlighted: highlightedNodeId === node.id,
49078
49820
  ...animation ? {
49079
49821
  animationType: animation.type,
49080
49822
  animationDuration: animation.duration
@@ -49082,7 +49824,7 @@ function requireGraphRenderer() {
49082
49824
  }
49083
49825
  };
49084
49826
  });
49085
- }, [nodes, configuration, violations, animationState.nodeAnimations, editable, showTooltips, editStateRef]);
49827
+ }, [localNodes, configuration, violations, animationState.nodeAnimations, editable, showTooltips, highlightedNodeId, editStateRef]);
49086
49828
  const baseNodesKey = (0, react_1.useMemo)(() => {
49087
49829
  return nodes.map((n) => n.id).sort().join(",");
49088
49830
  }, [nodes]);
@@ -49144,8 +49886,9 @@ function requireGraphRenderer() {
49144
49886
  }, [editable, updateEditState]);
49145
49887
  const xyflowEdges = (0, react_1.useMemo)(() => {
49146
49888
  const converted = (0, graphConverter_1.convertToXYFlowEdges)(edges, configuration, violations);
49147
- return converted.map((edge) => {
49889
+ const mappedEdges = converted.map((edge) => {
49148
49890
  const animation = animationState.edgeAnimations[edge.id];
49891
+ const isSelected = selectedEdgeIds.has(edge.id);
49149
49892
  return {
49150
49893
  ...edge,
49151
49894
  data: {
@@ -49156,10 +49899,21 @@ function requireGraphRenderer() {
49156
49899
  animationDuration: animation.duration,
49157
49900
  animationDirection: animation.direction
49158
49901
  } : {}
49159
- }
49902
+ },
49903
+ // Add z-index to help with stacking (selected edges get higher values)
49904
+ zIndex: isSelected ? 1e3 : 0
49160
49905
  };
49161
49906
  });
49162
- }, [edges, configuration, violations, animationState.edgeAnimations, showTooltips]);
49907
+ return mappedEdges.sort((a, b) => {
49908
+ const aSelected = selectedEdgeIds.has(a.id);
49909
+ const bSelected = selectedEdgeIds.has(b.id);
49910
+ if (aSelected && !bSelected)
49911
+ return 1;
49912
+ if (!aSelected && bSelected)
49913
+ return -1;
49914
+ return 0;
49915
+ });
49916
+ }, [edges, configuration, violations, animationState.edgeAnimations, showTooltips, selectedEdgeIds]);
49163
49917
  (0, react_1.useEffect)(() => {
49164
49918
  const timeoutId = setTimeout(() => {
49165
49919
  fitView({
@@ -49389,8 +50143,8 @@ function requireGraphRenderer() {
49389
50143
  }, children: (0, jsx_runtime_1.jsx)("p", { children: "No canvas data provided." }) });
49390
50144
  }
49391
50145
  const { configuration, nodes, edges } = canvasData;
49392
- const { violations, configName, showMinimap, showControls, showBackground, backgroundVariant, backgroundGap, showCenterIndicator, showTooltips, fitViewDuration, events, onEventProcessed, editable, onPendingChangesChange, onSourceClick } = props;
49393
- return (0, jsx_runtime_1.jsx)("div", { className, style: { width, height, position: "relative" }, children: (0, jsx_runtime_1.jsx)(react_2.ReactFlowProvider, { children: (0, jsx_runtime_1.jsx)(GraphRendererInner, { configuration, nodes, edges, violations, configName, showMinimap, showControls, showBackground, backgroundVariant, backgroundGap, showCenterIndicator, showTooltips, fitViewDuration, events, onEventProcessed, editable, onPendingChangesChange, editStateRef, onSourceClick }) }) });
50146
+ const { violations, configName, showMinimap, showControls, showBackground, backgroundVariant, backgroundGap, showCenterIndicator, showTooltips, fitViewDuration, highlightedNodeId, events, onEventProcessed, editable, onPendingChangesChange, onSourceClick } = props;
50147
+ return (0, jsx_runtime_1.jsx)("div", { className, style: { width, height, position: "relative" }, children: (0, jsx_runtime_1.jsx)(react_2.ReactFlowProvider, { children: (0, jsx_runtime_1.jsx)(GraphRendererInner, { configuration, nodes, edges, violations, configName, showMinimap, showControls, showBackground, backgroundVariant, backgroundGap, showCenterIndicator, showTooltips, fitViewDuration, highlightedNodeId, events, onEventProcessed, editable, onPendingChangesChange, editStateRef, onSourceClick }) }) });
49394
50148
  });
49395
50149
  exports$1.GraphRenderer.displayName = "GraphRenderer";
49396
50150
  })(GraphRenderer);
@@ -49481,6 +50235,141 @@ function requireConfigurationSelector() {
49481
50235
  ConfigurationSelector.ConfigurationSelector = ConfigurationSelector$1;
49482
50236
  return ConfigurationSelector;
49483
50237
  }
50238
+ var TestEventPanel = {};
50239
+ var hasRequiredTestEventPanel;
50240
+ function requireTestEventPanel() {
50241
+ if (hasRequiredTestEventPanel) return TestEventPanel;
50242
+ hasRequiredTestEventPanel = 1;
50243
+ Object.defineProperty(TestEventPanel, "__esModule", { value: true });
50244
+ TestEventPanel.TestEventPanel = void 0;
50245
+ const jsx_runtime_1 = require$$0;
50246
+ const react_1 = React2;
50247
+ const industry_theme_1 = requireCjs();
50248
+ const lucide_react_1 = require$$3;
50249
+ const TestEventPanel$1 = ({ spans, currentSpanIndex, currentEventIndex, highlightedPhase }) => {
50250
+ const { theme } = (0, industry_theme_1.useTheme)();
50251
+ const [showHelp, setShowHelp] = (0, react_1.useState)(false);
50252
+ const currentSpan = spans[currentSpanIndex];
50253
+ const eventsUpToNow = (currentSpan == null ? void 0 : currentSpan.events.slice(0, currentEventIndex + 1)) || [];
50254
+ return (0, jsx_runtime_1.jsxs)("div", { style: {
50255
+ width: "100%",
50256
+ height: "100%",
50257
+ backgroundColor: theme.colors.background,
50258
+ color: theme.colors.text,
50259
+ padding: "20px",
50260
+ fontFamily: theme.fonts.monospace,
50261
+ fontSize: "14px",
50262
+ overflow: "auto",
50263
+ boxSizing: "border-box"
50264
+ }, children: [(0, jsx_runtime_1.jsxs)("div", { style: { display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: "15px" }, children: [(0, jsx_runtime_1.jsx)("div", { style: { fontWeight: "bold", fontSize: "18px" }, children: "Wide Event Pattern - Code Journey" }), (0, jsx_runtime_1.jsx)("button", { onClick: () => setShowHelp(true), style: {
50265
+ background: "transparent",
50266
+ border: "none",
50267
+ cursor: "pointer",
50268
+ padding: "4px",
50269
+ display: "flex",
50270
+ alignItems: "center",
50271
+ color: theme.colors.textMuted
50272
+ }, onMouseEnter: (e) => {
50273
+ e.currentTarget.style.color = theme.colors.text;
50274
+ }, onMouseLeave: (e) => {
50275
+ e.currentTarget.style.color = theme.colors.textMuted;
50276
+ }, children: (0, jsx_runtime_1.jsx)(lucide_react_1.HelpCircle, { size: 20 }) })] }), (0, jsx_runtime_1.jsxs)("div", { style: { fontSize: "13px", color: theme.colors.textMuted, marginBottom: "15px" }, children: ["Test: ", (currentSpan == null ? void 0 : currentSpan.name) || "Loading..."] }), showHelp && (0, jsx_runtime_1.jsx)("div", { style: {
50277
+ position: "fixed",
50278
+ top: 0,
50279
+ left: 0,
50280
+ right: 0,
50281
+ bottom: 0,
50282
+ backgroundColor: "rgba(0, 0, 0, 0.7)",
50283
+ display: "flex",
50284
+ alignItems: "center",
50285
+ justifyContent: "center",
50286
+ zIndex: 9999
50287
+ }, onClick: () => setShowHelp(false), children: (0, jsx_runtime_1.jsxs)("div", { style: {
50288
+ backgroundColor: theme.colors.background,
50289
+ color: theme.colors.text,
50290
+ padding: "24px",
50291
+ borderRadius: "8px",
50292
+ maxWidth: "600px",
50293
+ border: `1px solid ${theme.colors.border}`
50294
+ }, onClick: (e) => e.stopPropagation(), children: [(0, jsx_runtime_1.jsx)("div", { style: { fontWeight: "bold", fontSize: "18px", marginBottom: "16px" }, children: "How to Read This Panel" }), (0, jsx_runtime_1.jsxs)("div", { style: { fontSize: "14px", marginBottom: "16px", lineHeight: "1.6" }, children: [(0, jsx_runtime_1.jsx)("p", { style: { marginBottom: "12px" }, children: (0, jsx_runtime_1.jsx)("strong", { children: "Watch how execution flows through files:" }) }), (0, jsx_runtime_1.jsxs)("ul", { style: { marginLeft: "20px", marginBottom: "16px" }, children: [(0, jsx_runtime_1.jsx)("li", { style: { marginBottom: "8px" }, children: (0, jsx_runtime_1.jsx)("span", { style: { color: "#60a5fa" }, children: "Blue = Test file" }) }), (0, jsx_runtime_1.jsx)("li", { children: (0, jsx_runtime_1.jsx)("span", { style: { color: "#4ade80" }, children: "Green → Code under test" }) })] }), (0, jsx_runtime_1.jsx)("p", { style: { marginBottom: "12px" }, children: (0, jsx_runtime_1.jsx)("strong", { children: "Span Context (Static)" }) }), (0, jsx_runtime_1.jsx)("pre", { style: {
50295
+ background: theme.colors.surface,
50296
+ padding: "12px",
50297
+ borderRadius: "4px",
50298
+ fontSize: "13px",
50299
+ overflow: "auto"
50300
+ }, children: `{
50301
+ "test.file": "GraphConverter.test.ts",
50302
+ "test.suite": "GraphConverter",
50303
+ "test.result": "pass"
50304
+ }` })] }), (0, jsx_runtime_1.jsx)("button", { onClick: () => setShowHelp(false), style: {
50305
+ padding: "8px 16px",
50306
+ backgroundColor: theme.colors.primary,
50307
+ color: theme.colors.background,
50308
+ border: "none",
50309
+ borderRadius: "4px",
50310
+ cursor: "pointer",
50311
+ fontSize: "14px",
50312
+ fontWeight: 500
50313
+ }, children: "Got it" })] }) }), currentSpan && (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("div", { style: {
50314
+ color: "#4ade80",
50315
+ fontWeight: "bold",
50316
+ marginBottom: "8px",
50317
+ fontSize: "15px"
50318
+ }, children: "Event Timeline (Context Mutations)" }), eventsUpToNow.map((event, idx) => {
50319
+ const filepath = event.attributes["code.filepath"];
50320
+ const lineno = event.attributes["code.lineno"];
50321
+ const isCodeUnderTest = filepath && filepath !== "GraphConverter.test.ts";
50322
+ const eventPhase = event.name.split(".")[0];
50323
+ const isHighlighted = highlightedPhase === eventPhase;
50324
+ return (0, jsx_runtime_1.jsxs)("div", { style: {
50325
+ marginBottom: "12px",
50326
+ paddingBottom: "12px",
50327
+ borderBottom: idx < eventsUpToNow.length - 1 ? `1px solid ${theme.colors.border}` : "none",
50328
+ opacity: highlightedPhase && !isHighlighted ? 0.4 : 1,
50329
+ transition: "opacity 0.2s ease",
50330
+ transform: isHighlighted ? "scale(1.02)" : "scale(1)",
50331
+ backgroundColor: isHighlighted ? theme.colors.surface : "transparent",
50332
+ padding: isHighlighted ? "8px" : "0",
50333
+ borderRadius: "4px"
50334
+ }, children: [(0, jsx_runtime_1.jsxs)("div", { style: {
50335
+ display: "flex",
50336
+ justifyContent: "space-between",
50337
+ alignItems: "center",
50338
+ marginBottom: "4px",
50339
+ gap: "8px"
50340
+ }, children: [(0, jsx_runtime_1.jsxs)("div", { style: { color: "#f59e0b", fontSize: "13px", flexShrink: 0 }, children: [idx + 1, ". ", event.name] }), filepath && (0, jsx_runtime_1.jsxs)("div", { style: {
50341
+ fontSize: "12px",
50342
+ color: isCodeUnderTest ? "#4ade80" : "#60a5fa",
50343
+ fontFamily: "monospace",
50344
+ background: isCodeUnderTest ? "#064e3b" : "#1e3a8a",
50345
+ padding: "2px 6px",
50346
+ borderRadius: "3px",
50347
+ flexShrink: 1,
50348
+ minWidth: 0,
50349
+ overflow: "hidden",
50350
+ textOverflow: "ellipsis",
50351
+ whiteSpace: "nowrap"
50352
+ }, children: [isCodeUnderTest && "→ ", filepath, ":", lineno] })] }), (0, jsx_runtime_1.jsx)("pre", { style: {
50353
+ background: theme.colors.surface,
50354
+ padding: "8px",
50355
+ borderRadius: "4px",
50356
+ margin: 0,
50357
+ fontSize: "12px",
50358
+ lineHeight: "1.4",
50359
+ overflow: "auto",
50360
+ maxWidth: "100%"
50361
+ }, children: JSON.stringify(Object.fromEntries(Object.entries(event.attributes).filter(([key]) => key !== "code.filepath" && key !== "code.lineno")), null, 2) })] }, idx);
50362
+ })] }) }), (0, jsx_runtime_1.jsxs)("div", { style: {
50363
+ marginTop: "20px",
50364
+ paddingTop: "15px",
50365
+ borderTop: `1px solid ${theme.colors.border}`,
50366
+ fontSize: "13px",
50367
+ color: theme.colors.textMuted
50368
+ }, children: [(0, jsx_runtime_1.jsxs)("div", { style: { marginBottom: "8px" }, children: [(0, jsx_runtime_1.jsx)("strong", { children: "Total tests:" }), " ", spans.length] }), (0, jsx_runtime_1.jsxs)("div", { style: { marginBottom: "8px" }, children: [(0, jsx_runtime_1.jsx)("strong", { children: "Pattern:" }), " One span per test + event timeline"] }), (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("strong", { children: "Status:" }), " ", (0, jsx_runtime_1.jsx)("span", { style: { color: "#4ade80" }, children: "All Passed ✓" })] })] })] });
50369
+ };
50370
+ TestEventPanel.TestEventPanel = TestEventPanel$1;
50371
+ return TestEventPanel;
50372
+ }
49484
50373
  var GenericNode = {};
49485
50374
  var hasRequiredGenericNode;
49486
50375
  function requireGenericNode() {
@@ -49525,7 +50414,7 @@ function requireDist() {
49525
50414
  hasRequiredDist = 1;
49526
50415
  (function(exports$1) {
49527
50416
  Object.defineProperty(exports$1, "__esModule", { value: true });
49528
- exports$1.resolveIcon = exports$1.Icon = exports$1.convertToXYFlowEdges = exports$1.convertToXYFlowNodes = exports$1.NodeTooltip = exports$1.CustomEdge = exports$1.GenericEdge = exports$1.CustomNode = exports$1.GenericNode = exports$1.ConfigurationSelector = exports$1.NodeInfoPanel = exports$1.EdgeInfoPanel = exports$1.MetricsDashboard = exports$1.EventLog = exports$1.GraphRenderer = void 0;
50417
+ exports$1.resolveIcon = exports$1.Icon = exports$1.convertToXYFlowEdges = exports$1.convertToXYFlowNodes = exports$1.NodeTooltip = exports$1.CustomEdge = exports$1.GenericEdge = exports$1.CustomNode = exports$1.GenericNode = exports$1.TestEventPanel = exports$1.ConfigurationSelector = exports$1.NodeInfoPanel = exports$1.EdgeInfoPanel = exports$1.MetricsDashboard = exports$1.EventLog = exports$1.GraphRenderer = void 0;
49529
50418
  var GraphRenderer_1 = requireGraphRenderer();
49530
50419
  Object.defineProperty(exports$1, "GraphRenderer", { enumerable: true, get: function() {
49531
50420
  return GraphRenderer_1.GraphRenderer;
@@ -49550,6 +50439,10 @@ function requireDist() {
49550
50439
  Object.defineProperty(exports$1, "ConfigurationSelector", { enumerable: true, get: function() {
49551
50440
  return ConfigurationSelector_1.ConfigurationSelector;
49552
50441
  } });
50442
+ var TestEventPanel_1 = requireTestEventPanel();
50443
+ Object.defineProperty(exports$1, "TestEventPanel", { enumerable: true, get: function() {
50444
+ return TestEventPanel_1.TestEventPanel;
50445
+ } });
49553
50446
  var GenericNode_1 = requireGenericNode();
49554
50447
  Object.defineProperty(exports$1, "GenericNode", { enumerable: true, get: function() {
49555
50448
  return GenericNode_1.GenericNode;
@@ -51848,12 +52741,12 @@ const TraceViewerPanel = ({
51848
52741
  boxShadow: "0 4px 12px rgba(0, 0, 0, 0.15)",
51849
52742
  zIndex: 100
51850
52743
  },
51851
- children: state.availableTraces.map((trace) => {
51852
- const isSelected = trace.id === state.selectedTraceId;
52744
+ children: state.availableTraces.map((trace2) => {
52745
+ const isSelected = trace2.id === state.selectedTraceId;
51853
52746
  return /* @__PURE__ */ jsxs(
51854
52747
  "button",
51855
52748
  {
51856
- onClick: () => handleTraceSelect(trace.id),
52749
+ onClick: () => handleTraceSelect(trace2.id),
51857
52750
  style: {
51858
52751
  display: "flex",
51859
52752
  flexDirection: "column",
@@ -51878,22 +52771,22 @@ const TraceViewerPanel = ({
51878
52771
  fontSize: theme.fontSizes[1],
51879
52772
  fontWeight: isSelected ? 500 : 400
51880
52773
  },
51881
- children: trace.name
52774
+ children: trace2.name
51882
52775
  }
51883
52776
  ),
51884
- trace.packageName && /* @__PURE__ */ jsx(
52777
+ trace2.packageName && /* @__PURE__ */ jsx(
51885
52778
  "span",
51886
52779
  {
51887
52780
  style: {
51888
52781
  fontSize: theme.fontSizes[0],
51889
52782
  color: theme.colors.textMuted
51890
52783
  },
51891
- children: trace.packageName
52784
+ children: trace2.packageName
51892
52785
  }
51893
52786
  )
51894
52787
  ]
51895
52788
  },
51896
- trace.id
52789
+ trace2.id
51897
52790
  );
51898
52791
  })
51899
52792
  }
@@ -52170,6 +53063,58 @@ const ExecutionStats = ({ metadata }) => {
52170
53063
  }
52171
53064
  );
52172
53065
  };
53066
+ function mapEventToNodeId(event, canvas) {
53067
+ var _a, _b, _c;
53068
+ if (!canvas || !canvas.nodes || canvas.nodes.length === 0) {
53069
+ return null;
53070
+ }
53071
+ for (const node of canvas.nodes) {
53072
+ const pvEvents = (_a = node.pv) == null ? void 0 : _a.events;
53073
+ if (pvEvents && typeof pvEvents === "object") {
53074
+ if (event.name in pvEvents) {
53075
+ return node.id;
53076
+ }
53077
+ }
53078
+ }
53079
+ if (event.attributes) {
53080
+ for (const node of canvas.nodes) {
53081
+ const resourceMatch = (_c = (_b = node.pv) == null ? void 0 : _b.otel) == null ? void 0 : _c.resourceMatch;
53082
+ if (resourceMatch && typeof resourceMatch === "object") {
53083
+ const matches = Object.entries(resourceMatch).every(([key, pattern]) => {
53084
+ var _a2;
53085
+ const value = (_a2 = event.attributes) == null ? void 0 : _a2[key];
53086
+ if (value === void 0) return false;
53087
+ if (typeof pattern === "string" && pattern === "*") {
53088
+ return true;
53089
+ }
53090
+ return value === pattern;
53091
+ });
53092
+ if (matches) {
53093
+ return node.id;
53094
+ }
53095
+ }
53096
+ }
53097
+ }
53098
+ return null;
53099
+ }
53100
+ function buildEventToNodeMap(canvas) {
53101
+ var _a;
53102
+ const map2 = /* @__PURE__ */ new Map();
53103
+ if (!canvas || !canvas.nodes) {
53104
+ return map2;
53105
+ }
53106
+ for (const node of canvas.nodes) {
53107
+ const pvEvents = (_a = node.pv) == null ? void 0 : _a.events;
53108
+ if (pvEvents && typeof pvEvents === "object") {
53109
+ for (const eventName of Object.keys(pvEvents)) {
53110
+ if (!map2.has(eventName)) {
53111
+ map2.set(eventName, node.id);
53112
+ }
53113
+ }
53114
+ }
53115
+ }
53116
+ return map2;
53117
+ }
52173
53118
  const ExecutionViewerPanel = ({
52174
53119
  context,
52175
53120
  actions,
@@ -52188,7 +53133,8 @@ const ExecutionViewerPanel = ({
52188
53133
  showExecutionSelector: false,
52189
53134
  isPlaying: false,
52190
53135
  currentSpanIndex: 0,
52191
- currentEventIndex: 0
53136
+ currentEventIndex: 0,
53137
+ highlightedNodeId: null
52192
53138
  });
52193
53139
  const contextRef = useRef(context);
52194
53140
  const actionsRef = useRef(actions);
@@ -52199,6 +53145,7 @@ const ExecutionViewerPanel = ({
52199
53145
  const selectedExecutionIdRef = useRef(null);
52200
53146
  selectedExecutionIdRef.current = state.selectedExecutionId;
52201
53147
  const playbackTimerRef = useRef(null);
53148
+ const eventNodeMapRef = useRef(/* @__PURE__ */ new Map());
52202
53149
  const loadExecution = useCallback(async (executionId) => {
52203
53150
  setState((prev) => ({ ...prev, loading: prev.canvas === null, error: null }));
52204
53151
  try {
@@ -52283,6 +53230,11 @@ const ExecutionViewerPanel = ({
52283
53230
  console.warn("[ExecutionViewer] Failed to load canvas:", error);
52284
53231
  }
52285
53232
  }
53233
+ if (canvas) {
53234
+ eventNodeMapRef.current = buildEventToNodeMap(canvas);
53235
+ } else {
53236
+ eventNodeMapRef.current = /* @__PURE__ */ new Map();
53237
+ }
52286
53238
  setState((prev) => ({
52287
53239
  ...prev,
52288
53240
  canvas,
@@ -52293,7 +53245,8 @@ const ExecutionViewerPanel = ({
52293
53245
  availableExecutions,
52294
53246
  selectedExecutionId: selectedExecution2.id,
52295
53247
  currentSpanIndex: 0,
52296
- currentEventIndex: 0
53248
+ currentEventIndex: 0,
53249
+ highlightedNodeId: null
52297
53250
  }));
52298
53251
  } catch (error) {
52299
53252
  console.error("[ExecutionViewer] Error loading execution:", error);
@@ -52331,7 +53284,8 @@ const ExecutionViewerPanel = ({
52331
53284
  ...prev,
52332
53285
  isPlaying: false,
52333
53286
  currentSpanIndex: 0,
52334
- currentEventIndex: 0
53287
+ currentEventIndex: 0,
53288
+ highlightedNodeId: null
52335
53289
  }));
52336
53290
  }, []);
52337
53291
  useEffect(() => {
@@ -52349,22 +53303,30 @@ const ExecutionViewerPanel = ({
52349
53303
  }, 0);
52350
53304
  playbackTimerRef.current = setInterval(() => {
52351
53305
  setState((prev) => {
52352
- var _a2;
53306
+ var _a2, _b;
52353
53307
  const spans2 = prev.execution ? ExecutionLoader.getSpans(prev.execution) : [];
52354
53308
  if (spans2.length === 0) return { ...prev, isPlaying: false };
52355
53309
  const currentSpan = spans2[prev.currentSpanIndex];
52356
53310
  const spanEventCount = ((_a2 = currentSpan == null ? void 0 : currentSpan.events) == null ? void 0 : _a2.length) || 0;
53311
+ let newSpanIndex = prev.currentSpanIndex;
53312
+ let newEventIndex = prev.currentEventIndex;
52357
53313
  if (prev.currentEventIndex < spanEventCount - 1) {
52358
- return { ...prev, currentEventIndex: prev.currentEventIndex + 1 };
52359
- }
52360
- if (prev.currentSpanIndex < spans2.length - 1) {
52361
- return {
52362
- ...prev,
52363
- currentSpanIndex: prev.currentSpanIndex + 1,
52364
- currentEventIndex: 0
52365
- };
53314
+ newEventIndex = prev.currentEventIndex + 1;
53315
+ } else if (prev.currentSpanIndex < spans2.length - 1) {
53316
+ newSpanIndex = prev.currentSpanIndex + 1;
53317
+ newEventIndex = 0;
53318
+ } else {
53319
+ return { ...prev, isPlaying: false };
52366
53320
  }
52367
- return { ...prev, isPlaying: false };
53321
+ const newSpan = spans2[newSpanIndex];
53322
+ const newEvent = (_b = newSpan == null ? void 0 : newSpan.events) == null ? void 0 : _b[newEventIndex];
53323
+ const highlightedNodeId = newEvent ? mapEventToNodeId(newEvent, prev.canvas) : null;
53324
+ return {
53325
+ ...prev,
53326
+ currentSpanIndex: newSpanIndex,
53327
+ currentEventIndex: newEventIndex,
53328
+ highlightedNodeId
53329
+ };
52368
53330
  });
52369
53331
  }, 800);
52370
53332
  return () => {
@@ -52657,7 +53619,8 @@ const ExecutionViewerPanel = ({
52657
53619
  showControls: true,
52658
53620
  showBackground: true,
52659
53621
  backgroundVariant: "lines",
52660
- showTooltips: true
53622
+ showTooltips: true,
53623
+ highlightedNodeId: state.highlightedNodeId
52661
53624
  }
52662
53625
  ) }) : /* @__PURE__ */ jsx(
52663
53626
  "div",
@@ -52714,6 +53677,15 @@ const ExecutionViewerPanel = ({
52714
53677
  return /* @__PURE__ */ jsxs(
52715
53678
  "div",
52716
53679
  {
53680
+ onClick: () => {
53681
+ const highlightedNodeId = mapEventToNodeId(event, state.canvas);
53682
+ setState((prev) => ({
53683
+ ...prev,
53684
+ currentSpanIndex: spanIdx,
53685
+ currentEventIndex: eventIdx,
53686
+ highlightedNodeId
53687
+ }));
53688
+ },
52717
53689
  style: {
52718
53690
  padding: "8px",
52719
53691
  marginBottom: "4px",
@@ -52721,7 +53693,8 @@ const ExecutionViewerPanel = ({
52721
53693
  border: `1px solid ${isActive ? "#3b82f6" : "#333"}`,
52722
53694
  borderRadius: "4px",
52723
53695
  fontSize: "12px",
52724
- opacity: isPast ? 0.5 : 1
53696
+ opacity: isPast ? 0.5 : 1,
53697
+ cursor: "pointer"
52725
53698
  },
52726
53699
  children: [
52727
53700
  /* @__PURE__ */ jsx("div", { style: { fontWeight: 600, color: "#fff", marginBottom: "4px" }, children: event.name }),