loom-spec 0.2.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/exportHtml.d.ts +25 -0
- package/dist/cli/exportHtml.js +201 -0
- package/dist/cli/exportHtml.js.map +1 -0
- package/dist/cli/importTrace.d.ts +15 -0
- package/dist/cli/importTrace.js +188 -0
- package/dist/cli/importTrace.js.map +1 -0
- package/dist/cli/index.js +81 -7
- package/dist/cli/index.js.map +1 -1
- package/dist/server/exportConfig.d.ts +42 -0
- package/dist/server/exportConfig.js +88 -0
- package/dist/server/exportConfig.js.map +1 -0
- package/dist/server/exportFilter.d.ts +59 -0
- package/dist/server/exportFilter.js +172 -0
- package/dist/server/exportFilter.js.map +1 -0
- package/dist/server/otel.d.ts +32 -0
- package/dist/server/otel.js +98 -0
- package/dist/server/otel.js.map +1 -0
- package/dist/view/assets/TimelineView-CsYV15RD.js +16 -0
- package/dist/view/assets/index-CqF2JC9l.js +210 -0
- package/dist/view/assets/index-CvyHnPjR.css +1 -0
- package/dist/view/index.html +2 -2
- package/dist/{view/assets/index-B18EbiQt.css → view-export/assets/bundle.css} +1 -1
- package/dist/view-export/assets/bundle.js +225 -0
- package/dist/view-export/index.html +24 -0
- package/package.json +3 -2
- package/templates/.claude/skills/loom-spec/SKILL.md +111 -0
- package/dist/view/assets/index-DAM9J2qS.js +0 -225
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Optional `.loom/exports.json` config — defines named export bundles so
|
|
3
|
+
* teams can keep export settings versioned in the repo instead of in shell
|
|
4
|
+
* commands. A new contributor runs `loom-spec export-html user-manual`
|
|
5
|
+
* and gets the same output as everyone else without having to remember
|
|
6
|
+
* which flags to pass.
|
|
7
|
+
*
|
|
8
|
+
* Shape:
|
|
9
|
+
*
|
|
10
|
+
* {
|
|
11
|
+
* "exports": {
|
|
12
|
+
* "user-manual": {
|
|
13
|
+
* "include-tags": ["public"],
|
|
14
|
+
* "exclude-tags": ["wip"],
|
|
15
|
+
* "diagram": "overview", // optional, single-diagram mode
|
|
16
|
+
* "no-timelines": true, // default false
|
|
17
|
+
* "out": "docs/architecture.html" // optional, default loom.html
|
|
18
|
+
* },
|
|
19
|
+
* "ops-runbook": {
|
|
20
|
+
* "include-tags": ["ops"]
|
|
21
|
+
* }
|
|
22
|
+
* }
|
|
23
|
+
* }
|
|
24
|
+
*
|
|
25
|
+
* All fields are optional. Unknown keys are tolerated (forward-compat).
|
|
26
|
+
* CLI flags override config values when both are present.
|
|
27
|
+
*/
|
|
28
|
+
import { readFile } from "node:fs/promises";
|
|
29
|
+
import { resolve } from "node:path";
|
|
30
|
+
function asStringArray(v, where) {
|
|
31
|
+
if (v === undefined)
|
|
32
|
+
return undefined;
|
|
33
|
+
if (!Array.isArray(v) || !v.every((s) => typeof s === "string")) {
|
|
34
|
+
throw new Error(`exports.json: ${where} must be an array of strings`);
|
|
35
|
+
}
|
|
36
|
+
return v;
|
|
37
|
+
}
|
|
38
|
+
function asString(v, where) {
|
|
39
|
+
if (v === undefined)
|
|
40
|
+
return undefined;
|
|
41
|
+
if (typeof v !== "string") {
|
|
42
|
+
throw new Error(`exports.json: ${where} must be a string`);
|
|
43
|
+
}
|
|
44
|
+
return v;
|
|
45
|
+
}
|
|
46
|
+
function asBool(v, where) {
|
|
47
|
+
if (v === undefined)
|
|
48
|
+
return undefined;
|
|
49
|
+
if (typeof v !== "boolean") {
|
|
50
|
+
throw new Error(`exports.json: ${where} must be a boolean`);
|
|
51
|
+
}
|
|
52
|
+
return v;
|
|
53
|
+
}
|
|
54
|
+
function normalize(raw) {
|
|
55
|
+
const out = { exports: {} };
|
|
56
|
+
const entries = Object.entries(raw.exports ?? {});
|
|
57
|
+
for (const [name, e] of entries) {
|
|
58
|
+
if (!/^[a-z0-9-]+$/.test(name)) {
|
|
59
|
+
throw new Error(`exports.json: bundle name '${name}' must match ^[a-z0-9-]+$`);
|
|
60
|
+
}
|
|
61
|
+
out.exports[name] = {
|
|
62
|
+
includeTags: asStringArray(e["include-tags"], `${name}.include-tags`),
|
|
63
|
+
excludeTags: asStringArray(e["exclude-tags"], `${name}.exclude-tags`),
|
|
64
|
+
diagram: asString(e.diagram, `${name}.diagram`),
|
|
65
|
+
noTimelines: asBool(e["no-timelines"], `${name}.no-timelines`),
|
|
66
|
+
out: asString(e.out, `${name}.out`),
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
return out;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Try to read `.loom/exports.json`. Returns null when the file is missing
|
|
73
|
+
* (that's a normal, non-error state — exports config is opt-in).
|
|
74
|
+
*/
|
|
75
|
+
export async function loadExportsConfig(loomPath) {
|
|
76
|
+
try {
|
|
77
|
+
const raw = await readFile(resolve(loomPath, "exports.json"), "utf8");
|
|
78
|
+
const parsed = JSON.parse(raw);
|
|
79
|
+
return normalize(parsed);
|
|
80
|
+
}
|
|
81
|
+
catch (e) {
|
|
82
|
+
const code = e.code;
|
|
83
|
+
if (code === "ENOENT")
|
|
84
|
+
return null;
|
|
85
|
+
throw e;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=exportConfig.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exportConfig.js","sourceRoot":"","sources":["../../src/server/exportConfig.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tag-based filter for HTML exports. Used by `loom-spec export-html` to
|
|
3
|
+
* produce scoped bundles (public manual, ops runbook, etc.) from the same
|
|
4
|
+
* `.loom/` source.
|
|
5
|
+
*
|
|
6
|
+
* Semantics:
|
|
7
|
+
*
|
|
8
|
+
* - A node "matches" iff
|
|
9
|
+
* (includeTags is empty OR node has at least one matching include tag)
|
|
10
|
+
* AND
|
|
11
|
+
* (excludeTags is empty OR node has none of the exclude tags)
|
|
12
|
+
*
|
|
13
|
+
* If both lists are empty the filter is a no-op (all nodes match).
|
|
14
|
+
*
|
|
15
|
+
* - Cascade rules (applied in order, so later rules see post-filter state):
|
|
16
|
+
* 1. Drop nodes that don't match.
|
|
17
|
+
* 2. Drop edges whose source or target node was dropped.
|
|
18
|
+
* 3. Drop or shrink groups: if any group's `children` are all dropped,
|
|
19
|
+
* drop the group; otherwise keep with surviving children.
|
|
20
|
+
* 4. Null out `drill_down` chevrons that target diagrams with zero
|
|
21
|
+
* surviving nodes after filtering.
|
|
22
|
+
* 5. Drop timeline events whose referenced node was dropped.
|
|
23
|
+
* 6. Drop timelines that end up with zero events.
|
|
24
|
+
* 7. Null out `triggered_by` refs pointing at dropped events.
|
|
25
|
+
*
|
|
26
|
+
* - Diagrams that end up empty are NOT dropped automatically — the caller
|
|
27
|
+
* decides (often via --diagram for explicit single-diagram exports).
|
|
28
|
+
* Empty diagrams still render an empty canvas, which is visible to the
|
|
29
|
+
* reader and gives them a clue something was filtered out.
|
|
30
|
+
*/
|
|
31
|
+
import type { LoomDiagram } from "../types/diagram.js";
|
|
32
|
+
import type { LoomTimeline } from "../types/timeline.js";
|
|
33
|
+
import type { LoomNodeTypes } from "../types/node-types.js";
|
|
34
|
+
export interface FilterSpec {
|
|
35
|
+
includeTags?: string[];
|
|
36
|
+
excludeTags?: string[];
|
|
37
|
+
}
|
|
38
|
+
export interface LoomExportPayload {
|
|
39
|
+
diagrams: Record<string, LoomDiagram>;
|
|
40
|
+
timelines: Record<string, LoomTimeline>;
|
|
41
|
+
nodeTypes: LoomNodeTypes;
|
|
42
|
+
}
|
|
43
|
+
export interface FilterResult {
|
|
44
|
+
payload: LoomExportPayload;
|
|
45
|
+
/** Summary, used by the CLI to print "Dropped N nodes, M edges, …". */
|
|
46
|
+
summary: {
|
|
47
|
+
nodesDropped: number;
|
|
48
|
+
edgesDropped: number;
|
|
49
|
+
groupsDropped: number;
|
|
50
|
+
eventsDropped: number;
|
|
51
|
+
timelinesDropped: number;
|
|
52
|
+
drillDownsCleared: number;
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Apply the filter cascade. Returns a deep-enough copy that mutating the
|
|
57
|
+
* result doesn't mutate the input.
|
|
58
|
+
*/
|
|
59
|
+
export declare function applyFilter(payload: LoomExportPayload, spec: FilterSpec): FilterResult;
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tag-based filter for HTML exports. Used by `loom-spec export-html` to
|
|
3
|
+
* produce scoped bundles (public manual, ops runbook, etc.) from the same
|
|
4
|
+
* `.loom/` source.
|
|
5
|
+
*
|
|
6
|
+
* Semantics:
|
|
7
|
+
*
|
|
8
|
+
* - A node "matches" iff
|
|
9
|
+
* (includeTags is empty OR node has at least one matching include tag)
|
|
10
|
+
* AND
|
|
11
|
+
* (excludeTags is empty OR node has none of the exclude tags)
|
|
12
|
+
*
|
|
13
|
+
* If both lists are empty the filter is a no-op (all nodes match).
|
|
14
|
+
*
|
|
15
|
+
* - Cascade rules (applied in order, so later rules see post-filter state):
|
|
16
|
+
* 1. Drop nodes that don't match.
|
|
17
|
+
* 2. Drop edges whose source or target node was dropped.
|
|
18
|
+
* 3. Drop or shrink groups: if any group's `children` are all dropped,
|
|
19
|
+
* drop the group; otherwise keep with surviving children.
|
|
20
|
+
* 4. Null out `drill_down` chevrons that target diagrams with zero
|
|
21
|
+
* surviving nodes after filtering.
|
|
22
|
+
* 5. Drop timeline events whose referenced node was dropped.
|
|
23
|
+
* 6. Drop timelines that end up with zero events.
|
|
24
|
+
* 7. Null out `triggered_by` refs pointing at dropped events.
|
|
25
|
+
*
|
|
26
|
+
* - Diagrams that end up empty are NOT dropped automatically — the caller
|
|
27
|
+
* decides (often via --diagram for explicit single-diagram exports).
|
|
28
|
+
* Empty diagrams still render an empty canvas, which is visible to the
|
|
29
|
+
* reader and gives them a clue something was filtered out.
|
|
30
|
+
*/
|
|
31
|
+
function nodeMatches(node, spec) {
|
|
32
|
+
const tags = node.tags ?? [];
|
|
33
|
+
const includes = spec.includeTags ?? [];
|
|
34
|
+
const excludes = spec.excludeTags ?? [];
|
|
35
|
+
if (includes.length > 0) {
|
|
36
|
+
if (!includes.some((t) => tags.includes(t)))
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
if (excludes.length > 0) {
|
|
40
|
+
if (excludes.some((t) => tags.includes(t)))
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Apply the filter cascade. Returns a deep-enough copy that mutating the
|
|
47
|
+
* result doesn't mutate the input.
|
|
48
|
+
*/
|
|
49
|
+
export function applyFilter(payload, spec) {
|
|
50
|
+
const noFilter = (spec.includeTags?.length ?? 0) === 0 &&
|
|
51
|
+
(spec.excludeTags?.length ?? 0) === 0;
|
|
52
|
+
if (noFilter) {
|
|
53
|
+
return {
|
|
54
|
+
payload,
|
|
55
|
+
summary: {
|
|
56
|
+
nodesDropped: 0,
|
|
57
|
+
edgesDropped: 0,
|
|
58
|
+
groupsDropped: 0,
|
|
59
|
+
eventsDropped: 0,
|
|
60
|
+
timelinesDropped: 0,
|
|
61
|
+
drillDownsCleared: 0,
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
let nodesDropped = 0;
|
|
66
|
+
let edgesDropped = 0;
|
|
67
|
+
let groupsDropped = 0;
|
|
68
|
+
let eventsDropped = 0;
|
|
69
|
+
let timelinesDropped = 0;
|
|
70
|
+
let drillDownsCleared = 0;
|
|
71
|
+
// First pass: filter each diagram's nodes / edges / groups.
|
|
72
|
+
// Track surviving node ids per diagram so we can decide which timelines
|
|
73
|
+
// and drill_down references stay valid.
|
|
74
|
+
const filteredDiagrams = {};
|
|
75
|
+
const survivingNodeIdsByDiagram = {};
|
|
76
|
+
for (const [id, d] of Object.entries(payload.diagrams)) {
|
|
77
|
+
const beforeNodes = d.nodes.length;
|
|
78
|
+
const beforeEdges = d.edges.length;
|
|
79
|
+
const beforeGroups = d.groups?.length ?? 0;
|
|
80
|
+
const survivingNodes = d.nodes.filter((n) => nodeMatches(n, spec));
|
|
81
|
+
const survivingIds = new Set(survivingNodes.map((n) => n.id));
|
|
82
|
+
survivingNodeIdsByDiagram[id] = survivingIds;
|
|
83
|
+
const survivingEdges = d.edges.filter((e) => {
|
|
84
|
+
const fromId = e.from.split(":")[0];
|
|
85
|
+
const toId = e.to.split(":")[0];
|
|
86
|
+
return survivingIds.has(fromId) && survivingIds.has(toId);
|
|
87
|
+
});
|
|
88
|
+
const survivingGroups = (d.groups ?? [])
|
|
89
|
+
.map((g) => ({
|
|
90
|
+
...g,
|
|
91
|
+
children: (g.children ?? []).filter((cid) => survivingIds.has(cid)),
|
|
92
|
+
}))
|
|
93
|
+
.filter((g) => g.children.length > 0);
|
|
94
|
+
nodesDropped += beforeNodes - survivingNodes.length;
|
|
95
|
+
edgesDropped += beforeEdges - survivingEdges.length;
|
|
96
|
+
groupsDropped += beforeGroups - survivingGroups.length;
|
|
97
|
+
filteredDiagrams[id] = {
|
|
98
|
+
...d,
|
|
99
|
+
nodes: survivingNodes,
|
|
100
|
+
edges: survivingEdges,
|
|
101
|
+
groups: survivingGroups.length > 0 ? survivingGroups : undefined,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
// Second pass: scrub drill_down refs pointing at fully-empty diagrams.
|
|
105
|
+
for (const d of Object.values(filteredDiagrams)) {
|
|
106
|
+
d.nodes = d.nodes.map((n) => {
|
|
107
|
+
if (!n.drill_down)
|
|
108
|
+
return n;
|
|
109
|
+
const targetSurvivors = survivingNodeIdsByDiagram[n.drill_down];
|
|
110
|
+
// Target diagram missing OR empty after filter → drop chevron.
|
|
111
|
+
if (!targetSurvivors || targetSurvivors.size === 0) {
|
|
112
|
+
drillDownsCleared++;
|
|
113
|
+
const { drill_down: _drop, ...rest } = n;
|
|
114
|
+
return rest;
|
|
115
|
+
}
|
|
116
|
+
return n;
|
|
117
|
+
});
|
|
118
|
+
if (d.groups) {
|
|
119
|
+
d.groups = d.groups.map((g) => {
|
|
120
|
+
if (!g.drill_down)
|
|
121
|
+
return g;
|
|
122
|
+
const targetSurvivors = survivingNodeIdsByDiagram[g.drill_down];
|
|
123
|
+
if (!targetSurvivors || targetSurvivors.size === 0) {
|
|
124
|
+
drillDownsCleared++;
|
|
125
|
+
const { drill_down: _drop, ...rest } = g;
|
|
126
|
+
return rest;
|
|
127
|
+
}
|
|
128
|
+
return g;
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
// Third pass: filter timelines. Drop events on filtered nodes; drop the
|
|
133
|
+
// whole timeline if it ends up empty; scrub dangling triggered_by refs.
|
|
134
|
+
const filteredTimelines = {};
|
|
135
|
+
for (const [id, tl] of Object.entries(payload.timelines)) {
|
|
136
|
+
const survivingIds = survivingNodeIdsByDiagram[tl.diagram];
|
|
137
|
+
if (!survivingIds || survivingIds.size === 0) {
|
|
138
|
+
// Underlying diagram fully filtered → drop timeline.
|
|
139
|
+
timelinesDropped++;
|
|
140
|
+
eventsDropped += tl.events.length;
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
const beforeEvents = tl.events.length;
|
|
144
|
+
const survivingEvents = tl.events.filter((e) => survivingIds.has(e.node));
|
|
145
|
+
eventsDropped += beforeEvents - survivingEvents.length;
|
|
146
|
+
if (survivingEvents.length === 0) {
|
|
147
|
+
timelinesDropped++;
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
const survivingEventIds = new Set(survivingEvents.map((e) => e.id));
|
|
151
|
+
const scrubbed = survivingEvents.map((e) => e.triggered_by && !survivingEventIds.has(e.triggered_by)
|
|
152
|
+
? { ...e, triggered_by: undefined }
|
|
153
|
+
: e);
|
|
154
|
+
filteredTimelines[id] = { ...tl, events: scrubbed };
|
|
155
|
+
}
|
|
156
|
+
return {
|
|
157
|
+
payload: {
|
|
158
|
+
diagrams: filteredDiagrams,
|
|
159
|
+
timelines: filteredTimelines,
|
|
160
|
+
nodeTypes: payload.nodeTypes,
|
|
161
|
+
},
|
|
162
|
+
summary: {
|
|
163
|
+
nodesDropped,
|
|
164
|
+
edgesDropped,
|
|
165
|
+
groupsDropped,
|
|
166
|
+
eventsDropped,
|
|
167
|
+
timelinesDropped,
|
|
168
|
+
drillDownsCleared,
|
|
169
|
+
},
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
//# sourceMappingURL=exportFilter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exportFilter.js","sourceRoot":"","sources":["../../src/server/exportFilter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal OTLP JSON parser. Reads the standard OpenTelemetry resource/scope/
|
|
3
|
+
* spans shape (`resourceSpans[].scopeSpans[].spans[]`) and projects it down
|
|
4
|
+
* to a flat list of normalized spans suitable for translating into a
|
|
5
|
+
* loom-spec timeline.
|
|
6
|
+
*
|
|
7
|
+
* Scope: just enough of the OTLP JSON shape for the import-trace CLI.
|
|
8
|
+
* Does NOT validate exhaustively, does NOT handle protobuf binary, does NOT
|
|
9
|
+
* walk Jaeger or Zipkin formats — those can be follow-ons.
|
|
10
|
+
*/
|
|
11
|
+
export interface ParsedSpan {
|
|
12
|
+
spanId: string;
|
|
13
|
+
parentSpanId: string | null;
|
|
14
|
+
/** Service that emitted the span (from resource attribute). */
|
|
15
|
+
serviceName: string | null;
|
|
16
|
+
/** Span name (often the operation or route). */
|
|
17
|
+
name: string;
|
|
18
|
+
/** "server" | "client" | "internal" | "producer" | "consumer" | "unknown". */
|
|
19
|
+
kind: SpanKind;
|
|
20
|
+
/** Start time in nanoseconds since UNIX epoch. */
|
|
21
|
+
startNs: bigint;
|
|
22
|
+
/** End time in nanoseconds since UNIX epoch. */
|
|
23
|
+
endNs: bigint;
|
|
24
|
+
/** Flattened key→value of attributes (resource + span merged; span wins). */
|
|
25
|
+
attributes: Record<string, string | number | boolean>;
|
|
26
|
+
}
|
|
27
|
+
export type SpanKind = "internal" | "server" | "client" | "producer" | "consumer" | "unknown";
|
|
28
|
+
/**
|
|
29
|
+
* Parse an OTLP JSON object (already JSON.parsed) into normalized spans.
|
|
30
|
+
* Throws if `resourceSpans` is missing — we don't try to recover or guess.
|
|
31
|
+
*/
|
|
32
|
+
export declare function parseOtlpJson(raw: unknown): ParsedSpan[];
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal OTLP JSON parser. Reads the standard OpenTelemetry resource/scope/
|
|
3
|
+
* spans shape (`resourceSpans[].scopeSpans[].spans[]`) and projects it down
|
|
4
|
+
* to a flat list of normalized spans suitable for translating into a
|
|
5
|
+
* loom-spec timeline.
|
|
6
|
+
*
|
|
7
|
+
* Scope: just enough of the OTLP JSON shape for the import-trace CLI.
|
|
8
|
+
* Does NOT validate exhaustively, does NOT handle protobuf binary, does NOT
|
|
9
|
+
* walk Jaeger or Zipkin formats — those can be follow-ons.
|
|
10
|
+
*/
|
|
11
|
+
const KIND_MAP = {
|
|
12
|
+
0: "unknown",
|
|
13
|
+
1: "internal",
|
|
14
|
+
2: "server",
|
|
15
|
+
3: "client",
|
|
16
|
+
4: "producer",
|
|
17
|
+
5: "consumer",
|
|
18
|
+
};
|
|
19
|
+
function attrValue(a) {
|
|
20
|
+
const v = a.value;
|
|
21
|
+
if (!v)
|
|
22
|
+
return undefined;
|
|
23
|
+
if (v.stringValue !== undefined)
|
|
24
|
+
return v.stringValue;
|
|
25
|
+
if (v.boolValue !== undefined)
|
|
26
|
+
return v.boolValue;
|
|
27
|
+
if (v.intValue !== undefined)
|
|
28
|
+
return Number(v.intValue);
|
|
29
|
+
if (v.doubleValue !== undefined)
|
|
30
|
+
return v.doubleValue;
|
|
31
|
+
return undefined;
|
|
32
|
+
}
|
|
33
|
+
function flattenAttrs(attrs) {
|
|
34
|
+
const out = {};
|
|
35
|
+
for (const a of attrs ?? []) {
|
|
36
|
+
const v = attrValue(a);
|
|
37
|
+
if (v !== undefined)
|
|
38
|
+
out[a.key] = v;
|
|
39
|
+
}
|
|
40
|
+
return out;
|
|
41
|
+
}
|
|
42
|
+
function toBigInt(t) {
|
|
43
|
+
if (t === undefined)
|
|
44
|
+
return 0n;
|
|
45
|
+
if (typeof t === "number")
|
|
46
|
+
return BigInt(t);
|
|
47
|
+
return BigInt(t);
|
|
48
|
+
}
|
|
49
|
+
function normalizeKind(k) {
|
|
50
|
+
if (typeof k === "number")
|
|
51
|
+
return KIND_MAP[k] ?? "unknown";
|
|
52
|
+
if (typeof k === "string") {
|
|
53
|
+
// OTLP exporters sometimes emit "SPAN_KIND_SERVER" etc.
|
|
54
|
+
const lower = k.replace(/^SPAN_KIND_/i, "").toLowerCase();
|
|
55
|
+
if (lower in { internal: 1, server: 1, client: 1, producer: 1, consumer: 1 }) {
|
|
56
|
+
return lower;
|
|
57
|
+
}
|
|
58
|
+
return "unknown";
|
|
59
|
+
}
|
|
60
|
+
return "unknown";
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Parse an OTLP JSON object (already JSON.parsed) into normalized spans.
|
|
64
|
+
* Throws if `resourceSpans` is missing — we don't try to recover or guess.
|
|
65
|
+
*/
|
|
66
|
+
export function parseOtlpJson(raw) {
|
|
67
|
+
if (!raw || typeof raw !== "object") {
|
|
68
|
+
throw new Error("Trace file is not a JSON object");
|
|
69
|
+
}
|
|
70
|
+
const root = raw;
|
|
71
|
+
if (!Array.isArray(root.resourceSpans)) {
|
|
72
|
+
throw new Error("Not an OTLP JSON trace — expected top-level 'resourceSpans' array. " +
|
|
73
|
+
"Jaeger/Zipkin formats are not yet supported.");
|
|
74
|
+
}
|
|
75
|
+
const out = [];
|
|
76
|
+
for (const rs of root.resourceSpans) {
|
|
77
|
+
const resourceAttrs = flattenAttrs(rs.resource?.attributes);
|
|
78
|
+
const serviceName = resourceAttrs["service.name"] ?? null;
|
|
79
|
+
for (const ss of rs.scopeSpans ?? []) {
|
|
80
|
+
for (const s of ss.spans ?? []) {
|
|
81
|
+
const spanAttrs = flattenAttrs(s.attributes);
|
|
82
|
+
const attributes = { ...resourceAttrs, ...spanAttrs };
|
|
83
|
+
out.push({
|
|
84
|
+
spanId: s.spanId ?? "",
|
|
85
|
+
parentSpanId: s.parentSpanId && s.parentSpanId.length > 0 ? s.parentSpanId : null,
|
|
86
|
+
serviceName,
|
|
87
|
+
name: s.name ?? "(unnamed)",
|
|
88
|
+
kind: normalizeKind(s.kind),
|
|
89
|
+
startNs: toBigInt(s.startTimeUnixNano),
|
|
90
|
+
endNs: toBigInt(s.endTimeUnixNano),
|
|
91
|
+
attributes,
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return out;
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=otel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"otel.js","sourceRoot":"","sources":["../../src/server/otel.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import{c as te,r as c,j as e,i as Q,s as de,a as se,l as ne,T as ue,P as me,D as fe}from"./index-CqF2JC9l.js";/**
|
|
2
|
+
* @license lucide-react v0.460.0 - ISC
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the ISC license.
|
|
5
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/const he=te("Pause",[["rect",{x:"14",y:"4",width:"4",height:"16",rx:"1",key:"zuxfzm"}],["rect",{x:"6",y:"4",width:"4",height:"16",rx:"1",key:"1okwgv"}]]);/**
|
|
7
|
+
* @license lucide-react v0.460.0 - ISC
|
|
8
|
+
*
|
|
9
|
+
* This source code is licensed under the ISC license.
|
|
10
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
11
|
+
*/const pe=te("Play",[["polygon",{points:"6 3 20 12 6 21 6 3",key:"1oa8hb"}]]);/**
|
|
12
|
+
* @license lucide-react v0.460.0 - ISC
|
|
13
|
+
*
|
|
14
|
+
* This source code is licensed under the ISC license.
|
|
15
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
16
|
+
*/const ve=te("SkipBack",[["polygon",{points:"19 20 9 12 19 4 19 20",key:"o2sva"}],["line",{x1:"5",x2:"5",y1:"19",y2:"5",key:"1ocqjk"}]]),M=110,S=30,R=52,K=24,ae=8,xe=4,G=8,ee=10;function ge(t){const o=Math.max(t/10,1),v=Math.floor(Math.log10(o)),p=Math.pow(10,v),m=o/p;let x;return m<1.5?x=p:m<3.5?x=2*p:m<7.5?x=5*p:x=10*p,Math.max(x,1)}function J(t){return t===0?"0":t<1e3?`${t}ms`:t%1e3===0?`${t/1e3}s`:`${(t/1e3).toFixed(2)}s`}function je(t){const j=[],o=new Set;for(const v of t.tracks??[])o.has(v.id)||(o.add(v.id),j.push(v));for(const v of t.events??[]){const p=v.track??`node:${v.node}`;o.has(p)||(o.add(p),j.push({id:p,label:p}))}return j.length===0&&j.push({id:"default",label:""}),j}function ie(t){return t.track??`node:${t.node}`}function re(t){return Math.round(t/ee)*ee}function ye({timeline:t,diagram:j,nodeTypes:o,selectedEventId:v,onSelectEvent:p,onUpdateEvent:m,playheadMs:x,onScrub:n,zoom:u=1}){const N=c.useRef(null),k=c.useRef(null),[P,z]=c.useState(800),[i,f]=c.useState(null),[d,h]=c.useState(!1);c.useEffect(()=>{const a=N.current;if(!a)return;const l=new ResizeObserver(s=>{const r=s[0];r&&z(r.contentRect.width)});return l.observe(a),z(a.getBoundingClientRect().width),()=>l.disconnect()},[]);const b=c.useMemo(()=>{const a=je(t),s=(t.events??[]).reduce((E,O)=>Math.max(E,O.start_ms+O.duration_ms),0),r=new Map;a.forEach((E,O)=>r.set(E.id,O));const y=Math.max(P-M-K,100)*u,_=s>0?y/s:1,Z=M+y+K,A=ge(s/u),Y=[];for(let E=0;E<=s+A*.001;E+=A)Y.push(E);const q=new Map;j.nodes.forEach(E=>q.set(E.id,E.type));const B=E=>{const O=q.get(E.node),U=O?o.types[O]:void 0;return(U==null?void 0:U.color)??"#71717a"},oe=S+a.length*R+8;return{tracks:a,trackOf:r,totalMs:s,pixelsPerMs:_,svgWidth:Z,tickStep:A,ticks:Y,colorOf:B,svgHeight:oe}},[t,j,o,P,u]),{tracks:T,trackOf:W,ticks:H,pixelsPerMs:C,colorOf:I,svgHeight:D,svgWidth:w,totalMs:$}=b,V=a=>{var s;if(!i||i.eventId!==a.id)return{event:a};const l=i.originalEvent;if(i.mode==="move"){const r=Math.max(0,re(l.start_ms+i.deltaMs)),g=W.get(ie(l))??0,y=Math.min(Math.max(0,g+i.deltaTracks),T.length-1),_=((s=T[y])==null?void 0:s.id)??l.track;return{event:{...l,start_ms:r,track:_}}}else{const r=Math.max(ee,re(l.duration_ms+i.deltaMs));return{event:{...l,duration_ms:r}}}};c.useEffect(()=>{if(!i)return;const a=s=>{const r=s.clientX-i.startX,g=s.clientY-i.startY,y=r/i.pixelsPerMs,_=Math.round(g/R);f({...i,deltaMs:y,deltaTracks:_})},l=()=>{const s=V(i.originalEvent);m&&(s.event.start_ms!==i.originalEvent.start_ms||s.event.duration_ms!==i.originalEvent.duration_ms||s.event.track!==i.originalEvent.track)&&m(i.eventId,()=>s.event),f(null)};return document.addEventListener("pointermove",a),document.addEventListener("pointerup",l),()=>{document.removeEventListener("pointermove",a),document.removeEventListener("pointerup",l)}},[i,m]),c.useEffect(()=>{if(!d||!n)return;const a=k.current;if(!a)return;const l=r=>{const g=a.getBoundingClientRect(),y=r.clientX-g.left-M,_=Math.max(0,Math.min($,y/C));n(_)},s=()=>h(!1);return document.addEventListener("pointermove",l),document.addEventListener("pointerup",s),()=>{document.removeEventListener("pointermove",l),document.removeEventListener("pointerup",s)}},[d,n,$,C]);const X=(a,l,s)=>{m&&(a.stopPropagation(),f({mode:s,eventId:l.id,startX:a.clientX,startY:a.clientY,originalEvent:l,pixelsPerMs:C,deltaMs:0,deltaTracks:0}))},F=a=>x===void 0?!1:x>=a.start_ms&&x<=a.start_ms+a.duration_ms,L=x!==void 0?M+x*C:null;return e.jsx("div",{className:"timeline-wrap",ref:N,children:e.jsxs("svg",{ref:k,className:"timeline-svg",width:w,height:D,onClick:a=>{a.target.tagName==="svg"&&p(null)},children:[T.map((a,l)=>{const s=S+l*R;return e.jsxs("g",{children:[a.color&&e.jsx("rect",{x:M,y:s,width:w-M-K,height:R,fill:a.color,opacity:.4}),e.jsx("line",{x1:0,x2:w,y1:s,y2:s,className:"timeline-lane-sep"}),e.jsx("text",{x:12,y:s+R/2,className:"timeline-track-label",dominantBaseline:"middle",children:a.label})]},`lane-${a.id}`)}),e.jsx("line",{x1:0,x2:w,y1:S+T.length*R,y2:S+T.length*R,className:"timeline-lane-sep"}),e.jsx("rect",{x:M,y:0,width:w-M-K,height:S,fill:"transparent",style:{cursor:n?"ew-resize":"default"},onPointerDown:a=>{var g;if(!n)return;a.preventDefault();const l=(g=k.current)==null?void 0:g.getBoundingClientRect();if(!l)return;const s=a.clientX-l.left-M,r=Math.max(0,Math.min($,s/C));n(r),h(!0)}}),e.jsxs("g",{className:"timeline-axis",children:[e.jsx("line",{x1:M,x2:w-K,y1:S-1,y2:S-1,className:"timeline-axis-line"}),H.map(a=>{const l=M+a*C;return e.jsxs("g",{children:[e.jsx("line",{x1:l,x2:l,y1:S-6,y2:S,className:"timeline-tick-mark"}),e.jsx("line",{x1:l,x2:l,y1:S,y2:D,className:"timeline-tick-grid"}),e.jsx("text",{x:l,y:S-9,className:"timeline-tick-label",textAnchor:"middle",children:J(a)})]},`tick-${a}`)})]}),(t.events??[]).map(a=>{const{event:l}=V(a),s=W.get(ie(l))??0,r=M+l.start_ms*C,g=Math.max(l.duration_ms*C,xe),y=S+s*R+ae,_=R-ae*2,Z=I(l),A=a.id===v,Y=F(l),q=(i==null?void 0:i.eventId)===a.id;return e.jsxs("g",{className:`timeline-clip kind-${l.kind??"compute"}${A?" selected":""}${Y?" active":""}${q?" ghost":""}`,onClick:B=>{B.stopPropagation(),p(a.id)},children:[e.jsx("rect",{x:r,y,width:g,height:_,rx:4,ry:4,fill:Z,fillOpacity:Y?1:.85,stroke:A?"var(--accent)":Z,strokeWidth:A?2:1,style:{cursor:m?"grab":"pointer"},onPointerDown:B=>X(B,a,"move")}),m&&g>G&&e.jsx("rect",{x:r+g-G,y,width:G,height:_,fill:"transparent",style:{cursor:"ew-resize"},onPointerDown:B=>X(B,a,"resize")}),g>40&&l.label&&e.jsx("text",{x:r+6,y:y+_/2,className:"timeline-clip-label",dominantBaseline:"middle",pointerEvents:"none",children:l.label}),e.jsx("title",{children:(l.label??l.node)+` — ${J(l.start_ms)}, ${J(l.duration_ms)} long`})]},a.id)}),L!==null&&e.jsxs("g",{className:"timeline-playhead",pointerEvents:"none",children:[e.jsx("line",{x1:L,x2:L,y1:4,y2:D,className:"timeline-playhead-line"}),e.jsx("polygon",{points:`${L-5},4 ${L+5},4 ${L},14`,className:"timeline-playhead-handle"})]})]})})}function le(t){return t<1e3?`${t} ms`:`${(t/1e3).toFixed(2)} s`}function Ne({selectedEvent:t,diagram:j}){if(!t)return e.jsx("div",{className:"inspector",children:e.jsx("div",{className:"empty",children:"Click a clip to inspect"})});const o=j.nodes.find(m=>m.id===t.node),v=t.code_refs??[],p=t.tags??[];return e.jsxs("div",{className:"inspector",children:[e.jsx("span",{className:"type-tag",style:{background:"#71717a",color:"#fff"},children:"EVENT"}),t.label&&e.jsx("h2",{children:t.label}),e.jsxs("div",{className:"field",children:[e.jsx("div",{className:"field-label",children:"Node"}),e.jsxs("div",{className:"field-value",children:[e.jsx("code",{children:t.node}),o&&e.jsxs("span",{style:{color:"var(--text-muted)"},children:[" · ",o.label]})]})]}),e.jsxs("div",{className:"field",children:[e.jsx("div",{className:"field-label",children:"Timing"}),e.jsxs("div",{className:"field-value",children:["starts ",le(t.start_ms)," ·"," ","lasts ",le(t.duration_ms)]})]}),t.kind&&e.jsxs("div",{className:"field",children:[e.jsx("div",{className:"field-label",children:"Kind"}),e.jsx("div",{className:"field-value",children:t.kind})]}),t.track&&e.jsxs("div",{className:"field",children:[e.jsx("div",{className:"field-label",children:"Track"}),e.jsx("div",{className:"field-value",children:e.jsx("code",{children:t.track})})]}),t.triggered_by&&e.jsxs("div",{className:"field",children:[e.jsx("div",{className:"field-label",children:"Triggered by"}),e.jsx("div",{className:"field-value",children:e.jsx("code",{children:t.triggered_by})})]}),t.description&&e.jsxs("div",{className:"field",children:[e.jsx("div",{className:"field-label",children:"Description"}),e.jsx("div",{className:"field-value",children:t.description})]}),v.length>0&&e.jsxs("div",{className:"field",children:[e.jsx("div",{className:"field-label",children:"Code refs"}),v.map((m,x)=>e.jsxs("div",{className:"code-ref",children:[m.path,m.symbol&&e.jsxs("span",{style:{color:"var(--text-muted)"},children:[" · ",m.symbol]}),m.lines&&e.jsxs("span",{style:{color:"var(--text-muted)"},children:[" · L",m.lines]})]},x))]}),p.length>0&&e.jsxs("div",{className:"field",children:[e.jsx("div",{className:"field-label",children:"Tags"}),e.jsx("div",{children:p.map(m=>e.jsx("span",{className:"tag",children:m},m))})]}),e.jsxs("div",{className:"field",children:[e.jsx("div",{className:"field-label",children:"ID"}),e.jsx("div",{className:"field-value",children:e.jsx("code",{children:t.id})})]})]})}const ke=[.25,.5,1,2,4];function ce(t){return t<1e3?`${Math.round(t)} ms`:`${(t/1e3).toFixed(2)} s`}function be({playing:t,positionMs:j,totalMs:o,speed:v,onPlayPause:p,onReset:m,onSpeed:x,actions:n}){return e.jsxs("div",{className:"transport-bar",children:[e.jsx("button",{className:"transport-btn",onClick:p,title:t?"Pause (space)":"Play (space)","aria-label":t?"Pause":"Play",children:t?e.jsx(he,{size:14}):e.jsx(pe,{size:14})}),e.jsx("button",{className:"transport-btn",onClick:m,title:"Reset to 0 (home)","aria-label":"Reset",children:e.jsx(ve,{size:14})}),e.jsxs("div",{className:"transport-position",children:[e.jsx("code",{children:ce(j)}),e.jsxs("span",{className:"muted",children:[" / ",ce(o)]})]}),n,e.jsx("div",{className:"transport-spacer"}),e.jsxs("label",{className:"transport-speed",children:[e.jsx("span",{className:"muted",children:"Speed"}),e.jsx("select",{value:v,onChange:u=>x(Number(u.target.value)),children:ke.map(u=>e.jsxs("option",{value:u,children:[u,"×"]},u))})]})]})}function we({diagram:t,nodeTypes:j,anchorRef:o,onPick:v,onClose:p}){const m=c.useRef(null),[x,n]=c.useState(null);return c.useLayoutEffect(()=>{function u(){const N=o.current;if(!N)return;const k=N.getBoundingClientRect();n({top:k.bottom+6,right:Math.max(8,window.innerWidth-k.right)})}return u(),window.addEventListener("resize",u),()=>window.removeEventListener("resize",u)},[o]),c.useEffect(()=>{function u(N){var k;if(m.current&&!m.current.contains(N.target)){if((k=o.current)!=null&&k.contains(N.target))return;p()}}return document.addEventListener("mousedown",u),()=>document.removeEventListener("mousedown",u)},[p,o]),x?e.jsxs("div",{className:"add-node-menu",ref:m,style:{top:x.top,right:x.right},children:[e.jsx("div",{className:"add-node-menu-title",children:"Add event on…"}),t.nodes.length===0?e.jsx("div",{className:"switcher-empty",children:"No nodes in this diagram"}):t.nodes.map(u=>{const N=j.types[u.type],k=(N==null?void 0:N.color)??"#71717a";return e.jsxs("button",{className:"add-node-item",onClick:()=>v(u.id),style:{"--node-color":k},children:[e.jsx("span",{className:"add-node-color"}),e.jsxs("div",{className:"add-node-text",children:[e.jsx("div",{className:"add-node-label",children:u.label}),e.jsx("code",{className:"add-node-key",children:u.id})]})]},u.id)})]}):null}const Ee=500;function Me(t){const[j,o]=c.useState({timeline:null,diagram:null,nodeTypes:null,loadError:null,saveStatus:"idle",saveError:null,connectionStatus:"connecting"}),v=c.useRef(null),p=c.useRef(null),m=c.useRef(j);m.current=j;const x=c.useRef(!1);c.useEffect(()=>{let i=!1;x.current=!1;async function f(){try{const d=await se(t);if(i)return;const h=await ne(d.diagram);if(i)return;p.current=d,x.current=!0,o(b=>({...b,timeline:d,diagram:h.diagram,nodeTypes:h.nodeTypes,loadError:null}))}catch(d){if(i)return;o(h=>({...h,loadError:d instanceof Error?d.message:String(d)}))}}return f(),()=>{i=!0}},[t]);const n=c.useCallback(()=>{Q()||(v.current&&clearTimeout(v.current),o(i=>({...i,saveStatus:"dirty",saveError:null})),v.current=setTimeout(async()=>{const i=p.current;if(i){o(f=>({...f,saveStatus:"saving"}));try{await de(i),o(f=>({...f,saveStatus:"saved"}))}catch(f){o(d=>({...d,saveStatus:"error",saveError:f instanceof Error?f.message:String(f)}))}}},Ee))},[]),u=c.useCallback(i=>{x.current&&(o(f=>{if(!f.timeline)return f;const d=i(f.timeline);return p.current=d,{...f,timeline:d}}),n())},[n]),N=c.useCallback((i,f)=>{u(d=>({...d,events:d.events.map(h=>h.id===i?f(h):h)}))},[u]),k=c.useCallback(i=>{u(f=>({...f,events:[...f.events,i]}))},[u]),P=c.useCallback(i=>{u(f=>({...f,events:f.events.filter(d=>d.id!==i)}))},[u]);return c.useEffect(()=>{if(Q()){o(d=>({...d,connectionStatus:"connected"}));return}o(d=>({...d,connectionStatus:"connecting"}));const i=new EventSource("/api/events");i.onopen=()=>o(d=>({...d,connectionStatus:"connected"})),i.onerror=()=>o(d=>({...d,connectionStatus:"disconnected"}));const f=async()=>{const d=m.current.saveStatus;if(!(d==="dirty"||d==="saving"))try{const h=await se(t),b=await ne(h.diagram);p.current=h,o(T=>({...T,timeline:h,diagram:b.diagram,nodeTypes:b.nodeTypes,saveStatus:"idle",saveError:null}))}catch{}};return i.addEventListener("change",d=>{try{const h=JSON.parse(d.data),b=m.current.timeline;(h.type==="timeline-changed"&&h.id===t||h.type==="diagram-changed"&&b&&h.id===b.diagram||h.type==="node-types-changed")&&f()}catch{}}),()=>{i.close()}},[t]),c.useEffect(()=>()=>{v.current&&clearTimeout(v.current)},[]),c.useMemo(()=>({...j,updateEvent:N,addEvent:k,deleteEvent:P}),[j,N,k,P])}function Se(t){const j=new Set(t.events.map(v=>v.id));let o=t.events.length+1;for(;j.has(`ev${o}`);)o++;return`ev${o}`}function _e({id:t,diagrams:j,timelines:o,isDefault:v,onClickHome:p,onNavigate:m,onCreateDiagram:x}){const n=Me(t),[u,N]=c.useState(null),[k,P]=c.useState(!1),z=c.useRef(null),[i,f]=c.useState(1),d=Q(),[h,b]=c.useState(0),[T,W]=c.useState(!1),[H,C]=c.useState(1),I=c.useRef(null),D=c.useRef(0),w=c.useMemo(()=>n.timeline?n.timeline.events.reduce((s,r)=>Math.max(s,r.start_ms+r.duration_ms),0):0,[n.timeline]),$=c.useMemo(()=>{const s=new Set;if(!n.timeline)return s;for(const r of n.timeline.events)h>=r.start_ms&&h<=r.start_ms+r.duration_ms&&s.add(r.node);return s},[n.timeline,h]),V=c.useMemo(()=>{const s=new Set;if(!n.diagram)return s;for(const r of n.diagram.edges){const g=r.from.indexOf(":"),y=g===-1?r.from:r.from.slice(0,g);$.has(y)&&s.add(r.id)}return s},[n.diagram,$]);c.useEffect(()=>{if(!T){I.current!==null&&(clearInterval(I.current),I.current=null);return}return D.current=performance.now(),I.current=setInterval(()=>{const s=performance.now(),r=s-D.current;D.current=s,b(g=>{const y=g+r*H;return y>=w?(W(!1),w):y})},16),()=>{I.current!==null&&clearInterval(I.current)}},[T,H,w]);const X=c.useCallback(()=>{w!==0&&W(s=>(!s&&h>=w&&b(0),!s))},[h,w]),F=c.useCallback(()=>{b(0),W(!1)},[]),L=c.useCallback(s=>{b(s)},[]),a=c.useCallback(s=>{const r=n.timeline;if(!r)return;const g=Math.max(0,Math.round(h)),y={id:Se(r),node:s,start_ms:g,duration_ms:200,kind:"compute"};n.addEvent(y),N(y.id),P(!1)},[n,h]);if(c.useEffect(()=>{const s=r=>{const g=r.target;g.tagName==="INPUT"||g.tagName==="TEXTAREA"||g.tagName==="SELECT"||(r.key===" "||r.code==="Space"?(r.preventDefault(),X()):r.key==="Home"?(r.preventDefault(),F()):(r.key==="Backspace"||r.key==="Delete")&&u&&!d&&(r.preventDefault(),n.deleteEvent(u),N(null)))};return document.addEventListener("keydown",s),()=>document.removeEventListener("keydown",s)},[X,F,u,n,d]),n.loadError)return e.jsxs("div",{className:"app timeline-app",children:[e.jsx("div",{className:"topbar",children:e.jsx("div",{className:"title",children:"loom-spec"})}),e.jsx("div",{className:"canvas-wrap",style:{padding:24},children:e.jsxs("code",{style:{color:"var(--status-stale)"},children:["Failed to load: ",n.loadError]})}),e.jsx("div",{className:"inspector",children:e.jsx("div",{className:"empty",children:"—"})})]});if(!n.timeline||!n.diagram||!n.nodeTypes)return e.jsxs("div",{className:"app timeline-app",children:[e.jsx("div",{className:"topbar",children:e.jsx("div",{className:"title",children:"loom-spec"})}),e.jsx("div",{className:"canvas-wrap",style:{padding:24,color:"var(--text-muted)"},children:"Loading timeline…"}),e.jsx("div",{className:"inspector",children:e.jsx("div",{className:"empty",children:"—"})})]});const l=u?n.timeline.events.find(s=>s.id===u)??null:null;return e.jsxs("div",{className:"app timeline-app",children:[e.jsx(ue,{viewKind:"timeline",viewId:t,title:n.timeline.title,subtitle:n.timeline.description,diagrams:j,timelines:o,saveStatus:n.saveStatus,saveError:n.saveError,connectionStatus:n.connectionStatus,onClickAdd:()=>{},addMenuOpen:!1,isDefault:v,onClickHome:p,onNavigate:m,onCreateDiagram:x,addButtonRef:null,hideAddButton:!0}),e.jsx(be,{playing:T,positionMs:h,totalMs:w,speed:H,onPlayPause:X,onReset:F,onSpeed:C,actions:e.jsxs(e.Fragment,{children:[!d&&e.jsxs("button",{ref:z,className:"transport-add-event",onClick:()=>P(s=>!s),title:"Add event at playhead",children:[e.jsx(me,{size:14})," Event"]}),e.jsxs("label",{className:"transport-zoom",children:[e.jsx("span",{className:"muted",children:"Zoom"}),e.jsxs("select",{value:i,onChange:s=>f(Number(s.target.value)),children:[e.jsx("option",{value:1,children:"1× (fit)"}),e.jsx("option",{value:2,children:"2×"}),e.jsx("option",{value:5,children:"5×"}),e.jsx("option",{value:10,children:"10×"}),e.jsx("option",{value:20,children:"20×"})]})]})]})}),k&&!d&&e.jsx(we,{diagram:n.diagram,nodeTypes:n.nodeTypes,anchorRef:z,onPick:a,onClose:()=>P(!1)}),e.jsx("div",{className:"canvas-wrap timeline-canvas-wrap",children:e.jsxs("div",{className:"timeline-split",children:[e.jsx("div",{className:"timeline-split-pane timeline-split-left",children:e.jsx(ye,{timeline:n.timeline,diagram:n.diagram,nodeTypes:n.nodeTypes,selectedEventId:u,onSelectEvent:N,onUpdateEvent:d?void 0:n.updateEvent,playheadMs:h,onScrub:L,zoom:i})}),e.jsx("div",{className:"timeline-split-pane timeline-split-right",children:e.jsx(fe,{diagram:n.diagram,nodeTypesConfig:n.nodeTypes,interactive:!1,activeNodeIds:$,pulsingEdgeIds:V})})]})}),e.jsx(Ne,{selectedEvent:l,diagram:n.diagram})]})}export{_e as TimelineView};
|