@pipelex/mthds-ui 0.6.4 → 0.6.5

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.
@@ -5,6 +5,19 @@ import {
5
5
  } from "./chunk-2NMEKWO5.js";
6
6
 
7
7
  // src/graph/types.ts
8
+ var PIPE_TYPE_PRESENCE = {
9
+ PipeLLM: true,
10
+ PipeExtract: true,
11
+ PipeCompose: true,
12
+ PipeImgGen: true,
13
+ PipeSearch: true,
14
+ PipeFunc: true,
15
+ PipeSequence: true,
16
+ PipeParallel: true,
17
+ PipeCondition: true,
18
+ PipeBatch: true
19
+ };
20
+ var KNOWN_PIPE_TYPES = new Set(Object.keys(PIPE_TYPE_PRESENCE));
8
21
  var NODE_TYPE_PIPE_CARD = "pipeCard";
9
22
  var NODE_TYPE_STUFF = "default";
10
23
  var NODE_TYPE_CONTROLLER = "controllerGroup";
@@ -39,6 +52,10 @@ var FOLD_MODE = {
39
52
  /** Renderer decides — reserved for future heuristics; currently behaves like EXPANDED. */
40
53
  AUTO: "auto"
41
54
  };
55
+ var GRAPH_THEME = {
56
+ DARK: "dark",
57
+ LIGHT: "light"
58
+ };
42
59
  var CONTROLLER_PADDING_X = 40;
43
60
  var CONTROLLER_PADDING_TOP = 48;
44
61
  var CONTROLLER_PADDING_BOTTOM = 20;
@@ -60,6 +77,161 @@ function nodeHeight(n) {
60
77
  return ((_b = n.data) == null ? void 0 : _b.isStuff) ? 60 : 70;
61
78
  }
62
79
 
80
+ // src/graph/validateGraphSpec.ts
81
+ var GraphSpecValidationError = class extends Error {
82
+ constructor(path, detail) {
83
+ const location = path === "" ? "<root>" : path;
84
+ super(`GraphSpec validation failed at ${location}: ${detail}`);
85
+ this.name = "GraphSpecValidationError";
86
+ this.path = path;
87
+ }
88
+ };
89
+ var PIPE_STATUSES = /* @__PURE__ */ new Set([
90
+ "succeeded",
91
+ "failed",
92
+ "running",
93
+ "scheduled",
94
+ "skipped",
95
+ "canceled"
96
+ ]);
97
+ var EDGE_KINDS = /* @__PURE__ */ new Set([
98
+ "contains",
99
+ "data",
100
+ "control",
101
+ "selected_outcome",
102
+ "batch_item",
103
+ "batch_aggregate",
104
+ "parallel_combine"
105
+ ]);
106
+ function isPlainObject(value) {
107
+ return typeof value === "object" && value !== null && !Array.isArray(value);
108
+ }
109
+ function describe(value) {
110
+ if (value === null) return "null";
111
+ if (value === void 0) return "undefined";
112
+ if (Array.isArray(value)) return "an array";
113
+ return `a ${typeof value}`;
114
+ }
115
+ function fail(path, detail) {
116
+ throw new GraphSpecValidationError(path, detail);
117
+ }
118
+ function requireNonEmptyString(value, path) {
119
+ if (typeof value !== "string" || value.length === 0) {
120
+ fail(path, `expected a non-empty string, got ${describe(value)}`);
121
+ }
122
+ return value;
123
+ }
124
+ function validateIoItem(item, path) {
125
+ if (!isPlainObject(item)) {
126
+ fail(path, `expected an object, got ${describe(item)}`);
127
+ }
128
+ requireNonEmptyString(item.name, `${path}.name`);
129
+ }
130
+ function validateNode(node, path) {
131
+ if (!isPlainObject(node)) {
132
+ fail(path, `expected an object, got ${describe(node)}`);
133
+ }
134
+ requireNonEmptyString(node.id, `${path}.id`);
135
+ if (node.kind !== "controller" && node.kind !== "operator") {
136
+ fail(
137
+ `${path}.kind`,
138
+ `expected "controller" or "operator" \u2014 a real pipelex run emits only pipe-call nodes, got ${JSON.stringify(node.kind)}`
139
+ );
140
+ }
141
+ if (typeof node.status !== "string" || !PIPE_STATUSES.has(node.status)) {
142
+ fail(
143
+ `${path}.status`,
144
+ `expected one of ${[...PIPE_STATUSES].join(", ")}, got ${JSON.stringify(node.status)}`
145
+ );
146
+ }
147
+ requireNonEmptyString(node.pipe_code, `${path}.pipe_code`);
148
+ const pipeType = requireNonEmptyString(node.pipe_type, `${path}.pipe_type`);
149
+ if (!KNOWN_PIPE_TYPES.has(pipeType)) {
150
+ fail(
151
+ `${path}.pipe_type`,
152
+ `unrecognized pipe class "${pipeType}" \u2014 add it to PipeOperatorType or PipeControllerType in types.ts`
153
+ );
154
+ }
155
+ requireNonEmptyString(node.description, `${path}.description`);
156
+ requireNonEmptyString(node.domain_code, `${path}.domain_code`);
157
+ let io = node.io;
158
+ if (io === void 0) {
159
+ io = { inputs: [], outputs: [] };
160
+ node.io = io;
161
+ }
162
+ if (!isPlainObject(io)) {
163
+ fail(`${path}.io`, `expected an object, got ${describe(io)}`);
164
+ }
165
+ if (io.inputs === void 0) {
166
+ io.inputs = [];
167
+ } else if (!Array.isArray(io.inputs)) {
168
+ fail(`${path}.io.inputs`, `expected an array, got ${describe(io.inputs)}`);
169
+ }
170
+ if (io.outputs === void 0) {
171
+ io.outputs = [];
172
+ } else if (!Array.isArray(io.outputs)) {
173
+ fail(`${path}.io.outputs`, `expected an array, got ${describe(io.outputs)}`);
174
+ }
175
+ io.inputs.forEach((item, j) => validateIoItem(item, `${path}.io.inputs[${j}]`));
176
+ io.outputs.forEach((item, j) => validateIoItem(item, `${path}.io.outputs[${j}]`));
177
+ }
178
+ function validateEdge(edge, path) {
179
+ if (!isPlainObject(edge)) {
180
+ fail(path, `expected an object, got ${describe(edge)}`);
181
+ }
182
+ requireNonEmptyString(edge.id, `${path}.id`);
183
+ requireNonEmptyString(edge.source, `${path}.source`);
184
+ requireNonEmptyString(edge.target, `${path}.target`);
185
+ if (typeof edge.kind !== "string" || !EDGE_KINDS.has(edge.kind)) {
186
+ fail(
187
+ `${path}.kind`,
188
+ `expected one of ${[...EDGE_KINDS].join(", ")}, got ${JSON.stringify(edge.kind)}`
189
+ );
190
+ }
191
+ }
192
+ function validateGraphSpec(raw) {
193
+ if (!isPlainObject(raw)) {
194
+ fail("", `expected a GraphSpec object, got ${describe(raw)}`);
195
+ }
196
+ if (!Array.isArray(raw.nodes)) {
197
+ fail("nodes", `expected an array, got ${describe(raw.nodes)}`);
198
+ }
199
+ if (!Array.isArray(raw.edges)) {
200
+ fail("edges", `expected an array, got ${describe(raw.edges)}`);
201
+ }
202
+ if (!isPlainObject(raw.meta)) {
203
+ fail("meta", `expected an object, got ${describe(raw.meta)}`);
204
+ }
205
+ if (raw.meta.format !== "mthds") {
206
+ fail(
207
+ "meta.format",
208
+ `expected "mthds" (this does not look like pipelex GraphSpec JSON), got ${JSON.stringify(raw.meta.format)}`
209
+ );
210
+ }
211
+ if (raw.pipe_registry !== void 0 && !isPlainObject(raw.pipe_registry)) {
212
+ fail("pipe_registry", `expected an object when present, got ${describe(raw.pipe_registry)}`);
213
+ }
214
+ if (raw.concept_registry !== void 0 && !isPlainObject(raw.concept_registry)) {
215
+ fail(
216
+ "concept_registry",
217
+ `expected an object when present, got ${describe(raw.concept_registry)}`
218
+ );
219
+ }
220
+ raw.nodes.forEach((node, i) => validateNode(node, `nodes[${i}]`));
221
+ raw.edges.forEach((edge, i) => validateEdge(edge, `edges[${i}]`));
222
+ return raw;
223
+ }
224
+ function asPipeCallNode(node, path = "node") {
225
+ if (node.kind !== "controller" && node.kind !== "operator") {
226
+ fail(
227
+ `${path}.kind`,
228
+ `expected a pipe-call node ("controller" or "operator"), got ${JSON.stringify(node.kind)}`
229
+ );
230
+ }
231
+ requireNonEmptyString(node.pipe_code, `${path}.pipe_code`);
232
+ return node;
233
+ }
234
+
63
235
  // src/graph/graphAnalysis.ts
64
236
  function buildDataflowAnalysis(graphspec) {
65
237
  if (!graphspec) return null;
@@ -77,9 +249,9 @@ function buildDataflowAnalysis(graphspec) {
77
249
  }
78
250
  const controllerNodeIds = new Set(Object.keys(containmentTree));
79
251
  for (const node of graphspec.nodes) {
80
- const nodeIo = node.io || {};
252
+ const nodeIo = node.io;
81
253
  const isController = controllerNodeIds.has(node.id);
82
- for (const output of nodeIo.outputs || []) {
254
+ for (const output of nodeIo.outputs) {
83
255
  if (output.digest && !stuffRegistry[output.digest]) {
84
256
  stuffRegistry[output.digest] = {
85
257
  name: output.name,
@@ -91,7 +263,7 @@ function buildDataflowAnalysis(graphspec) {
91
263
  stuffProducers[output.digest] = node.id;
92
264
  }
93
265
  }
94
- for (const input of nodeIo.inputs || []) {
266
+ for (const input of nodeIo.inputs) {
95
267
  if (input.digest && !stuffRegistry[input.digest]) {
96
268
  stuffRegistry[input.digest] = {
97
269
  name: input.name,
@@ -115,7 +287,6 @@ function buildDataflowAnalysis(graphspec) {
115
287
  };
116
288
  }
117
289
  function buildChildToControllerMap(graphspec, analysis) {
118
- var _a;
119
290
  const childToController = {};
120
291
  for (const [ctrlId, children] of Object.entries(analysis.containmentTree)) {
121
292
  for (const childId of children) {
@@ -133,7 +304,7 @@ function buildChildToControllerMap(graphspec, analysis) {
133
304
  if (!analysis.controllerNodeIds.has(node.id)) continue;
134
305
  const parentCtrlId = childToController[node.id];
135
306
  if (!parentCtrlId) continue;
136
- for (const output of ((_a = node.io) == null ? void 0 : _a.outputs) || []) {
307
+ for (const output of node.io.outputs) {
137
308
  if (!output.digest) continue;
138
309
  const stuffId = "stuff_" + output.digest;
139
310
  if (!childToController[stuffId]) {
@@ -214,55 +385,20 @@ function resolveConceptRef(spec, codeOrRef) {
214
385
  }
215
386
 
216
387
  // src/graph/pipeCardPayload.ts
217
- function defaultDescription(pipeType, pipeCode) {
218
- const code = pipeCode || "this step";
219
- const verb = {
220
- PipeLLM: "Analyze and generate output using",
221
- PipeExtract: "Extract content from",
222
- PipeCompose: "Compose output using",
223
- PipeImgGen: "Generate image for",
224
- PipeSearch: "Search the web for",
225
- PipeFunc: "Process data in"
226
- };
227
- return `${verb[pipeType] || "Execute"} ${code.replace(/_/g, " ")}`;
228
- }
229
- function buildPipeCardPayload(node, graphspec, analysis) {
230
- var _a, _b, _c, _d, _e, _f, _g;
231
- const pipeType = node.pipe_type;
232
- const pipeCode = node.pipe_code || node.id;
233
- const isController = analysis.controllerNodeIds.has(node.id);
234
- const inputs = ((_b = (_a = node.io) == null ? void 0 : _a.inputs) != null ? _b : []).map((i) => {
235
- var _a2, _b2;
236
- return {
237
- name: (_a2 = i.name) != null ? _a2 : "",
238
- concept: (_b2 = i.concept) != null ? _b2 : ""
239
- };
240
- });
241
- const outputs = ((_d = (_c = node.io) == null ? void 0 : _c.outputs) != null ? _d : []).map((o) => {
242
- var _a2, _b2;
243
- return {
244
- name: (_a2 = o.name) != null ? _a2 : "",
245
- concept: (_b2 = o.concept) != null ? _b2 : ""
246
- };
247
- });
248
- const registryDescription = node.pipe_code ? (_f = (_e = graphspec.pipe_registry) == null ? void 0 : _e[node.pipe_code]) == null ? void 0 : _f.description : void 0;
249
- let description;
250
- if (node.description) {
251
- description = node.description;
252
- } else if (registryDescription) {
253
- description = registryDescription;
254
- } else if (isController) {
255
- description = void 0;
256
- } else {
257
- description = defaultDescription(pipeType, node.pipe_code);
258
- }
388
+ function buildPipeCardPayload(node) {
259
389
  return {
260
- pipeCode,
261
- pipeType,
262
- description,
263
- status: (_g = node.status) != null ? _g : "scheduled",
264
- inputs,
265
- outputs
390
+ pipeCode: node.pipe_code,
391
+ pipeType: node.pipe_type,
392
+ description: node.description,
393
+ status: node.status,
394
+ inputs: node.io.inputs.map((i) => {
395
+ var _a;
396
+ return { name: i.name, concept: (_a = i.concept) != null ? _a : "" };
397
+ }),
398
+ outputs: node.io.outputs.map((o) => {
399
+ var _a;
400
+ return { name: o.name, concept: (_a = o.concept) != null ? _a : "" };
401
+ })
266
402
  };
267
403
  }
268
404
 
@@ -285,20 +421,21 @@ function buildDataflowGraph(graphspec, analysis, edgeType) {
285
421
  }
286
422
  for (const node of graphspec.nodes) {
287
423
  if (!participatingPipes.has(node.id)) continue;
288
- const isFailed = node.status === "failed";
289
- const label = node.pipe_code || node.id.split(":").pop() || node.id;
290
- const pipeCardData = buildPipeCardPayload(node, graphspec, analysis);
424
+ const pipeNode = asPipeCallNode(node, `nodes[${node.id}]`);
425
+ const isFailed = pipeNode.status === "failed";
426
+ const label = pipeNode.pipe_code;
427
+ const pipeCardData = buildPipeCardPayload(pipeNode);
291
428
  nodes.push({
292
- id: node.id,
429
+ id: pipeNode.id,
293
430
  type: NODE_TYPE_PIPE_CARD,
294
431
  data: {
295
432
  labelDescriptor: { kind: "pipe", label, isFailed },
296
- nodeData: node,
433
+ nodeData: pipeNode,
297
434
  isPipe: true,
298
435
  isStuff: false,
299
436
  labelText: label,
300
437
  pipeCode: pipeCardData.pipeCode,
301
- pipeType: node.pipe_type,
438
+ pipeType: pipeNode.pipe_type,
302
439
  pipeCardData
303
440
  },
304
441
  position: { x: 0, y: 0 }
@@ -306,7 +443,7 @@ function buildDataflowGraph(graphspec, analysis, edgeType) {
306
443
  }
307
444
  for (const [digest, stuffInfo] of Object.entries(analysis.stuffRegistry)) {
308
445
  const stuffId = stuffNodeId(digest);
309
- const label = stuffInfo.name || "data";
446
+ const label = stuffInfo.name;
310
447
  const concept = stuffInfo.concept || "";
311
448
  const textWidth = Math.max(label.length, concept.length) * STUFF_CHAR_WIDTH_PX + STUFF_LABEL_PADDING;
312
449
  const stuffWidth = Math.max(MIN_STUFF_WIDTH, textWidth);
@@ -377,7 +514,7 @@ function buildDataflowGraph(graphspec, analysis, edgeType) {
377
514
  const sourceId = stuffNodeId(edge.source_stuff_digest);
378
515
  const targetId = stuffNodeId(edge.target_stuff_digest);
379
516
  edges.push({
380
- id: edge.id || "edge_" + edgeId++,
517
+ id: edge.id,
381
518
  source: sourceId,
382
519
  target: targetId,
383
520
  type: "smoothstep",
@@ -402,7 +539,7 @@ function buildDataflowGraph(graphspec, analysis, edgeType) {
402
539
  const targetId = stuffNodeId(edge.target_stuff_digest);
403
540
  const isBatchItem = edge.kind === "batch_item";
404
541
  edges.push({
405
- id: edge.id || "edge_" + edgeId++,
542
+ id: edge.id,
406
543
  source: sourceId,
407
544
  target: targetId,
408
545
  type: edgeType,
@@ -548,7 +685,7 @@ function applyFolds(graphData, analysis, graphspec, foldedSet, onToggleFold) {
548
685
  if (outermostFoldedAncestor(folded, childToCtrl, foldedSet)) continue;
549
686
  const specNode = findSpecNode(graphspec, folded);
550
687
  if (!specNode) continue;
551
- const payload = buildPipeCardPayload(specNode, graphspec, analysis);
688
+ const payload = buildPipeCardPayload(asPipeCallNode(specNode, folded));
552
689
  if (onToggleFold) {
553
690
  payload.onExpand = (options) => onToggleFold(folded, options);
554
691
  }
@@ -1096,7 +1233,7 @@ function buildControllerNodes(graphspec, analysis, layoutedNodes, controllerPosi
1096
1233
  const controllerInfo = {};
1097
1234
  for (const node of graphspec.nodes) {
1098
1235
  if (analysis.controllerNodeIds.has(node.id)) {
1099
- controllerInfo[node.id] = node;
1236
+ controllerInfo[node.id] = asPipeCallNode(node, `nodes[${node.id}]`);
1100
1237
  }
1101
1238
  }
1102
1239
  const depthCache = {};
@@ -1168,8 +1305,14 @@ function buildControllerNodes(graphspec, analysis, layoutedNodes, controllerPosi
1168
1305
  groupW = maxX - minX + 2 * padX;
1169
1306
  groupH = maxY - minY + padTop + padBottom;
1170
1307
  }
1171
- const info = controllerInfo[controllerId] || {};
1172
- const pipeCode = info.pipe_code || controllerId.split(":").pop() || controllerId;
1308
+ const info = controllerInfo[controllerId];
1309
+ if (!info) {
1310
+ throw new GraphSpecValidationError(
1311
+ `nodes[${controllerId}]`,
1312
+ `controller "${controllerId}" is referenced by a "contains" edge but has no corresponding node in graphspec.nodes`
1313
+ );
1314
+ }
1315
+ const pipeCode = info.pipe_code;
1173
1316
  const groupNode = {
1174
1317
  id: controllerId,
1175
1318
  type: NODE_TYPE_CONTROLLER,
@@ -1319,50 +1462,176 @@ function applyControllers(layoutedNodes, layoutedEdges, graphspec, analysis, sho
1319
1462
  }
1320
1463
 
1321
1464
  // src/graph/graphConfig.ts
1465
+ var FONT_TOKENS = {
1466
+ "--font-sans": '"Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif',
1467
+ "--font-mono": '"JetBrains Mono", "Monaco", "Menlo", monospace'
1468
+ };
1469
+ var DARK_PALETTE_COLORS = __spreadProps(__spreadValues({}, FONT_TOKENS), {
1470
+ // Surfaces
1471
+ "--surface-page": "#0a0a0a",
1472
+ "--surface-panel": "#111118",
1473
+ "--surface-overlay": "rgba(17, 17, 24, 0.8)",
1474
+ "--surface-overlay-hover": "rgba(30, 30, 40, 0.9)",
1475
+ "--surface-overlay-disabled": "rgba(17, 17, 24, 0.6)",
1476
+ "--surface-elevated": "rgba(255, 255, 255, 0.06)",
1477
+ "--surface-elevated-hover": "rgba(255, 255, 255, 0.1)",
1478
+ "--surface-sunken": "rgba(255, 255, 255, 0.03)",
1479
+ "--surface-pill": "rgba(255, 255, 255, 0.06)",
1480
+ "--surface-pill-border": "rgba(255, 255, 255, 0.08)",
1481
+ // Borders
1482
+ "--border-subtle": "rgba(255, 255, 255, 0.06)",
1483
+ "--border-default": "rgba(255, 255, 255, 0.1)",
1484
+ "--border-strong": "rgba(255, 255, 255, 0.18)",
1485
+ "--border-dashed": "rgba(255, 255, 255, 0.15)",
1486
+ // Text
1487
+ "--text-primary": "#f8fafc",
1488
+ "--text-default": "#e2e8f0",
1489
+ "--text-secondary": "#cbd5e1",
1490
+ "--text-muted": "#94a3b8",
1491
+ "--text-dim": "#64748b",
1492
+ "--text-on-accent": "#0e0e0e",
1493
+ // Effects
1494
+ "--shadow-sm": "0 2px 8px rgba(0, 0, 0, 0.4)",
1495
+ "--shadow-md": "0 4px 16px rgba(0, 0, 0, 0.6)",
1496
+ "--shadow-lg": "0 8px 24px rgba(0, 0, 0, 0.5)",
1497
+ "--focus-ring": "rgba(59, 130, 246, 0.6)",
1498
+ // Domain-semantic colors (graph nodes/edges)
1499
+ "--color-pipe": "#ff6b6b",
1500
+ "--color-pipe-bg": "rgba(224,108,117,0.18)",
1501
+ "--color-pipe-text": "#ffffff",
1502
+ "--color-stuff": "#4ECDC4",
1503
+ "--color-stuff-bg": "rgba(78,205,196,0.12)",
1504
+ "--color-stuff-border": "#9ddcfd",
1505
+ "--color-stuff-text": "#98FB98",
1506
+ "--color-stuff-text-dim": "#9ddcfd",
1507
+ "--color-edge": "#FFFACD",
1508
+ "--color-batch-item": "#bd93f9",
1509
+ "--color-batch-aggregate": "#50fa7b",
1510
+ "--color-parallel-combine": "#d6a4ff",
1511
+ "--color-success": "#50FA7B",
1512
+ "--color-success-bg": "rgba(80,250,123,0.15)",
1513
+ "--color-error": "#FF5555",
1514
+ "--color-error-bg": "rgba(255,85,85,0.15)",
1515
+ "--color-error-border": "rgba(255,85,85,0.2)",
1516
+ "--color-accent": "#8BE9FD",
1517
+ "--color-accent-strong": "#3b82f6",
1518
+ "--color-warning": "#FFB86C",
1519
+ // Controller-group palette (tinted borders/backgrounds per controller type)
1520
+ "--ctrl-sequence-border": "rgba(148, 163, 184, 0.25)",
1521
+ "--ctrl-sequence-bg": "rgba(148, 163, 184, 0.03)",
1522
+ "--ctrl-sequence-text": "#94a3b8",
1523
+ "--ctrl-parallel-border": "rgba(139, 233, 253, 0.35)",
1524
+ "--ctrl-parallel-bg": "rgba(139, 233, 253, 0.03)",
1525
+ "--ctrl-parallel-text": "#8be9fd",
1526
+ "--ctrl-condition-border": "rgba(251, 191, 36, 0.35)",
1527
+ "--ctrl-condition-bg": "rgba(251, 191, 36, 0.03)",
1528
+ "--ctrl-condition-text": "#fbbf24",
1529
+ "--ctrl-batch-border": "rgba(167, 139, 250, 0.35)",
1530
+ "--ctrl-batch-bg": "rgba(167, 139, 250, 0.03)",
1531
+ "--ctrl-batch-text": "#a78bfa",
1532
+ "--ctrl-folded-bg": "rgba(148, 163, 184, 0.25)",
1533
+ "--ctrl-folded-border": "rgba(148, 163, 184, 0.4)",
1534
+ // Legacy aliases — kept so existing inline styles in graph builders keep
1535
+ // working until they're migrated. New code should use the semantic tokens.
1536
+ "--color-bg": "#0a0a0a",
1537
+ "--color-bg-dots": "rgba(255, 255, 255, 0.35)",
1538
+ "--color-text-muted": "#94a3b8",
1539
+ "--color-controller-text": "#94a3b8"
1540
+ });
1541
+ var LIGHT_PALETTE_COLORS = __spreadProps(__spreadValues({}, FONT_TOKENS), {
1542
+ // Surfaces — chart stays a soft off-white, panels slightly cooler
1543
+ "--surface-page": "#f8fafc",
1544
+ "--surface-panel": "#ffffff",
1545
+ "--surface-overlay": "rgba(255, 255, 255, 0.92)",
1546
+ "--surface-overlay-hover": "rgba(241, 245, 249, 0.96)",
1547
+ "--surface-overlay-disabled": "rgba(255, 255, 255, 0.7)",
1548
+ "--surface-elevated": "rgba(15, 23, 42, 0.05)",
1549
+ "--surface-elevated-hover": "rgba(15, 23, 42, 0.09)",
1550
+ "--surface-sunken": "rgba(15, 23, 42, 0.03)",
1551
+ "--surface-pill": "rgba(15, 23, 42, 0.05)",
1552
+ "--surface-pill-border": "rgba(15, 23, 42, 0.1)",
1553
+ // Borders
1554
+ "--border-subtle": "rgba(15, 23, 42, 0.08)",
1555
+ "--border-default": "rgba(15, 23, 42, 0.12)",
1556
+ "--border-strong": "rgba(15, 23, 42, 0.22)",
1557
+ "--border-dashed": "rgba(15, 23, 42, 0.18)",
1558
+ // Text
1559
+ "--text-primary": "#020617",
1560
+ "--text-default": "#0f172a",
1561
+ "--text-secondary": "#1e293b",
1562
+ "--text-muted": "#475569",
1563
+ "--text-dim": "#64748b",
1564
+ "--text-on-accent": "#ffffff",
1565
+ // Effects
1566
+ "--shadow-sm": "0 2px 8px rgba(15, 23, 42, 0.08)",
1567
+ "--shadow-md": "0 4px 16px rgba(15, 23, 42, 0.14)",
1568
+ "--shadow-lg": "0 8px 24px rgba(15, 23, 42, 0.15)",
1569
+ "--focus-ring": "rgba(2, 132, 199, 0.5)",
1570
+ // Domain-semantic colors — darker for contrast on light backgrounds
1571
+ "--color-pipe": "#dc2626",
1572
+ "--color-pipe-bg": "rgba(220, 38, 38, 0.1)",
1573
+ "--color-pipe-text": "#ffffff",
1574
+ "--color-stuff": "#0891b2",
1575
+ "--color-stuff-bg": "rgba(8, 145, 178, 0.08)",
1576
+ "--color-stuff-border": "#0e7490",
1577
+ "--color-stuff-text": "#0f172a",
1578
+ "--color-stuff-text-dim": "#475569",
1579
+ "--color-edge": "#64748b",
1580
+ "--color-batch-item": "#7c3aed",
1581
+ "--color-batch-aggregate": "#15803d",
1582
+ "--color-parallel-combine": "#6d28d9",
1583
+ "--color-success": "#15803d",
1584
+ "--color-success-bg": "rgba(21, 128, 61, 0.12)",
1585
+ "--color-error": "#dc2626",
1586
+ "--color-error-bg": "rgba(220, 38, 38, 0.1)",
1587
+ "--color-error-border": "rgba(220, 38, 38, 0.25)",
1588
+ "--color-accent": "#0284c7",
1589
+ "--color-accent-strong": "#0284c7",
1590
+ "--color-warning": "#d97706",
1591
+ // Controller-group palette
1592
+ "--ctrl-sequence-border": "rgba(71, 85, 105, 0.3)",
1593
+ "--ctrl-sequence-bg": "rgba(71, 85, 105, 0.04)",
1594
+ "--ctrl-sequence-text": "#475569",
1595
+ "--ctrl-parallel-border": "rgba(8, 145, 178, 0.4)",
1596
+ "--ctrl-parallel-bg": "rgba(8, 145, 178, 0.05)",
1597
+ "--ctrl-parallel-text": "#0e7490",
1598
+ "--ctrl-condition-border": "rgba(217, 119, 6, 0.4)",
1599
+ "--ctrl-condition-bg": "rgba(217, 119, 6, 0.05)",
1600
+ "--ctrl-condition-text": "#b45309",
1601
+ "--ctrl-batch-border": "rgba(124, 58, 237, 0.4)",
1602
+ "--ctrl-batch-bg": "rgba(124, 58, 237, 0.05)",
1603
+ "--ctrl-batch-text": "#6d28d9",
1604
+ "--ctrl-folded-bg": "rgba(71, 85, 105, 0.18)",
1605
+ "--ctrl-folded-border": "rgba(71, 85, 105, 0.35)",
1606
+ // Legacy aliases
1607
+ "--color-bg": "#f8fafc",
1608
+ "--color-bg-dots": "rgba(15, 23, 42, 0.18)",
1609
+ "--color-text-muted": "#475569",
1610
+ "--color-controller-text": "#475569"
1611
+ });
1612
+ function getPaletteForTheme(theme) {
1613
+ return theme === GRAPH_THEME.LIGHT ? LIGHT_PALETTE_COLORS : DARK_PALETTE_COLORS;
1614
+ }
1322
1615
  var DEFAULT_GRAPH_CONFIG = {
1323
1616
  direction: "LR",
1324
1617
  showControllers: false,
1325
1618
  foldMode: FOLD_MODE.EXPANDED,
1619
+ theme: GRAPH_THEME.DARK,
1326
1620
  nodesep: 50,
1327
1621
  ranksep: 100,
1328
1622
  edgeType: EDGE_TYPE.DEFAULT,
1329
1623
  initialZoom: null,
1330
- panToTop: true,
1331
- paletteColors: {
1332
- // Graph node/edge colors
1333
- "--color-pipe": "#ff6b6b",
1334
- "--color-pipe-bg": "rgba(224,108,117,0.18)",
1335
- "--color-pipe-text": "#ffffff",
1336
- "--color-stuff": "#4ECDC4",
1337
- "--color-stuff-bg": "rgba(78,205,196,0.12)",
1338
- "--color-stuff-border": "#9ddcfd",
1339
- "--color-stuff-text": "#98FB98",
1340
- "--color-stuff-text-dim": "#9ddcfd",
1341
- "--color-edge": "#FFFACD",
1342
- "--color-batch-item": "#bd93f9",
1343
- "--color-batch-aggregate": "#50fa7b",
1344
- "--color-parallel-combine": "#d6a4ff",
1345
- "--color-success": "#50FA7B",
1346
- "--color-success-bg": "rgba(80,250,123,0.15)",
1347
- "--color-error": "#FF5555",
1348
- "--color-error-bg": "rgba(255,85,85,0.15)",
1349
- "--color-accent": "#8BE9FD",
1350
- "--color-warning": "#FFB86C",
1351
- // Base theme vars used by graph-core.css
1352
- "--color-bg": "#0a0a0a",
1353
- "--color-bg-dots": "rgba(255, 255, 255, 0.35)",
1354
- "--color-text-muted": "#94a3b8",
1355
- "--color-controller-text": "#94a3b8",
1356
- "--font-sans": '"Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif',
1357
- "--font-mono": '"JetBrains Mono", "Monaco", "Menlo", monospace',
1358
- "--shadow-lg": "0 8px 24px rgba(0, 0, 0, 0.5)"
1359
- }
1624
+ panToTop: true
1625
+ // `paletteColors` intentionally omitted from the default. The viewer derives
1626
+ // the palette from the active `theme`. Consumers only set `paletteColors` to
1627
+ // override specific tokens (sparse merge on top of the theme palette).
1360
1628
  };
1361
1629
  function getPaletteColors() {
1362
- return DEFAULT_GRAPH_CONFIG.paletteColors || {};
1630
+ return DARK_PALETTE_COLORS;
1363
1631
  }
1364
1632
 
1365
1633
  export {
1634
+ KNOWN_PIPE_TYPES,
1366
1635
  NODE_TYPE_PIPE_CARD,
1367
1636
  NODE_TYPE_STUFF,
1368
1637
  NODE_TYPE_CONTROLLER,
@@ -1373,12 +1642,16 @@ export {
1373
1642
  GRAPH_DIRECTION,
1374
1643
  EDGE_TYPE,
1375
1644
  FOLD_MODE,
1645
+ GRAPH_THEME,
1376
1646
  CONTROLLER_PADDING_X,
1377
1647
  CONTROLLER_PADDING_TOP,
1378
1648
  CONTROLLER_PADDING_BOTTOM,
1379
1649
  ARROW_CLOSED_MARKER,
1380
1650
  nodeWidth,
1381
1651
  nodeHeight,
1652
+ GraphSpecValidationError,
1653
+ validateGraphSpec,
1654
+ asPipeCallNode,
1382
1655
  buildDataflowAnalysis,
1383
1656
  buildChildToControllerMap,
1384
1657
  getPipeBlueprint,
@@ -1400,7 +1673,10 @@ export {
1400
1673
  MAX_VISIBLE_CONTROLLER_CHILDREN,
1401
1674
  buildControllerNodes,
1402
1675
  applyControllers,
1676
+ DARK_PALETTE_COLORS,
1677
+ LIGHT_PALETTE_COLORS,
1678
+ getPaletteForTheme,
1403
1679
  DEFAULT_GRAPH_CONFIG,
1404
1680
  getPaletteColors
1405
1681
  };
1406
- //# sourceMappingURL=chunk-FHRUYFGV.js.map
1682
+ //# sourceMappingURL=chunk-ILX53OYM.js.map