@industry-theme/principal-view-panels 0.1.46 → 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;
9638
- }
9639
- const context = this.buildRuleContext(configuration, rule, options);
9640
- const effectiveSeverity = this.getEffectiveSeverity(rule, options);
9641
- if (effectiveSeverity === "off") {
9642
- 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)
9643
10258
  }
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
  }
@@ -51917,6 +52810,913 @@ const TraceViewerPanel = ({
51917
52810
  }
51918
52811
  );
51919
52812
  };
52813
+ const EXECUTION_FILE_PATTERNS = [
52814
+ // Packages monorepo pattern: packages/core/__executions__/api-tests.spans.json
52815
+ /^packages\/([^/]+)\/__executions__\/(.+)\.(?:spans|execution|events)\.json$/,
52816
+ // Inside .principal-views: .principal-views/__executions__/graph-converter.spans.json
52817
+ /^\.principal-views\/__executions__\/(.+)\.(?:spans|execution|events)\.json$/,
52818
+ // Direct __executions__ folder: __executions__/test-run.spans.json
52819
+ /^__executions__\/(.+)\.(?:spans|execution|events)\.json$/
52820
+ ];
52821
+ function getExecutionNameFromFilename(filename) {
52822
+ return filename.replace(/\.(?:spans|execution|events)\.json$/, "").split("-").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
52823
+ }
52824
+ function getCanvasBasename(filename) {
52825
+ return filename.replace(/\.(?:spans|execution|events)\.json$/, "");
52826
+ }
52827
+ class ExecutionLoader {
52828
+ /**
52829
+ * Parse JSON execution artifact content
52830
+ */
52831
+ static parseExecutionArtifact(content) {
52832
+ try {
52833
+ const parsed = JSON.parse(content);
52834
+ if (Array.isArray(parsed)) {
52835
+ return { spans: parsed };
52836
+ }
52837
+ return parsed;
52838
+ } catch (error) {
52839
+ throw new Error(`Failed to parse execution artifact JSON: ${error.message}`);
52840
+ }
52841
+ }
52842
+ /**
52843
+ * Get spans array from artifact (handles both formats)
52844
+ */
52845
+ static getSpans(artifact) {
52846
+ if (artifact.spans) {
52847
+ return artifact.spans;
52848
+ }
52849
+ if (Array.isArray(artifact)) {
52850
+ return artifact;
52851
+ }
52852
+ const spans = [];
52853
+ for (const key in artifact) {
52854
+ if (!isNaN(Number(key))) {
52855
+ spans.push(artifact[key]);
52856
+ }
52857
+ }
52858
+ return spans;
52859
+ }
52860
+ /**
52861
+ * Extract metadata from an execution artifact
52862
+ */
52863
+ static getExecutionMetadata(artifact) {
52864
+ const spans = ExecutionLoader.getSpans(artifact);
52865
+ const spanCount = spans.length;
52866
+ const eventCount = spans.reduce((total, span) => {
52867
+ var _a;
52868
+ return total + (((_a = span.events) == null ? void 0 : _a.length) || 0);
52869
+ }, 0);
52870
+ const metadata = artifact.metadata;
52871
+ let status = "success";
52872
+ if (metadata == null ? void 0 : metadata.status) {
52873
+ status = metadata.status;
52874
+ } else if (spans.length > 0) {
52875
+ const hasError = spans.some(
52876
+ (s) => s.status === "ERROR" || s.status === "error" || s.status === "FAILED"
52877
+ );
52878
+ status = hasError ? "error" : "OK";
52879
+ }
52880
+ return {
52881
+ name: (metadata == null ? void 0 : metadata.canvasName) || "Untitled Execution",
52882
+ canvasName: metadata == null ? void 0 : metadata.canvasName,
52883
+ exportedAt: metadata == null ? void 0 : metadata.exportedAt,
52884
+ source: metadata == null ? void 0 : metadata.source,
52885
+ framework: metadata == null ? void 0 : metadata.framework,
52886
+ status,
52887
+ spanCount,
52888
+ eventCount
52889
+ };
52890
+ }
52891
+ /**
52892
+ * Find all execution artifact files in the file tree
52893
+ */
52894
+ static findExecutionFiles(files) {
52895
+ const executionFiles = [];
52896
+ for (const file of files) {
52897
+ const filePath = file.relativePath || file.path || "";
52898
+ const fileName = file.name || filePath.split("/").pop() || "";
52899
+ for (const pattern of EXECUTION_FILE_PATTERNS) {
52900
+ const match = filePath.match(pattern);
52901
+ if (match) {
52902
+ let id;
52903
+ let packageName;
52904
+ let baseName;
52905
+ if (pattern === EXECUTION_FILE_PATTERNS[0]) {
52906
+ packageName = match[1];
52907
+ baseName = match[2];
52908
+ id = `${packageName}-${baseName}`;
52909
+ } else if (pattern === EXECUTION_FILE_PATTERNS[1]) {
52910
+ baseName = match[1];
52911
+ id = `pv-${baseName}`;
52912
+ } else {
52913
+ baseName = match[1];
52914
+ id = baseName;
52915
+ }
52916
+ executionFiles.push({
52917
+ id,
52918
+ name: getExecutionNameFromFilename(fileName),
52919
+ path: filePath,
52920
+ canvasBasename: getCanvasBasename(fileName),
52921
+ packageName
52922
+ });
52923
+ break;
52924
+ }
52925
+ }
52926
+ }
52927
+ return executionFiles.sort((a, b) => {
52928
+ if (a.packageName && b.packageName) {
52929
+ const pkgCompare = a.packageName.localeCompare(b.packageName);
52930
+ if (pkgCompare !== 0) return pkgCompare;
52931
+ } else if (a.packageName) {
52932
+ return -1;
52933
+ } else if (b.packageName) {
52934
+ return 1;
52935
+ }
52936
+ return a.name.localeCompare(b.name);
52937
+ });
52938
+ }
52939
+ /**
52940
+ * Find execution artifact for a given canvas file path
52941
+ */
52942
+ static findExecutionForCanvas(canvasPath, files) {
52943
+ const canvasFilename = canvasPath.split("/").pop() || "";
52944
+ const canvasBasename = canvasFilename.replace(/\.otel\.canvas$/, "");
52945
+ const executions = ExecutionLoader.findExecutionFiles(files);
52946
+ return executions.find((exec) => exec.canvasBasename === canvasBasename) || null;
52947
+ }
52948
+ /**
52949
+ * Find canvas file for a given execution artifact path
52950
+ */
52951
+ static findCanvasForExecution(executionPath, files) {
52952
+ const executionFilename = executionPath.split("/").pop() || "";
52953
+ const canvasBasename = getCanvasBasename(executionFilename);
52954
+ for (const file of files) {
52955
+ const filePath = file.relativePath || file.path || "";
52956
+ const fileName = file.name || filePath.split("/").pop() || "";
52957
+ if (fileName === `${canvasBasename}.otel.canvas` || fileName === `${canvasBasename}.canvas`) {
52958
+ return filePath;
52959
+ }
52960
+ }
52961
+ return null;
52962
+ }
52963
+ }
52964
+ const ExecutionStats = ({ metadata }) => {
52965
+ const formatDate = (isoString) => {
52966
+ if (!isoString) return "Unknown";
52967
+ try {
52968
+ const date = new Date(isoString);
52969
+ return date.toLocaleString();
52970
+ } catch {
52971
+ return isoString;
52972
+ }
52973
+ };
52974
+ const getStatusColor = (status) => {
52975
+ switch (status) {
52976
+ case "success":
52977
+ case "OK":
52978
+ return "#10b981";
52979
+ // green
52980
+ case "error":
52981
+ case "ERROR":
52982
+ case "FAILED":
52983
+ return "#ef4444";
52984
+ // red
52985
+ default:
52986
+ return "#6b7280";
52987
+ }
52988
+ };
52989
+ const getStatusIcon = (status) => {
52990
+ switch (status) {
52991
+ case "success":
52992
+ case "OK":
52993
+ return "✓";
52994
+ case "error":
52995
+ case "ERROR":
52996
+ case "FAILED":
52997
+ return "✗";
52998
+ default:
52999
+ return "○";
53000
+ }
53001
+ };
53002
+ return /* @__PURE__ */ jsxs(
53003
+ "div",
53004
+ {
53005
+ style: {
53006
+ padding: "12px 16px",
53007
+ borderTop: "1px solid #333",
53008
+ background: "#1a1a1a",
53009
+ display: "flex",
53010
+ gap: "24px",
53011
+ fontSize: "12px",
53012
+ color: "#999",
53013
+ fontFamily: "monospace"
53014
+ },
53015
+ children: [
53016
+ metadata.status && /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: "6px" }, children: [
53017
+ /* @__PURE__ */ jsx(
53018
+ "span",
53019
+ {
53020
+ style: {
53021
+ color: getStatusColor(metadata.status),
53022
+ fontSize: "14px",
53023
+ fontWeight: "bold"
53024
+ },
53025
+ children: getStatusIcon(metadata.status)
53026
+ }
53027
+ ),
53028
+ /* @__PURE__ */ jsx("span", { style: { color: "#ccc" }, children: "Status:" }),
53029
+ /* @__PURE__ */ jsx("span", { style: { color: getStatusColor(metadata.status) }, children: metadata.status.toUpperCase() })
53030
+ ] }),
53031
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: "6px" }, children: [
53032
+ /* @__PURE__ */ jsx("span", { style: { color: "#ccc" }, children: "Spans:" }),
53033
+ /* @__PURE__ */ jsx("span", { style: { color: "#3b82f6" }, children: metadata.spanCount })
53034
+ ] }),
53035
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: "6px" }, children: [
53036
+ /* @__PURE__ */ jsx("span", { style: { color: "#ccc" }, children: "Events:" }),
53037
+ /* @__PURE__ */ jsx("span", { style: { color: "#8b5cf6" }, children: metadata.eventCount })
53038
+ ] }),
53039
+ metadata.framework && /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: "6px" }, children: [
53040
+ /* @__PURE__ */ jsx("span", { style: { color: "#ccc" }, children: "Framework:" }),
53041
+ /* @__PURE__ */ jsx("span", { style: { color: "#f59e0b" }, children: metadata.framework })
53042
+ ] }),
53043
+ metadata.source && /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: "6px" }, children: [
53044
+ /* @__PURE__ */ jsx("span", { style: { color: "#ccc" }, children: "Source:" }),
53045
+ /* @__PURE__ */ jsx("span", { style: { color: "#10b981" }, children: metadata.source })
53046
+ ] }),
53047
+ metadata.exportedAt && /* @__PURE__ */ jsxs(
53048
+ "div",
53049
+ {
53050
+ style: {
53051
+ display: "flex",
53052
+ alignItems: "center",
53053
+ gap: "6px",
53054
+ marginLeft: "auto"
53055
+ },
53056
+ children: [
53057
+ /* @__PURE__ */ jsx("span", { style: { color: "#ccc" }, children: "Exported:" }),
53058
+ /* @__PURE__ */ jsx("span", { style: { color: "#6b7280" }, children: formatDate(metadata.exportedAt) })
53059
+ ]
53060
+ }
53061
+ )
53062
+ ]
53063
+ }
53064
+ );
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
+ }
53118
+ const ExecutionViewerPanel = ({
53119
+ context,
53120
+ actions,
53121
+ events
53122
+ }) => {
53123
+ var _a;
53124
+ const { theme } = useTheme();
53125
+ const [state, setState] = useState({
53126
+ canvas: null,
53127
+ execution: null,
53128
+ metadata: null,
53129
+ loading: true,
53130
+ error: null,
53131
+ availableExecutions: [],
53132
+ selectedExecutionId: null,
53133
+ showExecutionSelector: false,
53134
+ isPlaying: false,
53135
+ currentSpanIndex: 0,
53136
+ currentEventIndex: 0,
53137
+ highlightedNodeId: null
53138
+ });
53139
+ const contextRef = useRef(context);
53140
+ const actionsRef = useRef(actions);
53141
+ const eventsRef = useRef(events);
53142
+ contextRef.current = context;
53143
+ actionsRef.current = actions;
53144
+ eventsRef.current = events;
53145
+ const selectedExecutionIdRef = useRef(null);
53146
+ selectedExecutionIdRef.current = state.selectedExecutionId;
53147
+ const playbackTimerRef = useRef(null);
53148
+ const eventNodeMapRef = useRef(/* @__PURE__ */ new Map());
53149
+ const loadExecution = useCallback(async (executionId) => {
53150
+ setState((prev) => ({ ...prev, loading: prev.canvas === null, error: null }));
53151
+ try {
53152
+ const ctx = contextRef.current;
53153
+ const acts = actionsRef.current;
53154
+ if (!ctx.hasSlice("fileTree")) {
53155
+ throw new Error("File tree data not available");
53156
+ }
53157
+ if (ctx.isSliceLoading("fileTree")) {
53158
+ return;
53159
+ }
53160
+ const fileTreeSlice = ctx.getSlice("fileTree");
53161
+ const fileTreeData = fileTreeSlice == null ? void 0 : fileTreeSlice.data;
53162
+ if (!(fileTreeData == null ? void 0 : fileTreeData.allFiles)) {
53163
+ setState((prev) => ({
53164
+ ...prev,
53165
+ canvas: null,
53166
+ execution: null,
53167
+ metadata: null,
53168
+ loading: false,
53169
+ error: null,
53170
+ availableExecutions: [],
53171
+ selectedExecutionId: null
53172
+ }));
53173
+ return;
53174
+ }
53175
+ const availableExecutions = ExecutionLoader.findExecutionFiles(fileTreeData.allFiles);
53176
+ if (availableExecutions.length === 0) {
53177
+ setState((prev) => ({
53178
+ ...prev,
53179
+ canvas: null,
53180
+ execution: null,
53181
+ metadata: null,
53182
+ loading: false,
53183
+ error: null,
53184
+ availableExecutions: [],
53185
+ selectedExecutionId: null
53186
+ }));
53187
+ return;
53188
+ }
53189
+ let selectedExecution2;
53190
+ if (executionId) {
53191
+ const found = availableExecutions.find((e) => e.id === executionId);
53192
+ if (!found) {
53193
+ throw new Error(`Execution with ID '${executionId}' not found`);
53194
+ }
53195
+ selectedExecution2 = found;
53196
+ } else if (selectedExecutionIdRef.current) {
53197
+ const found = availableExecutions.find((e) => e.id === selectedExecutionIdRef.current);
53198
+ selectedExecution2 = found || availableExecutions[0];
53199
+ } else {
53200
+ selectedExecution2 = availableExecutions[0];
53201
+ }
53202
+ const readFile = acts.readFile;
53203
+ if (!readFile) {
53204
+ throw new Error("readFile action not available");
53205
+ }
53206
+ const repositoryPath = ctx.repositoryPath;
53207
+ if (!repositoryPath) {
53208
+ throw new Error("Repository path not available");
53209
+ }
53210
+ const fullExecutionPath = `${repositoryPath}/${selectedExecution2.path}`;
53211
+ const executionContent = await readFile(fullExecutionPath);
53212
+ if (!executionContent || typeof executionContent !== "string") {
53213
+ throw new Error("Failed to read execution file");
53214
+ }
53215
+ const execution = ExecutionLoader.parseExecutionArtifact(executionContent);
53216
+ const metadata = ExecutionLoader.getExecutionMetadata(execution);
53217
+ const canvasPath = ExecutionLoader.findCanvasForExecution(
53218
+ selectedExecution2.path,
53219
+ fileTreeData.allFiles
53220
+ );
53221
+ let canvas = null;
53222
+ if (canvasPath) {
53223
+ try {
53224
+ const fullCanvasPath = `${repositoryPath}/${canvasPath}`;
53225
+ const canvasContent = await readFile(fullCanvasPath);
53226
+ if (canvasContent && typeof canvasContent === "string") {
53227
+ canvas = JSON.parse(canvasContent);
53228
+ }
53229
+ } catch (error) {
53230
+ console.warn("[ExecutionViewer] Failed to load canvas:", error);
53231
+ }
53232
+ }
53233
+ if (canvas) {
53234
+ eventNodeMapRef.current = buildEventToNodeMap(canvas);
53235
+ } else {
53236
+ eventNodeMapRef.current = /* @__PURE__ */ new Map();
53237
+ }
53238
+ setState((prev) => ({
53239
+ ...prev,
53240
+ canvas,
53241
+ execution,
53242
+ metadata,
53243
+ loading: false,
53244
+ error: null,
53245
+ availableExecutions,
53246
+ selectedExecutionId: selectedExecution2.id,
53247
+ currentSpanIndex: 0,
53248
+ currentEventIndex: 0,
53249
+ highlightedNodeId: null
53250
+ }));
53251
+ } catch (error) {
53252
+ console.error("[ExecutionViewer] Error loading execution:", error);
53253
+ setState((prev) => ({
53254
+ ...prev,
53255
+ loading: false,
53256
+ error: error.message
53257
+ }));
53258
+ }
53259
+ }, []);
53260
+ useEffect(() => {
53261
+ loadExecution();
53262
+ }, [loadExecution]);
53263
+ useEffect(() => {
53264
+ if (!events) return;
53265
+ const handleEvent = (event) => {
53266
+ var _a2;
53267
+ if (event.type === "custom" && event.action === "selectExecution") {
53268
+ const executionId = (_a2 = event.payload) == null ? void 0 : _a2.executionId;
53269
+ if (executionId) {
53270
+ loadExecution(executionId);
53271
+ }
53272
+ }
53273
+ };
53274
+ events.on("custom", handleEvent);
53275
+ return () => {
53276
+ events.off("custom", handleEvent);
53277
+ };
53278
+ }, [events, loadExecution]);
53279
+ const handlePlayPause = useCallback(() => {
53280
+ setState((prev) => ({ ...prev, isPlaying: !prev.isPlaying }));
53281
+ }, []);
53282
+ const handleReset = useCallback(() => {
53283
+ setState((prev) => ({
53284
+ ...prev,
53285
+ isPlaying: false,
53286
+ currentSpanIndex: 0,
53287
+ currentEventIndex: 0,
53288
+ highlightedNodeId: null
53289
+ }));
53290
+ }, []);
53291
+ useEffect(() => {
53292
+ if (!state.isPlaying || !state.execution) {
53293
+ if (playbackTimerRef.current) {
53294
+ clearInterval(playbackTimerRef.current);
53295
+ playbackTimerRef.current = null;
53296
+ }
53297
+ return;
53298
+ }
53299
+ const spans = ExecutionLoader.getSpans(state.execution);
53300
+ spans.reduce((sum, span) => {
53301
+ var _a2;
53302
+ return sum + (((_a2 = span.events) == null ? void 0 : _a2.length) || 0);
53303
+ }, 0);
53304
+ playbackTimerRef.current = setInterval(() => {
53305
+ setState((prev) => {
53306
+ var _a2, _b;
53307
+ const spans2 = prev.execution ? ExecutionLoader.getSpans(prev.execution) : [];
53308
+ if (spans2.length === 0) return { ...prev, isPlaying: false };
53309
+ const currentSpan = spans2[prev.currentSpanIndex];
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;
53313
+ if (prev.currentEventIndex < spanEventCount - 1) {
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 };
53320
+ }
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
+ };
53330
+ });
53331
+ }, 800);
53332
+ return () => {
53333
+ if (playbackTimerRef.current) {
53334
+ clearInterval(playbackTimerRef.current);
53335
+ playbackTimerRef.current = null;
53336
+ }
53337
+ };
53338
+ }, [state.isPlaying, state.execution]);
53339
+ if (!state.loading && state.availableExecutions.length === 0) {
53340
+ return /* @__PURE__ */ jsx(
53341
+ "div",
53342
+ {
53343
+ style: {
53344
+ display: "flex",
53345
+ alignItems: "center",
53346
+ justifyContent: "center",
53347
+ height: "100%",
53348
+ background: theme.colors.background,
53349
+ color: theme.colors.text
53350
+ },
53351
+ children: /* @__PURE__ */ jsxs("div", { style: { textAlign: "center", maxWidth: "600px", padding: "20px" }, children: [
53352
+ /* @__PURE__ */ jsx(Activity, { size: 48, style: { margin: "0 auto 20px", opacity: 0.3 } }),
53353
+ /* @__PURE__ */ jsx("h2", { style: { margin: "0 0 10px 0", fontSize: "18px", fontWeight: 600 }, children: "No Execution Artifacts Found" }),
53354
+ /* @__PURE__ */ jsxs("p", { style: { margin: "0 0 20px 0", color: theme.colors.textSecondary, lineHeight: 1.5 }, children: [
53355
+ "Execution artifacts should be saved to ",
53356
+ /* @__PURE__ */ jsx("code", { children: "__executions__/*.spans.json" }),
53357
+ " or",
53358
+ " ",
53359
+ /* @__PURE__ */ jsx("code", { children: "packages/*/__executions__/*.spans.json" })
53360
+ ] }),
53361
+ /* @__PURE__ */ jsxs(
53362
+ "div",
53363
+ {
53364
+ style: {
53365
+ background: "#1e1e1e",
53366
+ padding: "12px",
53367
+ borderRadius: "4px",
53368
+ fontFamily: "monospace",
53369
+ fontSize: "12px",
53370
+ textAlign: "left",
53371
+ color: "#d4d4d4"
53372
+ },
53373
+ children: [
53374
+ /* @__PURE__ */ jsx("div", { children: "# Export execution data from tests" }),
53375
+ /* @__PURE__ */ jsxs("div", { children: [
53376
+ "exportExecutionArtifact(canvas, spans, ",
53377
+ "{"
53378
+ ] }),
53379
+ /* @__PURE__ */ jsx("div", { children: "  outputPath: '__executions__/my-test.spans.json'" }),
53380
+ /* @__PURE__ */ jsxs("div", { children: [
53381
+ "}",
53382
+ ");"
53383
+ ] })
53384
+ ]
53385
+ }
53386
+ )
53387
+ ] })
53388
+ }
53389
+ );
53390
+ }
53391
+ if (state.error) {
53392
+ return /* @__PURE__ */ jsx(
53393
+ "div",
53394
+ {
53395
+ style: {
53396
+ display: "flex",
53397
+ alignItems: "center",
53398
+ justifyContent: "center",
53399
+ height: "100%",
53400
+ background: theme.colors.background,
53401
+ color: theme.colors.error
53402
+ },
53403
+ children: /* @__PURE__ */ jsxs("div", { style: { textAlign: "center", padding: "20px" }, children: [
53404
+ /* @__PURE__ */ jsx("h2", { style: { margin: "0 0 10px 0", fontSize: "18px" }, children: "Error Loading Execution" }),
53405
+ /* @__PURE__ */ jsx("p", { style: { margin: 0, fontSize: "14px" }, children: state.error })
53406
+ ] })
53407
+ }
53408
+ );
53409
+ }
53410
+ if (state.loading) {
53411
+ return /* @__PURE__ */ jsx(
53412
+ "div",
53413
+ {
53414
+ style: {
53415
+ display: "flex",
53416
+ alignItems: "center",
53417
+ justifyContent: "center",
53418
+ height: "100%",
53419
+ background: theme.colors.background,
53420
+ color: theme.colors.text
53421
+ },
53422
+ children: /* @__PURE__ */ jsx(Loader, { className: "animate-spin", size: 32 })
53423
+ }
53424
+ );
53425
+ }
53426
+ const selectedExecution = state.availableExecutions.find(
53427
+ (e) => e.id === state.selectedExecutionId
53428
+ );
53429
+ return /* @__PURE__ */ jsxs(
53430
+ "div",
53431
+ {
53432
+ style: {
53433
+ display: "flex",
53434
+ flexDirection: "column",
53435
+ height: "100%",
53436
+ background: theme.colors.background,
53437
+ color: theme.colors.text
53438
+ },
53439
+ children: [
53440
+ /* @__PURE__ */ jsxs(
53441
+ "div",
53442
+ {
53443
+ style: {
53444
+ display: "flex",
53445
+ alignItems: "center",
53446
+ padding: "12px 16px",
53447
+ borderBottom: `1px solid ${theme.colors.border}`,
53448
+ background: "#1a1a1a",
53449
+ gap: "12px"
53450
+ },
53451
+ children: [
53452
+ /* @__PURE__ */ jsxs("div", { style: { position: "relative" }, children: [
53453
+ /* @__PURE__ */ jsxs(
53454
+ "button",
53455
+ {
53456
+ onClick: () => setState((prev) => ({ ...prev, showExecutionSelector: !prev.showExecutionSelector })),
53457
+ style: {
53458
+ display: "flex",
53459
+ alignItems: "center",
53460
+ gap: "8px",
53461
+ padding: "6px 12px",
53462
+ background: "#2a2a2a",
53463
+ border: "1px solid #3a3a3a",
53464
+ borderRadius: "4px",
53465
+ color: "#fff",
53466
+ cursor: "pointer",
53467
+ fontSize: "13px"
53468
+ },
53469
+ children: [
53470
+ /* @__PURE__ */ jsx(Activity, { size: 16 }),
53471
+ /* @__PURE__ */ jsx("span", { children: (selectedExecution == null ? void 0 : selectedExecution.name) || "Select Execution" }),
53472
+ (selectedExecution == null ? void 0 : selectedExecution.packageName) && /* @__PURE__ */ jsx(
53473
+ "span",
53474
+ {
53475
+ style: {
53476
+ padding: "2px 6px",
53477
+ background: "#3b82f6",
53478
+ borderRadius: "3px",
53479
+ fontSize: "11px",
53480
+ fontWeight: 600
53481
+ },
53482
+ children: selectedExecution.packageName
53483
+ }
53484
+ ),
53485
+ /* @__PURE__ */ jsx(ChevronDown, { size: 16 })
53486
+ ]
53487
+ }
53488
+ ),
53489
+ state.showExecutionSelector && /* @__PURE__ */ jsxs(Fragment, { children: [
53490
+ /* @__PURE__ */ jsx(
53491
+ "div",
53492
+ {
53493
+ style: {
53494
+ position: "fixed",
53495
+ inset: 0,
53496
+ zIndex: 999
53497
+ },
53498
+ onClick: () => setState((prev) => ({ ...prev, showExecutionSelector: false }))
53499
+ }
53500
+ ),
53501
+ /* @__PURE__ */ jsx(
53502
+ "div",
53503
+ {
53504
+ style: {
53505
+ position: "absolute",
53506
+ top: "100%",
53507
+ left: 0,
53508
+ marginTop: "4px",
53509
+ minWidth: "300px",
53510
+ maxHeight: "400px",
53511
+ overflowY: "auto",
53512
+ background: "#2a2a2a",
53513
+ border: "1px solid #3a3a3a",
53514
+ borderRadius: "4px",
53515
+ boxShadow: "0 4px 12px rgba(0,0,0,0.3)",
53516
+ zIndex: 1e3
53517
+ },
53518
+ children: state.availableExecutions.map((execution) => /* @__PURE__ */ jsxs(
53519
+ "button",
53520
+ {
53521
+ onClick: () => {
53522
+ loadExecution(execution.id);
53523
+ setState((prev) => ({ ...prev, showExecutionSelector: false }));
53524
+ },
53525
+ style: {
53526
+ width: "100%",
53527
+ padding: "10px 16px",
53528
+ background: execution.id === state.selectedExecutionId ? "#3b82f6" : "transparent",
53529
+ border: "none",
53530
+ borderBottom: "1px solid #3a3a3a",
53531
+ color: "#fff",
53532
+ textAlign: "left",
53533
+ cursor: "pointer",
53534
+ fontSize: "13px",
53535
+ display: "flex",
53536
+ alignItems: "center",
53537
+ gap: "8px"
53538
+ },
53539
+ children: [
53540
+ /* @__PURE__ */ jsx(Activity, { size: 14 }),
53541
+ /* @__PURE__ */ jsx("span", { style: { flex: 1 }, children: execution.name }),
53542
+ execution.packageName && /* @__PURE__ */ jsx(
53543
+ "span",
53544
+ {
53545
+ style: {
53546
+ padding: "2px 6px",
53547
+ background: execution.id === state.selectedExecutionId ? "#2563eb" : "#3b82f6",
53548
+ borderRadius: "3px",
53549
+ fontSize: "11px",
53550
+ fontWeight: 600
53551
+ },
53552
+ children: execution.packageName
53553
+ }
53554
+ )
53555
+ ]
53556
+ },
53557
+ execution.id
53558
+ ))
53559
+ }
53560
+ )
53561
+ ] })
53562
+ ] }),
53563
+ /* @__PURE__ */ jsxs("div", { style: { flex: 1 }, children: [
53564
+ /* @__PURE__ */ jsx("div", { style: { fontSize: "14px", fontWeight: 600 }, children: ((_a = state.metadata) == null ? void 0 : _a.name) || "Execution Viewer" }),
53565
+ selectedExecution && /* @__PURE__ */ jsx("div", { style: { fontSize: "11px", color: "#999", marginTop: "2px" }, children: selectedExecution.path })
53566
+ ] }),
53567
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: "8px" }, children: [
53568
+ /* @__PURE__ */ jsx(
53569
+ "button",
53570
+ {
53571
+ onClick: handleReset,
53572
+ style: {
53573
+ padding: "6px 10px",
53574
+ background: "#2a2a2a",
53575
+ border: "1px solid #3a3a3a",
53576
+ borderRadius: "4px",
53577
+ color: "#fff",
53578
+ cursor: "pointer",
53579
+ display: "flex",
53580
+ alignItems: "center",
53581
+ gap: "4px"
53582
+ },
53583
+ title: "Reset",
53584
+ children: /* @__PURE__ */ jsx(RotateCcw, { size: 14 })
53585
+ }
53586
+ ),
53587
+ /* @__PURE__ */ jsxs(
53588
+ "button",
53589
+ {
53590
+ onClick: handlePlayPause,
53591
+ style: {
53592
+ padding: "6px 10px",
53593
+ background: state.isPlaying ? "#ef4444" : "#10b981",
53594
+ border: "none",
53595
+ borderRadius: "4px",
53596
+ color: "#fff",
53597
+ cursor: "pointer",
53598
+ display: "flex",
53599
+ alignItems: "center",
53600
+ gap: "4px",
53601
+ fontWeight: 600
53602
+ },
53603
+ children: [
53604
+ state.isPlaying ? /* @__PURE__ */ jsx(Pause, { size: 14 }) : /* @__PURE__ */ jsx(Play, { size: 14 }),
53605
+ state.isPlaying ? "Pause" : "Play"
53606
+ ]
53607
+ }
53608
+ )
53609
+ ] })
53610
+ ]
53611
+ }
53612
+ ),
53613
+ /* @__PURE__ */ jsxs("div", { style: { flex: 1, display: "flex", overflow: "hidden" }, children: [
53614
+ state.canvas ? /* @__PURE__ */ jsx("div", { style: { flex: "0 0 60%", position: "relative" }, children: /* @__PURE__ */ jsx(
53615
+ distExports.GraphRenderer,
53616
+ {
53617
+ canvas: state.canvas,
53618
+ showMinimap: true,
53619
+ showControls: true,
53620
+ showBackground: true,
53621
+ backgroundVariant: "lines",
53622
+ showTooltips: true,
53623
+ highlightedNodeId: state.highlightedNodeId
53624
+ }
53625
+ ) }) : /* @__PURE__ */ jsx(
53626
+ "div",
53627
+ {
53628
+ style: {
53629
+ flex: "0 0 60%",
53630
+ display: "flex",
53631
+ alignItems: "center",
53632
+ justifyContent: "center",
53633
+ background: "#0a0a0a",
53634
+ color: "#666"
53635
+ },
53636
+ children: /* @__PURE__ */ jsxs("div", { style: { textAlign: "center" }, children: [
53637
+ /* @__PURE__ */ jsx("p", { children: "No matching canvas found" }),
53638
+ /* @__PURE__ */ jsxs("p", { style: { fontSize: "12px", marginTop: "8px" }, children: [
53639
+ "Expected: ",
53640
+ selectedExecution == null ? void 0 : selectedExecution.canvasBasename,
53641
+ ".otel.canvas"
53642
+ ] })
53643
+ ] })
53644
+ }
53645
+ ),
53646
+ /* @__PURE__ */ jsxs("div", { style: { flex: "0 0 40%", borderLeft: "1px solid #333", display: "flex", flexDirection: "column" }, children: [
53647
+ /* @__PURE__ */ jsxs("div", { style: { padding: "16px", borderBottom: "1px solid #333" }, children: [
53648
+ /* @__PURE__ */ jsx("h3", { style: { margin: "0 0 8px 0", fontSize: "14px", fontWeight: 600 }, children: "Execution Events" }),
53649
+ /* @__PURE__ */ jsxs("div", { style: { fontSize: "12px", color: "#999" }, children: [
53650
+ "Span ",
53651
+ state.currentSpanIndex + 1,
53652
+ " of ",
53653
+ ExecutionLoader.getSpans(state.execution || {}).length,
53654
+ " • ",
53655
+ "Event ",
53656
+ state.currentEventIndex + 1
53657
+ ] })
53658
+ ] }),
53659
+ /* @__PURE__ */ jsx("div", { style: { flex: 1, overflowY: "auto", padding: "16px" }, children: ExecutionLoader.getSpans(state.execution || {}).map((span, spanIdx) => {
53660
+ var _a2;
53661
+ return /* @__PURE__ */ jsxs("div", { style: { marginBottom: "24px" }, children: [
53662
+ /* @__PURE__ */ jsx(
53663
+ "div",
53664
+ {
53665
+ style: {
53666
+ fontSize: "13px",
53667
+ fontWeight: 600,
53668
+ marginBottom: "8px",
53669
+ color: spanIdx === state.currentSpanIndex ? "#3b82f6" : "#ccc"
53670
+ },
53671
+ children: span.name
53672
+ }
53673
+ ),
53674
+ (_a2 = span.events) == null ? void 0 : _a2.map((event, eventIdx) => {
53675
+ const isActive = spanIdx === state.currentSpanIndex && eventIdx === state.currentEventIndex;
53676
+ const isPast = spanIdx < state.currentSpanIndex || spanIdx === state.currentSpanIndex && eventIdx < state.currentEventIndex;
53677
+ return /* @__PURE__ */ jsxs(
53678
+ "div",
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
+ },
53689
+ style: {
53690
+ padding: "8px",
53691
+ marginBottom: "4px",
53692
+ background: isActive ? "#3b82f610" : "transparent",
53693
+ border: `1px solid ${isActive ? "#3b82f6" : "#333"}`,
53694
+ borderRadius: "4px",
53695
+ fontSize: "12px",
53696
+ opacity: isPast ? 0.5 : 1,
53697
+ cursor: "pointer"
53698
+ },
53699
+ children: [
53700
+ /* @__PURE__ */ jsx("div", { style: { fontWeight: 600, color: "#fff", marginBottom: "4px" }, children: event.name }),
53701
+ event.attributes && Object.keys(event.attributes).length > 0 && /* @__PURE__ */ jsx("div", { style: { color: "#999", fontFamily: "monospace", fontSize: "11px" }, children: Object.entries(event.attributes).map(([key, value]) => /* @__PURE__ */ jsxs("div", { children: [
53702
+ key,
53703
+ ": ",
53704
+ JSON.stringify(value)
53705
+ ] }, key)) })
53706
+ ]
53707
+ },
53708
+ eventIdx
53709
+ );
53710
+ })
53711
+ ] }, span.id);
53712
+ }) })
53713
+ ] })
53714
+ ] }),
53715
+ state.metadata && /* @__PURE__ */ jsx(ExecutionStats, { metadata: state.metadata })
53716
+ ]
53717
+ }
53718
+ );
53719
+ };
51920
53720
  const focusNodeTool = {
51921
53721
  name: "focus_node",
51922
53722
  description: "Focuses the graph view on a specific node",
@@ -52717,6 +54517,31 @@ const panels = [
52717
54517
  onUnmount: async (_context) => {
52718
54518
  console.log("Trace Viewer Panel unmounting");
52719
54519
  }
54520
+ },
54521
+ {
54522
+ metadata: {
54523
+ id: "principal-ai.execution-viewer",
54524
+ name: "Execution Viewer",
54525
+ icon: "⚡",
54526
+ version: "0.1.0",
54527
+ author: "Principal AI",
54528
+ description: "Visualizes execution artifacts (test runs) overlaid on canvas diagrams with playback controls",
54529
+ slices: ["fileTree"]
54530
+ },
54531
+ component: ExecutionViewerPanel,
54532
+ onMount: async (context) => {
54533
+ var _a;
54534
+ console.log(
54535
+ "Execution Viewer Panel mounted",
54536
+ (_a = context.currentScope.repository) == null ? void 0 : _a.path
54537
+ );
54538
+ if (context.hasSlice("fileTree") && !context.isSliceLoading("fileTree")) {
54539
+ await context.refresh("repository", "fileTree");
54540
+ }
54541
+ },
54542
+ onUnmount: async (_context) => {
54543
+ console.log("Execution Viewer Panel unmounting");
54544
+ }
52720
54545
  }
52721
54546
  ];
52722
54547
  const onPackageLoad = async () => {
@@ -52727,6 +54552,7 @@ const onPackageUnload = async () => {
52727
54552
  };
52728
54553
  export {
52729
54554
  EventControllerPanel,
54555
+ ExecutionViewerPanel,
52730
54556
  PanelFileSystemAdapter,
52731
54557
  TraceViewerPanel,
52732
54558
  focusNodeTool,