@vaultgradient/pipequery-lang 1.2.2

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.
@@ -0,0 +1,1374 @@
1
+ // src/react/PipeQueryBuilder.tsx
2
+ import { useState as useState3, useCallback, useMemo, useEffect, useRef } from "react";
3
+ import { Box as Box6 } from "@mui/material";
4
+
5
+ // src/react/SourceNode.tsx
6
+ import {
7
+ Paper,
8
+ Stack,
9
+ FormControl,
10
+ InputLabel,
11
+ Select,
12
+ MenuItem,
13
+ Chip,
14
+ alpha
15
+ } from "@mui/material";
16
+ import StorageIcon from "@mui/icons-material/Storage";
17
+ import { jsx, jsxs } from "react/jsx-runtime";
18
+ function SourceNode({
19
+ source,
20
+ onSourceChange,
21
+ availableSources,
22
+ rowCount,
23
+ orientation,
24
+ compact
25
+ }) {
26
+ const isH = orientation === "horizontal";
27
+ const iconSz = compact ? 16 : 20;
28
+ const chipFontSz = compact ? "0.6rem" : "0.7rem";
29
+ return /* @__PURE__ */ jsx(
30
+ Paper,
31
+ {
32
+ variant: "outlined",
33
+ sx: {
34
+ p: compact ? 0.75 : 1.5,
35
+ ...isH ? {
36
+ borderLeft: "3px solid",
37
+ borderLeftColor: "primary.main",
38
+ width: compact ? 140 : 180,
39
+ flexShrink: 0,
40
+ alignSelf: "stretch",
41
+ display: "flex",
42
+ alignItems: "center"
43
+ } : {
44
+ borderTop: "3px solid",
45
+ borderTopColor: "primary.main"
46
+ },
47
+ background: (t) => `linear-gradient(135deg, ${alpha(t.palette.primary.main, 0.06)} 0%, transparent 60%)`
48
+ },
49
+ children: /* @__PURE__ */ jsxs(
50
+ Stack,
51
+ {
52
+ direction: isH && compact ? "column" : "row",
53
+ spacing: compact ? 0.5 : 1,
54
+ sx: { alignItems: isH && compact ? "flex-start" : "center", width: "100%" },
55
+ children: [
56
+ /* @__PURE__ */ jsx(StorageIcon, { sx: { color: "primary.main", fontSize: iconSz } }),
57
+ /* @__PURE__ */ jsxs(FormControl, { size: "small", sx: { flex: 1, minWidth: 0 }, children: [
58
+ !compact && /* @__PURE__ */ jsx(InputLabel, { children: "Source" }),
59
+ /* @__PURE__ */ jsx(
60
+ Select,
61
+ {
62
+ value: source,
63
+ label: compact ? void 0 : "Source",
64
+ onChange: (e) => onSourceChange(e.target.value),
65
+ MenuProps: { style: { zIndex: 1400 } },
66
+ sx: {
67
+ fontSize: compact ? "0.72rem" : "0.85rem",
68
+ "& .MuiSelect-select": compact ? { py: 0.5, px: 0.75 } : {}
69
+ },
70
+ children: availableSources.map((s) => /* @__PURE__ */ jsx(MenuItem, { value: s, sx: { fontSize: compact ? "0.72rem" : "0.85rem" }, children: s }, s))
71
+ }
72
+ )
73
+ ] }),
74
+ rowCount != null && /* @__PURE__ */ jsx(
75
+ Chip,
76
+ {
77
+ label: `${rowCount.toLocaleString()}`,
78
+ size: "small",
79
+ variant: "outlined",
80
+ sx: {
81
+ fontFamily: '"JetBrains Mono", monospace',
82
+ fontSize: chipFontSz,
83
+ height: compact ? 18 : 24,
84
+ borderColor: (t) => alpha(t.palette.primary.main, 0.3)
85
+ }
86
+ }
87
+ )
88
+ ]
89
+ }
90
+ )
91
+ }
92
+ );
93
+ }
94
+
95
+ // src/react/ResultNode.tsx
96
+ import { Paper as Paper2, Stack as Stack2, Typography, Box, alpha as alpha2 } from "@mui/material";
97
+ import CodeIcon from "@mui/icons-material/Code";
98
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
99
+ function ResultNode({
100
+ query,
101
+ orientation,
102
+ compact
103
+ }) {
104
+ const isH = orientation === "horizontal";
105
+ return /* @__PURE__ */ jsxs2(
106
+ Paper2,
107
+ {
108
+ variant: "outlined",
109
+ sx: {
110
+ p: compact ? 0.75 : 1.5,
111
+ ...isH ? {
112
+ borderRight: "3px solid",
113
+ borderRightColor: "success.main",
114
+ width: compact ? 160 : 220,
115
+ flexShrink: 0,
116
+ alignSelf: "stretch",
117
+ display: "flex",
118
+ flexDirection: "column",
119
+ justifyContent: "center"
120
+ } : {
121
+ borderBottom: "3px solid",
122
+ borderBottomColor: "success.main"
123
+ },
124
+ background: (t) => `linear-gradient(135deg, ${alpha2(t.palette.success.main, 0.04)} 0%, transparent 60%)`
125
+ },
126
+ children: [
127
+ /* @__PURE__ */ jsxs2(Stack2, { direction: "row", spacing: 0.5, sx: { alignItems: "center", mb: compact ? 0.25 : 0.75 }, children: [
128
+ /* @__PURE__ */ jsx2(CodeIcon, { sx: { fontSize: compact ? 14 : 18, color: "success.main" } }),
129
+ /* @__PURE__ */ jsx2(Typography, { sx: { fontWeight: 600, fontSize: compact ? "0.65rem" : "0.8rem" }, children: "Query" })
130
+ ] }),
131
+ /* @__PURE__ */ jsx2(Box, { sx: {
132
+ p: compact ? 0.5 : 1,
133
+ bgcolor: "background.default",
134
+ borderRadius: 0.5,
135
+ fontFamily: '"JetBrains Mono", "Fira Code", monospace',
136
+ fontSize: compact ? "0.62rem" : "0.72rem",
137
+ color: "text.primary",
138
+ minHeight: compact ? 20 : 32,
139
+ whiteSpace: "pre-wrap",
140
+ wordBreak: "break-all",
141
+ lineHeight: 1.5,
142
+ overflow: "hidden",
143
+ maxHeight: isH ? 80 : "none"
144
+ }, children: query || "\u2014" })
145
+ ]
146
+ }
147
+ );
148
+ }
149
+
150
+ // src/react/PipelineConnector.tsx
151
+ import { useState as useState2 } from "react";
152
+ import { Box as Box3, IconButton, alpha as alpha4 } from "@mui/material";
153
+ import AddIcon from "@mui/icons-material/Add";
154
+
155
+ // src/react/StepPicker.tsx
156
+ import { useState } from "react";
157
+ import {
158
+ Box as Box2,
159
+ Typography as Typography2,
160
+ Chip as Chip2,
161
+ Stack as Stack3,
162
+ Popover,
163
+ Select as Select2,
164
+ MenuItem as MenuItem2,
165
+ ListSubheader,
166
+ alpha as alpha3
167
+ } from "@mui/material";
168
+
169
+ // src/react/icons.tsx
170
+ import FilterListIcon from "@mui/icons-material/FilterList";
171
+ import ViewColumnIcon from "@mui/icons-material/ViewColumn";
172
+ import SwapVertIcon from "@mui/icons-material/SwapVert";
173
+ import WorkspacesIcon from "@mui/icons-material/Workspaces";
174
+ import VerticalAlignTopIcon from "@mui/icons-material/VerticalAlignTop";
175
+ import VerticalAlignBottomIcon from "@mui/icons-material/VerticalAlignBottom";
176
+ import MergeTypeIcon from "@mui/icons-material/MergeType";
177
+ import TransformIcon from "@mui/icons-material/Transform";
178
+ import BarChartIcon from "@mui/icons-material/BarChart";
179
+ import SwapHorizIcon from "@mui/icons-material/SwapHoriz";
180
+ import UnfoldLessIcon from "@mui/icons-material/UnfoldLess";
181
+ import CompressIcon from "@mui/icons-material/Compress";
182
+ import DeblurIcon from "@mui/icons-material/Deblur";
183
+ import PivotTableChartIcon from "@mui/icons-material/PivotTableChart";
184
+ import { jsx as jsx3 } from "react/jsx-runtime";
185
+ var ICON_MAP = {
186
+ where: FilterListIcon,
187
+ distinct: DeblurIcon,
188
+ select: ViewColumnIcon,
189
+ map: TransformIcon,
190
+ flatten: UnfoldLessIcon,
191
+ transpose: SwapHorizIcon,
192
+ sort: SwapVertIcon,
193
+ groupBy: WorkspacesIcon,
194
+ reduce: CompressIcon,
195
+ rollup: BarChartIcon,
196
+ pivot: PivotTableChartIcon,
197
+ first: VerticalAlignTopIcon,
198
+ last: VerticalAlignBottomIcon,
199
+ join: MergeTypeIcon
200
+ };
201
+ function OperationIcon({ type, size = 16 }) {
202
+ const Icon = ICON_MAP[type];
203
+ return /* @__PURE__ */ jsx3(Icon, { sx: { fontSize: size } });
204
+ }
205
+
206
+ // src/react/types.ts
207
+ var CATEGORY_COLORS = {
208
+ filter: "#42a5f5",
209
+ transform: "#66bb6a",
210
+ sort: "#ffa726",
211
+ aggregate: "#ab47bc",
212
+ limit: "#26a69a",
213
+ join: "#ff7043"
214
+ };
215
+ var OPERATION_META = {
216
+ where: { label: "Filter (where)", shortLabel: "where", category: "filter", color: CATEGORY_COLORS.filter },
217
+ distinct: { label: "Distinct", shortLabel: "distinct", category: "filter", color: CATEGORY_COLORS.filter },
218
+ select: { label: "Select fields", shortLabel: "select", category: "transform", color: CATEGORY_COLORS.transform },
219
+ map: { label: "Map (add fields)", shortLabel: "map", category: "transform", color: CATEGORY_COLORS.transform },
220
+ flatten: { label: "Flatten", shortLabel: "flatten", category: "transform", color: CATEGORY_COLORS.transform },
221
+ transpose: { label: "Transpose", shortLabel: "transpose", category: "transform", color: CATEGORY_COLORS.transform },
222
+ sort: { label: "Sort", shortLabel: "sort", category: "sort", color: CATEGORY_COLORS.sort },
223
+ groupBy: { label: "Group By", shortLabel: "groupBy", category: "aggregate", color: CATEGORY_COLORS.aggregate },
224
+ reduce: { label: "Reduce", shortLabel: "reduce", category: "aggregate", color: CATEGORY_COLORS.aggregate },
225
+ rollup: { label: "Rollup", shortLabel: "rollup", category: "aggregate", color: CATEGORY_COLORS.aggregate },
226
+ pivot: { label: "Pivot", shortLabel: "pivot", category: "aggregate", color: CATEGORY_COLORS.aggregate },
227
+ first: { label: "First N", shortLabel: "first", category: "limit", color: CATEGORY_COLORS.limit },
228
+ last: { label: "Last N", shortLabel: "last", category: "limit", color: CATEGORY_COLORS.limit },
229
+ join: { label: "Join", shortLabel: "join", category: "join", color: CATEGORY_COLORS.join }
230
+ };
231
+ var CATEGORIES_ORDERED = [
232
+ { key: "filter", label: "Filter", ops: ["where", "distinct"] },
233
+ { key: "transform", label: "Transform", ops: ["select", "map", "flatten", "transpose"] },
234
+ { key: "sort", label: "Sort", ops: ["sort"] },
235
+ { key: "aggregate", label: "Aggregate", ops: ["groupBy", "reduce", "rollup", "pivot"] },
236
+ { key: "limit", label: "Limit", ops: ["first", "last"] },
237
+ { key: "join", label: "Join", ops: ["join"] }
238
+ ];
239
+ function createDefaultConfig(type) {
240
+ switch (type) {
241
+ case "where":
242
+ return { type, config: { condition: "" } };
243
+ case "select":
244
+ return { type, config: { fields: [], expressions: [] } };
245
+ case "sort":
246
+ return { type, config: { criteria: [{ field: "", direction: "asc" }] } };
247
+ case "groupBy":
248
+ return { type, config: { fields: [] } };
249
+ case "join":
250
+ return { type, config: { rightSource: "", condition: "" } };
251
+ case "first":
252
+ return { type, config: { count: 10 } };
253
+ case "last":
254
+ return { type, config: { count: 10 } };
255
+ case "distinct":
256
+ return { type, config: { fields: [] } };
257
+ case "map":
258
+ return { type, config: { expressions: [""] } };
259
+ case "reduce":
260
+ return { type, config: { initial: "0", accumulator: "" } };
261
+ case "rollup":
262
+ return { type, config: { keys: [], aggregates: [""] } };
263
+ case "pivot":
264
+ return { type, config: { pivotField: "", aggregates: [""] } };
265
+ case "flatten":
266
+ return { type, config: { field: "" } };
267
+ case "transpose":
268
+ return { type, config: { headerField: "" } };
269
+ }
270
+ }
271
+ function getStepSummary(step) {
272
+ switch (step.type) {
273
+ case "where":
274
+ return step.config.condition || "(no condition)";
275
+ case "select": {
276
+ const all = [...step.config.fields, ...step.config.expressions.filter(Boolean)];
277
+ return all.length > 0 ? all.join(", ") : "(no fields)";
278
+ }
279
+ case "sort": {
280
+ const parts = step.config.criteria.filter((c) => c.field).map(
281
+ (c) => c.direction === "desc" ? `${c.field} \u2193` : `${c.field} \u2191`
282
+ );
283
+ return parts.length > 0 ? parts.join(", ") : "(no criteria)";
284
+ }
285
+ case "groupBy":
286
+ return step.config.fields.length > 0 ? step.config.fields.join(", ") : "(no fields)";
287
+ case "join":
288
+ return step.config.rightSource ? `${step.config.rightSource} on ${step.config.condition || "..."}` : "(not configured)";
289
+ case "first":
290
+ case "last":
291
+ return `${step.config.count} rows`;
292
+ case "distinct":
293
+ return step.config.fields.length > 0 ? step.config.fields.join(", ") : "all fields";
294
+ case "map": {
295
+ const exprs = step.config.expressions.filter(Boolean);
296
+ return exprs.length > 0 ? exprs.join(", ") : "(no expressions)";
297
+ }
298
+ case "reduce":
299
+ return step.config.accumulator || "(no accumulator)";
300
+ case "rollup": {
301
+ const parts = [...step.config.keys, ...step.config.aggregates.filter(Boolean)];
302
+ return parts.length > 0 ? parts.join(", ") : "(not configured)";
303
+ }
304
+ case "pivot":
305
+ return step.config.pivotField || "(no pivot field)";
306
+ case "flatten":
307
+ return step.config.field || "all";
308
+ case "transpose":
309
+ return step.config.headerField || "auto";
310
+ }
311
+ }
312
+ function generateQuery(source, steps) {
313
+ if (!source) return "";
314
+ const parts = [source];
315
+ for (const { step } of steps) {
316
+ const dsl = stepToDsl(step);
317
+ if (dsl) parts.push(dsl);
318
+ }
319
+ return parts.join(" | ");
320
+ }
321
+ function stepToDsl(step) {
322
+ switch (step.type) {
323
+ case "where":
324
+ return step.config.condition ? `where(${step.config.condition})` : null;
325
+ case "select": {
326
+ const all = [...step.config.fields, ...step.config.expressions.filter(Boolean)];
327
+ return all.length > 0 ? `select(${all.join(", ")})` : null;
328
+ }
329
+ case "sort": {
330
+ const parts = step.config.criteria.filter((c) => c.field).map((c) => c.direction === "desc" ? `${c.field} desc` : c.field);
331
+ return parts.length > 0 ? `sort(${parts.join(", ")})` : null;
332
+ }
333
+ case "groupBy":
334
+ return step.config.fields.length > 0 ? `groupBy(${step.config.fields.join(", ")})` : null;
335
+ case "join":
336
+ return step.config.rightSource && step.config.condition ? `join(${step.config.rightSource}, ${step.config.condition})` : null;
337
+ case "first":
338
+ return `first(${step.config.count})`;
339
+ case "last":
340
+ return `last(${step.config.count})`;
341
+ case "distinct":
342
+ return step.config.fields.length > 0 ? `distinct(${step.config.fields.join(", ")})` : "distinct()";
343
+ case "map": {
344
+ const exprs = step.config.expressions.filter(Boolean);
345
+ return exprs.length > 0 ? `map(${exprs.join(", ")})` : null;
346
+ }
347
+ case "reduce":
348
+ return step.config.initial && step.config.accumulator ? `reduce(${step.config.initial}, ${step.config.accumulator})` : null;
349
+ case "rollup": {
350
+ const parts = [...step.config.keys, ...step.config.aggregates.filter(Boolean)];
351
+ return parts.length > 0 ? `rollup(${parts.join(", ")})` : null;
352
+ }
353
+ case "pivot": {
354
+ const aggs = step.config.aggregates.filter(Boolean);
355
+ return step.config.pivotField && aggs.length > 0 ? `pivot(${step.config.pivotField}, ${aggs.join(", ")})` : null;
356
+ }
357
+ case "flatten":
358
+ return step.config.field ? `flatten(${step.config.field})` : "flatten()";
359
+ case "transpose":
360
+ return step.config.headerField ? `transpose(${step.config.headerField})` : "transpose()";
361
+ }
362
+ }
363
+
364
+ // src/react/StepPicker.tsx
365
+ import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
366
+ function PopoverPicker({ anchorEl, onClose, onSelect, orientation }) {
367
+ const isH = orientation === "horizontal";
368
+ return /* @__PURE__ */ jsx4(
369
+ Popover,
370
+ {
371
+ open: Boolean(anchorEl),
372
+ anchorEl,
373
+ onClose,
374
+ anchorOrigin: isH ? { vertical: "bottom", horizontal: "center" } : { vertical: "center", horizontal: "right" },
375
+ transformOrigin: isH ? { vertical: "top", horizontal: "center" } : { vertical: "center", horizontal: "left" },
376
+ slotProps: { paper: { sx: { p: 1.5, minWidth: 240, maxWidth: 320 } } },
377
+ children: CATEGORIES_ORDERED.map(({ key, label, ops }) => /* @__PURE__ */ jsxs3(Box2, { sx: { mb: 1.25, "&:last-child": { mb: 0 } }, children: [
378
+ /* @__PURE__ */ jsx4(
379
+ Typography2,
380
+ {
381
+ variant: "caption",
382
+ sx: {
383
+ color: CATEGORY_COLORS[key],
384
+ fontWeight: 700,
385
+ textTransform: "uppercase",
386
+ letterSpacing: "0.05em",
387
+ fontSize: "0.58rem",
388
+ mb: 0.4,
389
+ display: "block"
390
+ },
391
+ children: label
392
+ }
393
+ ),
394
+ /* @__PURE__ */ jsx4(Stack3, { direction: "row", sx: { flexWrap: "wrap", gap: 0.5 }, children: ops.map((op) => /* @__PURE__ */ jsx4(
395
+ Chip2,
396
+ {
397
+ label: OPERATION_META[op].shortLabel,
398
+ size: "small",
399
+ icon: /* @__PURE__ */ jsx4(OperationIcon, { type: op, size: 14 }),
400
+ onClick: () => onSelect(op),
401
+ variant: "outlined",
402
+ sx: {
403
+ cursor: "pointer",
404
+ height: 24,
405
+ fontSize: "0.68rem",
406
+ borderColor: alpha3(CATEGORY_COLORS[key], 0.35),
407
+ color: CATEGORY_COLORS[key],
408
+ "& .MuiChip-icon": { color: CATEGORY_COLORS[key] },
409
+ "&:hover": {
410
+ bgcolor: alpha3(CATEGORY_COLORS[key], 0.12),
411
+ borderColor: CATEGORY_COLORS[key]
412
+ }
413
+ }
414
+ },
415
+ op
416
+ )) })
417
+ ] }, key))
418
+ }
419
+ );
420
+ }
421
+ function CompactPicker({ onSelect }) {
422
+ const [value, setValue] = useState("");
423
+ return /* @__PURE__ */ jsx4(
424
+ Select2,
425
+ {
426
+ size: "small",
427
+ value,
428
+ displayEmpty: true,
429
+ onChange: (e) => {
430
+ const v = e.target.value;
431
+ if (v) {
432
+ onSelect(v);
433
+ setValue("");
434
+ }
435
+ },
436
+ renderValue: () => /* @__PURE__ */ jsx4(Typography2, { sx: { fontSize: "0.65rem", color: "text.secondary" }, children: "+ step" }),
437
+ MenuProps: { style: { zIndex: 1400 } },
438
+ sx: {
439
+ minWidth: 70,
440
+ height: 22,
441
+ "& .MuiSelect-select": { py: 0.25, px: 0.75 },
442
+ fontSize: "0.7rem"
443
+ },
444
+ children: CATEGORIES_ORDERED.map(({ key, label, ops }) => [
445
+ /* @__PURE__ */ jsx4(
446
+ ListSubheader,
447
+ {
448
+ sx: {
449
+ fontSize: "0.58rem",
450
+ fontWeight: 700,
451
+ textTransform: "uppercase",
452
+ letterSpacing: "0.05em",
453
+ color: CATEGORY_COLORS[key],
454
+ lineHeight: "24px",
455
+ bgcolor: "background.paper"
456
+ },
457
+ children: label
458
+ },
459
+ `h-${key}`
460
+ ),
461
+ ...ops.map((op) => /* @__PURE__ */ jsxs3(
462
+ MenuItem2,
463
+ {
464
+ value: op,
465
+ sx: {
466
+ fontSize: "0.7rem",
467
+ py: 0.25,
468
+ minHeight: "auto",
469
+ gap: 0.75,
470
+ color: CATEGORY_COLORS[key]
471
+ },
472
+ children: [
473
+ /* @__PURE__ */ jsx4(OperationIcon, { type: op, size: 13 }),
474
+ OPERATION_META[op].shortLabel
475
+ ]
476
+ },
477
+ op
478
+ ))
479
+ ]).flat()
480
+ }
481
+ );
482
+ }
483
+ function StepPicker({
484
+ anchorEl,
485
+ onClose,
486
+ onSelect,
487
+ compact,
488
+ orientation
489
+ }) {
490
+ if (compact) {
491
+ return null;
492
+ }
493
+ return /* @__PURE__ */ jsx4(
494
+ PopoverPicker,
495
+ {
496
+ anchorEl,
497
+ onClose,
498
+ onSelect,
499
+ orientation
500
+ }
501
+ );
502
+ }
503
+
504
+ // src/react/PipelineConnector.tsx
505
+ import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
506
+ function PipelineConnector({
507
+ orientation,
508
+ compact,
509
+ onInsert
510
+ }) {
511
+ const [anchorEl, setAnchorEl] = useState2(null);
512
+ const isH = orientation === "horizontal";
513
+ const lineLen = compact ? 6 : 10;
514
+ const btnSize = compact ? 20 : 26;
515
+ const iconSize = compact ? 12 : 14;
516
+ if (compact) {
517
+ return /* @__PURE__ */ jsxs4(Box3, { sx: {
518
+ display: "flex",
519
+ flexDirection: isH ? "row" : "column",
520
+ alignItems: "center",
521
+ ...isH ? { py: 0 } : { px: 0 }
522
+ }, children: [
523
+ /* @__PURE__ */ jsx5(Box3, { sx: {
524
+ ...isH ? { height: 2, width: lineLen, bgcolor: (t) => alpha4(t.palette.text.secondary, 0.15) } : { width: 2, height: lineLen, bgcolor: (t) => alpha4(t.palette.text.secondary, 0.15) }
525
+ } }),
526
+ /* @__PURE__ */ jsx5(CompactPicker, { onSelect: onInsert }),
527
+ /* @__PURE__ */ jsx5(Box3, { sx: {
528
+ ...isH ? { height: 2, width: lineLen, bgcolor: (t) => alpha4(t.palette.text.secondary, 0.15) } : { width: 2, height: lineLen, bgcolor: (t) => alpha4(t.palette.text.secondary, 0.15) }
529
+ } })
530
+ ] });
531
+ }
532
+ return /* @__PURE__ */ jsxs4(Box3, { sx: {
533
+ display: "flex",
534
+ flexDirection: isH ? "row" : "column",
535
+ alignItems: "center",
536
+ ...isH ? { py: 0 } : { py: 0.25 }
537
+ }, children: [
538
+ /* @__PURE__ */ jsx5(Box3, { sx: {
539
+ ...isH ? { height: 2, width: lineLen, bgcolor: (t) => alpha4(t.palette.text.secondary, 0.2) } : { width: 2, height: lineLen, bgcolor: (t) => alpha4(t.palette.text.secondary, 0.2) }
540
+ } }),
541
+ /* @__PURE__ */ jsx5(
542
+ IconButton,
543
+ {
544
+ size: "small",
545
+ onClick: (e) => setAnchorEl(e.currentTarget),
546
+ sx: {
547
+ width: btnSize,
548
+ height: btnSize,
549
+ border: "2px dashed",
550
+ borderColor: (t) => alpha4(t.palette.text.secondary, 0.25),
551
+ color: "text.secondary",
552
+ transition: "all 0.2s",
553
+ "&:hover": {
554
+ borderColor: "primary.main",
555
+ borderStyle: "solid",
556
+ color: "primary.main",
557
+ bgcolor: (t) => alpha4(t.palette.primary.main, 0.08),
558
+ transform: "scale(1.15)"
559
+ }
560
+ },
561
+ children: /* @__PURE__ */ jsx5(AddIcon, { sx: { fontSize: iconSize } })
562
+ }
563
+ ),
564
+ /* @__PURE__ */ jsx5(Box3, { sx: {
565
+ ...isH ? { height: 2, width: lineLen, bgcolor: (t) => alpha4(t.palette.text.secondary, 0.2) } : { width: 2, height: lineLen, bgcolor: (t) => alpha4(t.palette.text.secondary, 0.2) }
566
+ } }),
567
+ /* @__PURE__ */ jsx5(
568
+ StepPicker,
569
+ {
570
+ anchorEl,
571
+ onClose: () => setAnchorEl(null),
572
+ onSelect: (type) => {
573
+ onInsert(type);
574
+ setAnchorEl(null);
575
+ },
576
+ compact: false,
577
+ orientation
578
+ }
579
+ )
580
+ ] });
581
+ }
582
+
583
+ // src/react/StepCard.tsx
584
+ import {
585
+ Paper as Paper3,
586
+ Stack as Stack5,
587
+ Typography as Typography4,
588
+ Box as Box5,
589
+ IconButton as IconButton3,
590
+ Collapse,
591
+ alpha as alpha6
592
+ } from "@mui/material";
593
+ import DeleteOutlineIcon2 from "@mui/icons-material/DeleteOutlined";
594
+ import ArrowUpwardIcon from "@mui/icons-material/ArrowUpward";
595
+ import ArrowDownwardIcon from "@mui/icons-material/ArrowDownward";
596
+ import ArrowBackIcon from "@mui/icons-material/ArrowBack";
597
+ import ArrowForwardIcon from "@mui/icons-material/ArrowForward";
598
+ import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
599
+
600
+ // src/react/StepConfigForm.tsx
601
+ import {
602
+ TextField,
603
+ Stack as Stack4,
604
+ Autocomplete,
605
+ ToggleButton,
606
+ ToggleButtonGroup,
607
+ Button,
608
+ IconButton as IconButton2,
609
+ FormControl as FormControl2,
610
+ InputLabel as InputLabel2,
611
+ Select as Select3,
612
+ MenuItem as MenuItem3,
613
+ Typography as Typography3,
614
+ Box as Box4,
615
+ alpha as alpha5
616
+ } from "@mui/material";
617
+ import DeleteOutlineIcon from "@mui/icons-material/DeleteOutlined";
618
+ import { Fragment, jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
619
+ import { createElement } from "react";
620
+ var MONO = '"JetBrains Mono", "Fira Code", monospace';
621
+ var POPPER_PROPS = { style: { zIndex: 1400 } };
622
+ var AGG_CATALOG = [
623
+ { group: "Basic", name: "count", hint: "Row count" },
624
+ { group: "Basic", name: "sum", hint: "Total" },
625
+ { group: "Basic", name: "avg", hint: "Arithmetic mean" },
626
+ { group: "Basic", name: "min", hint: "Minimum" },
627
+ { group: "Basic", name: "max", hint: "Maximum" },
628
+ { group: "Statistical", name: "median", hint: "50th percentile" },
629
+ { group: "Statistical", name: "stddev", hint: "Std deviation" },
630
+ { group: "Statistical", name: "var", hint: "Variance" },
631
+ { group: "Statistical", name: "percentile", hint: "p-th percentile (2-arg)" },
632
+ { group: "Distribution", name: "skew", hint: "Skewness" },
633
+ { group: "Distribution", name: "kurt", hint: "Excess kurtosis" },
634
+ { group: "Finance", name: "vwap", hint: "Vol-weighted avg price (2-arg)" },
635
+ { group: "Finance", name: "wavg", hint: "Weighted average (2-arg)" },
636
+ { group: "Finance", name: "drawdown", hint: "Max peak-to-trough decline" },
637
+ { group: "Ratios", name: "pct", hint: "Group % of total" },
638
+ { group: "Ratios", name: "sharpe", hint: "mean / stddev" },
639
+ { group: "Ratios", name: "calmar", hint: "mean / |max drawdown|" },
640
+ { group: "Ratios", name: "sortino", hint: "mean / downside dev" },
641
+ { group: "Ratios", name: "info_ratio", hint: "Information ratio (2-arg)" },
642
+ { group: "Counting", name: "distinct_count", hint: "Unique values" },
643
+ { group: "Counting", name: "sum_abs", hint: "Sum of |values|" },
644
+ { group: "Counting", name: "abs_sum", hint: "|Sum of values|" },
645
+ { group: "Counting", name: "first_value", hint: "First in group" },
646
+ { group: "Counting", name: "last_value", hint: "Last in group" }
647
+ ];
648
+ var AGG_NAMES_SET = new Set(AGG_CATALOG.map((a) => a.name));
649
+ var TWO_ARG_FNS = /* @__PURE__ */ new Set(["percentile", "vwap", "wavg", "info_ratio"]);
650
+ var AGG_RE = /^(\w+)\(\s*([^,)]*?)\s*(?:,\s*([^)]*?)\s*)?\)(?:\s+as\s+(\w+))?$/;
651
+ function parseAggExpr(expr) {
652
+ if (!expr.trim()) return { fn: "", field: "", field2: "", alias: "" };
653
+ const m = expr.trim().match(AGG_RE);
654
+ if (!m) return null;
655
+ const [, fn, field = "", field2 = "", alias = ""] = m;
656
+ if (!AGG_NAMES_SET.has(fn)) return null;
657
+ return { fn, field: field.trim(), field2: field2.trim(), alias: alias.trim() };
658
+ }
659
+ function serializeAgg(p) {
660
+ if (!p.fn) return "";
661
+ const args = p.field2 ? `${p.field}, ${p.field2}` : p.field;
662
+ const base = `${p.fn}(${args})`;
663
+ return p.alias ? `${base} as ${p.alias}` : base;
664
+ }
665
+ function AggregateExprRow({
666
+ value,
667
+ availableFields,
668
+ compact,
669
+ onChangeValue,
670
+ onDelete,
671
+ canDelete
672
+ }) {
673
+ const mono = { fontFamily: MONO, fontSize: compact ? "0.72rem" : "0.8rem" };
674
+ const parsed = parseAggExpr(value);
675
+ if (parsed === null) {
676
+ return /* @__PURE__ */ jsxs5(Stack4, { direction: "row", spacing: 0.5, sx: { alignItems: "center" }, children: [
677
+ /* @__PURE__ */ jsx6(
678
+ TextField,
679
+ {
680
+ size: "small",
681
+ fullWidth: true,
682
+ placeholder: "e.g. sum(revenue) as total",
683
+ value,
684
+ onChange: (e) => onChangeValue(e.target.value),
685
+ slotProps: { input: { sx: mono } }
686
+ }
687
+ ),
688
+ canDelete && /* @__PURE__ */ jsx6(IconButton2, { size: "small", sx: { p: 0.25 }, onClick: onDelete, children: /* @__PURE__ */ jsx6(DeleteOutlineIcon, { sx: { fontSize: compact ? 12 : 14 } }) })
689
+ ] });
690
+ }
691
+ const isTwoArg = TWO_ARG_FNS.has(parsed.fn);
692
+ const update = (patch) => {
693
+ const next = { ...parsed, ...patch };
694
+ if (patch.fn !== void 0 && !TWO_ARG_FNS.has(patch.fn)) {
695
+ next.field2 = "";
696
+ }
697
+ onChangeValue(serializeAgg(next));
698
+ };
699
+ return /* @__PURE__ */ jsxs5(Stack4, { direction: "row", spacing: 0.5, sx: { alignItems: "center", flexWrap: "wrap" }, children: [
700
+ /* @__PURE__ */ jsx6(
701
+ Autocomplete,
702
+ {
703
+ size: "small",
704
+ options: AGG_CATALOG,
705
+ groupBy: (opt) => opt.group,
706
+ getOptionLabel: (opt) => typeof opt === "string" ? opt : opt.name,
707
+ isOptionEqualToValue: (opt, val) => opt.name === (typeof val === "string" ? val : val.name),
708
+ value: AGG_CATALOG.find((a) => a.name === parsed.fn) ?? null,
709
+ onChange: (_, v) => update({ fn: v?.name ?? "" }),
710
+ slotProps: { popper: POPPER_PROPS },
711
+ renderOption: (props, opt) => /* @__PURE__ */ createElement(Box4, { component: "li", ...props, key: opt.name, sx: { display: "flex", gap: 1, alignItems: "center" } }, /* @__PURE__ */ jsx6(Typography3, { sx: { fontFamily: MONO, fontSize: "0.78rem", fontWeight: 600, color: "#82aaff", minWidth: 90 }, children: opt.name }), /* @__PURE__ */ jsx6(Typography3, { sx: { fontSize: "0.72rem", color: alpha5("#fff", 0.45) }, children: opt.hint })),
712
+ sx: { minWidth: compact ? 100 : 130, flex: "0 1 auto" },
713
+ renderInput: (p) => /* @__PURE__ */ jsx6(TextField, { ...p, placeholder: "fn", sx: { "& input": { ...mono, minWidth: "60px !important" } } })
714
+ }
715
+ ),
716
+ parsed.fn !== "" && parsed.fn !== "count" && /* @__PURE__ */ jsx6(
717
+ Autocomplete,
718
+ {
719
+ size: "small",
720
+ freeSolo: true,
721
+ options: availableFields,
722
+ value: parsed.field,
723
+ onInputChange: (_, v) => update({ field: v }),
724
+ sx: { minWidth: compact ? 80 : 100, flex: 1 },
725
+ slotProps: { popper: POPPER_PROPS },
726
+ renderInput: (p) => /* @__PURE__ */ jsx6(TextField, { ...p, placeholder: "field", sx: { "& input": mono } })
727
+ }
728
+ ),
729
+ isTwoArg && /* @__PURE__ */ jsx6(
730
+ Autocomplete,
731
+ {
732
+ size: "small",
733
+ freeSolo: true,
734
+ options: availableFields,
735
+ value: parsed.field2,
736
+ onInputChange: (_, v) => update({ field2: v }),
737
+ sx: { minWidth: compact ? 80 : 100, flex: 1 },
738
+ slotProps: { popper: POPPER_PROPS },
739
+ renderInput: (p) => /* @__PURE__ */ jsx6(TextField, { ...p, placeholder: "arg2", sx: { "& input": mono } })
740
+ }
741
+ ),
742
+ /* @__PURE__ */ jsx6(
743
+ TextField,
744
+ {
745
+ size: "small",
746
+ placeholder: "as",
747
+ value: parsed.alias,
748
+ onChange: (e) => update({ alias: e.target.value }),
749
+ sx: { width: compact ? 60 : 80 },
750
+ slotProps: { input: { sx: { ...mono, px: 0.5 } } }
751
+ }
752
+ ),
753
+ canDelete && /* @__PURE__ */ jsx6(IconButton2, { size: "small", sx: { p: 0.25 }, onClick: onDelete, children: /* @__PURE__ */ jsx6(DeleteOutlineIcon, { sx: { fontSize: compact ? 12 : 14 } }) })
754
+ ] });
755
+ }
756
+ function AggregateList({
757
+ aggregates,
758
+ availableFields,
759
+ compact,
760
+ btnSz,
761
+ onChange
762
+ }) {
763
+ return /* @__PURE__ */ jsxs5(Fragment, { children: [
764
+ aggregates.map((agg, i) => /* @__PURE__ */ jsx6(
765
+ AggregateExprRow,
766
+ {
767
+ value: agg,
768
+ availableFields,
769
+ compact,
770
+ canDelete: aggregates.length > 1,
771
+ onChangeValue: (v) => {
772
+ const next = [...aggregates];
773
+ next[i] = v;
774
+ onChange(next);
775
+ },
776
+ onDelete: () => onChange(aggregates.filter((_, j) => j !== i))
777
+ },
778
+ i
779
+ )),
780
+ /* @__PURE__ */ jsx6(
781
+ Button,
782
+ {
783
+ size: "small",
784
+ sx: { fontSize: btnSz, py: 0.25, alignSelf: "flex-start" },
785
+ onClick: () => onChange([...aggregates, ""]),
786
+ children: "+ agg"
787
+ }
788
+ )
789
+ ] });
790
+ }
791
+ function StepConfigForm({
792
+ step,
793
+ availableFields,
794
+ joinSources,
795
+ compact,
796
+ onChange
797
+ }) {
798
+ const sp = compact ? 0.5 : 1;
799
+ const mono = { fontFamily: MONO, fontSize: compact ? "0.72rem" : "0.8rem" };
800
+ const btnSz = compact ? "0.65rem" : "0.75rem";
801
+ switch (step.type) {
802
+ case "where":
803
+ return /* @__PURE__ */ jsx6(
804
+ TextField,
805
+ {
806
+ size: "small",
807
+ fullWidth: true,
808
+ placeholder: "e.g. price > 100",
809
+ value: step.config.condition,
810
+ onChange: (e) => onChange({ ...step, config: { condition: e.target.value } }),
811
+ slotProps: { input: { sx: mono } }
812
+ }
813
+ );
814
+ case "select":
815
+ return /* @__PURE__ */ jsxs5(Stack4, { spacing: sp, children: [
816
+ /* @__PURE__ */ jsx6(
817
+ Autocomplete,
818
+ {
819
+ multiple: true,
820
+ size: "small",
821
+ freeSolo: true,
822
+ options: availableFields,
823
+ value: step.config.fields,
824
+ onChange: (_, v) => onChange({ ...step, config: { ...step.config, fields: v } }),
825
+ slotProps: { popper: POPPER_PROPS },
826
+ renderInput: (p) => /* @__PURE__ */ jsx6(TextField, { ...p, placeholder: "Fields...", sx: { "& input": mono } })
827
+ }
828
+ ),
829
+ /* @__PURE__ */ jsx6(
830
+ AggregateList,
831
+ {
832
+ aggregates: step.config.expressions,
833
+ availableFields,
834
+ compact,
835
+ btnSz,
836
+ onChange: (expressions) => onChange({ ...step, config: { ...step.config, expressions } })
837
+ }
838
+ )
839
+ ] });
840
+ case "sort":
841
+ return /* @__PURE__ */ jsxs5(Stack4, { spacing: sp, children: [
842
+ step.config.criteria.map((c, i) => /* @__PURE__ */ jsxs5(Stack4, { direction: "row", spacing: 0.5, sx: { alignItems: "center" }, children: [
843
+ /* @__PURE__ */ jsx6(
844
+ Autocomplete,
845
+ {
846
+ size: "small",
847
+ freeSolo: true,
848
+ options: availableFields,
849
+ sx: { flex: 1 },
850
+ value: c.field,
851
+ slotProps: { popper: POPPER_PROPS },
852
+ onInputChange: (_, v) => {
853
+ const criteria = [...step.config.criteria];
854
+ criteria[i] = { ...criteria[i], field: v };
855
+ onChange({ ...step, config: { criteria } });
856
+ },
857
+ renderInput: (p) => /* @__PURE__ */ jsx6(TextField, { ...p, placeholder: "Field", sx: { "& input": mono } })
858
+ }
859
+ ),
860
+ /* @__PURE__ */ jsxs5(
861
+ ToggleButtonGroup,
862
+ {
863
+ size: "small",
864
+ exclusive: true,
865
+ value: c.direction,
866
+ onChange: (_, v) => {
867
+ if (!v) return;
868
+ const criteria = [...step.config.criteria];
869
+ criteria[i] = { ...criteria[i], direction: v };
870
+ onChange({ ...step, config: { criteria } });
871
+ },
872
+ children: [
873
+ /* @__PURE__ */ jsx6(ToggleButton, { value: "asc", sx: { px: 0.75, fontSize: compact ? "0.6rem" : "0.7rem" }, children: "ASC" }),
874
+ /* @__PURE__ */ jsx6(ToggleButton, { value: "desc", sx: { px: 0.75, fontSize: compact ? "0.6rem" : "0.7rem" }, children: "DESC" })
875
+ ]
876
+ }
877
+ ),
878
+ step.config.criteria.length > 1 && /* @__PURE__ */ jsx6(IconButton2, { size: "small", sx: { p: 0.25 }, onClick: () => {
879
+ const criteria = step.config.criteria.filter((_, j) => j !== i);
880
+ onChange({ ...step, config: { criteria } });
881
+ }, children: /* @__PURE__ */ jsx6(DeleteOutlineIcon, { sx: { fontSize: compact ? 12 : 14 } }) })
882
+ ] }, i)),
883
+ /* @__PURE__ */ jsx6(
884
+ Button,
885
+ {
886
+ size: "small",
887
+ sx: { fontSize: btnSz, py: 0.25, alignSelf: "flex-start" },
888
+ onClick: () => onChange({ ...step, config: { criteria: [...step.config.criteria, { field: "", direction: "asc" }] } }),
889
+ children: "+ criterion"
890
+ }
891
+ )
892
+ ] });
893
+ case "groupBy":
894
+ return /* @__PURE__ */ jsx6(
895
+ Autocomplete,
896
+ {
897
+ multiple: true,
898
+ size: "small",
899
+ freeSolo: true,
900
+ options: availableFields,
901
+ value: step.config.fields,
902
+ onChange: (_, v) => onChange({ ...step, config: { fields: v } }),
903
+ slotProps: { popper: POPPER_PROPS },
904
+ renderInput: (p) => /* @__PURE__ */ jsx6(TextField, { ...p, placeholder: "Fields to group by...", sx: { "& input": mono } })
905
+ }
906
+ );
907
+ case "join":
908
+ return /* @__PURE__ */ jsxs5(Stack4, { spacing: sp, children: [
909
+ /* @__PURE__ */ jsxs5(FormControl2, { size: "small", fullWidth: true, children: [
910
+ /* @__PURE__ */ jsx6(InputLabel2, { children: "Right source" }),
911
+ /* @__PURE__ */ jsx6(
912
+ Select3,
913
+ {
914
+ value: step.config.rightSource,
915
+ label: "Right source",
916
+ onChange: (e) => onChange({ ...step, config: { ...step.config, rightSource: e.target.value } }),
917
+ MenuProps: { style: { zIndex: 1400 } },
918
+ children: joinSources.map((s) => /* @__PURE__ */ jsx6(MenuItem3, { value: s, children: s }, s))
919
+ }
920
+ )
921
+ ] }),
922
+ /* @__PURE__ */ jsx6(
923
+ TextField,
924
+ {
925
+ size: "small",
926
+ fullWidth: true,
927
+ placeholder: "e.g. customerId == id",
928
+ value: step.config.condition,
929
+ onChange: (e) => onChange({ ...step, config: { ...step.config, condition: e.target.value } }),
930
+ slotProps: { input: { sx: mono } }
931
+ }
932
+ )
933
+ ] });
934
+ case "first":
935
+ case "last":
936
+ return /* @__PURE__ */ jsx6(
937
+ TextField,
938
+ {
939
+ size: "small",
940
+ type: "number",
941
+ fullWidth: true,
942
+ value: step.config.count,
943
+ onChange: (e) => onChange({ ...step, config: { count: Number(e.target.value) || 1 } }),
944
+ slotProps: { input: { sx: mono } }
945
+ }
946
+ );
947
+ case "distinct":
948
+ return /* @__PURE__ */ jsx6(
949
+ Autocomplete,
950
+ {
951
+ multiple: true,
952
+ size: "small",
953
+ freeSolo: true,
954
+ options: availableFields,
955
+ value: step.config.fields,
956
+ onChange: (_, v) => onChange({ ...step, config: { fields: v } }),
957
+ slotProps: { popper: POPPER_PROPS },
958
+ renderInput: (p) => /* @__PURE__ */ jsx6(TextField, { ...p, placeholder: "Fields (empty = all)...", sx: { "& input": mono } })
959
+ }
960
+ );
961
+ case "map":
962
+ return /* @__PURE__ */ jsxs5(Stack4, { spacing: sp, children: [
963
+ step.config.expressions.map((expr, i) => /* @__PURE__ */ jsx6(
964
+ TextField,
965
+ {
966
+ size: "small",
967
+ fullWidth: true,
968
+ placeholder: "e.g. price * 1.1 as priceWithTax",
969
+ value: expr,
970
+ onChange: (e) => {
971
+ const expressions = [...step.config.expressions];
972
+ expressions[i] = e.target.value;
973
+ onChange({ ...step, config: { expressions } });
974
+ },
975
+ slotProps: { input: { sx: mono } }
976
+ },
977
+ i
978
+ )),
979
+ /* @__PURE__ */ jsx6(
980
+ Button,
981
+ {
982
+ size: "small",
983
+ sx: { fontSize: btnSz, py: 0.25, alignSelf: "flex-start" },
984
+ onClick: () => onChange({ ...step, config: { expressions: [...step.config.expressions, ""] } }),
985
+ children: "+ expr"
986
+ }
987
+ )
988
+ ] });
989
+ case "reduce":
990
+ return /* @__PURE__ */ jsxs5(Stack4, { spacing: sp, children: [
991
+ /* @__PURE__ */ jsx6(
992
+ TextField,
993
+ {
994
+ size: "small",
995
+ fullWidth: true,
996
+ label: "Initial",
997
+ placeholder: "e.g. 0",
998
+ value: step.config.initial,
999
+ onChange: (e) => onChange({ ...step, config: { ...step.config, initial: e.target.value } }),
1000
+ slotProps: { input: { sx: mono } }
1001
+ }
1002
+ ),
1003
+ /* @__PURE__ */ jsx6(
1004
+ TextField,
1005
+ {
1006
+ size: "small",
1007
+ fullWidth: true,
1008
+ label: "Accumulator",
1009
+ placeholder: "e.g. _acc + price",
1010
+ value: step.config.accumulator,
1011
+ onChange: (e) => onChange({ ...step, config: { ...step.config, accumulator: e.target.value } }),
1012
+ slotProps: { input: { sx: mono } }
1013
+ }
1014
+ )
1015
+ ] });
1016
+ case "rollup":
1017
+ return /* @__PURE__ */ jsxs5(Stack4, { spacing: sp, children: [
1018
+ /* @__PURE__ */ jsx6(
1019
+ Autocomplete,
1020
+ {
1021
+ multiple: true,
1022
+ size: "small",
1023
+ freeSolo: true,
1024
+ options: availableFields,
1025
+ value: step.config.keys,
1026
+ onChange: (_, v) => onChange({ ...step, config: { ...step.config, keys: v } }),
1027
+ slotProps: { popper: POPPER_PROPS },
1028
+ renderInput: (p) => /* @__PURE__ */ jsx6(TextField, { ...p, placeholder: "Group keys...", sx: { "& input": mono } })
1029
+ }
1030
+ ),
1031
+ /* @__PURE__ */ jsx6(
1032
+ AggregateList,
1033
+ {
1034
+ aggregates: step.config.aggregates,
1035
+ availableFields,
1036
+ compact,
1037
+ btnSz,
1038
+ onChange: (aggregates) => onChange({ ...step, config: { ...step.config, aggregates } })
1039
+ }
1040
+ )
1041
+ ] });
1042
+ case "pivot":
1043
+ return /* @__PURE__ */ jsxs5(Stack4, { spacing: sp, children: [
1044
+ /* @__PURE__ */ jsx6(
1045
+ Autocomplete,
1046
+ {
1047
+ size: "small",
1048
+ freeSolo: true,
1049
+ options: availableFields,
1050
+ value: step.config.pivotField,
1051
+ onInputChange: (_, v) => onChange({ ...step, config: { ...step.config, pivotField: v } }),
1052
+ slotProps: { popper: POPPER_PROPS },
1053
+ renderInput: (p) => /* @__PURE__ */ jsx6(TextField, { ...p, placeholder: "Pivot field...", sx: { "& input": mono } })
1054
+ }
1055
+ ),
1056
+ /* @__PURE__ */ jsx6(
1057
+ AggregateList,
1058
+ {
1059
+ aggregates: step.config.aggregates,
1060
+ availableFields,
1061
+ compact,
1062
+ btnSz,
1063
+ onChange: (aggregates) => onChange({ ...step, config: { ...step.config, aggregates } })
1064
+ }
1065
+ )
1066
+ ] });
1067
+ case "flatten":
1068
+ return /* @__PURE__ */ jsx6(
1069
+ Autocomplete,
1070
+ {
1071
+ size: "small",
1072
+ freeSolo: true,
1073
+ options: availableFields,
1074
+ value: step.config.field,
1075
+ onInputChange: (_, v) => onChange({ ...step, config: { field: v } }),
1076
+ slotProps: { popper: POPPER_PROPS },
1077
+ renderInput: (p) => /* @__PURE__ */ jsx6(TextField, { ...p, placeholder: "Field (optional)...", sx: { "& input": mono } })
1078
+ }
1079
+ );
1080
+ case "transpose":
1081
+ return /* @__PURE__ */ jsx6(
1082
+ Autocomplete,
1083
+ {
1084
+ size: "small",
1085
+ freeSolo: true,
1086
+ options: availableFields,
1087
+ value: step.config.headerField,
1088
+ onInputChange: (_, v) => onChange({ ...step, config: { headerField: v } }),
1089
+ slotProps: { popper: POPPER_PROPS },
1090
+ renderInput: (p) => /* @__PURE__ */ jsx6(TextField, { ...p, placeholder: "Header field...", sx: { "& input": mono } })
1091
+ }
1092
+ );
1093
+ }
1094
+ }
1095
+
1096
+ // src/react/StepCard.tsx
1097
+ import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
1098
+ function StepCard({
1099
+ step,
1100
+ isFirst,
1101
+ isLast,
1102
+ expanded,
1103
+ onToggleExpand,
1104
+ availableFields,
1105
+ joinSources,
1106
+ onUpdate,
1107
+ onRemove,
1108
+ onMoveUp,
1109
+ onMoveDown,
1110
+ orientation,
1111
+ compact
1112
+ }) {
1113
+ const meta = OPERATION_META[step.step.type];
1114
+ const summary = getStepSummary(step.step);
1115
+ const isH = orientation === "horizontal";
1116
+ const iconSz = compact ? 14 : 16;
1117
+ const actionIconSz = compact ? 12 : 14;
1118
+ const PrevIcon = isH ? ArrowBackIcon : ArrowUpwardIcon;
1119
+ const NextIcon = isH ? ArrowForwardIcon : ArrowDownwardIcon;
1120
+ return /* @__PURE__ */ jsxs6(
1121
+ Paper3,
1122
+ {
1123
+ variant: "outlined",
1124
+ sx: {
1125
+ overflow: "hidden",
1126
+ ...isH ? {
1127
+ borderTop: "3px solid",
1128
+ borderTopColor: meta.color,
1129
+ borderRadius: "8px 8px 4px 4px",
1130
+ width: compact ? expanded ? 240 : 140 : expanded ? 280 : 200,
1131
+ flexShrink: 0,
1132
+ alignSelf: "stretch",
1133
+ transition: "box-shadow 0.2s, width 0.25s ease"
1134
+ } : {
1135
+ borderLeft: "4px solid",
1136
+ borderLeftColor: meta.color,
1137
+ borderRadius: "4px 8px 8px 4px",
1138
+ transition: "box-shadow 0.2s"
1139
+ },
1140
+ "&:hover": {
1141
+ boxShadow: `0 0 0 1px ${alpha6(meta.color, 0.25)}`
1142
+ },
1143
+ "& .step-actions": {
1144
+ opacity: 0,
1145
+ transition: "opacity 0.15s"
1146
+ },
1147
+ "&:hover .step-actions": {
1148
+ opacity: 1
1149
+ }
1150
+ },
1151
+ children: [
1152
+ /* @__PURE__ */ jsxs6(
1153
+ Stack5,
1154
+ {
1155
+ direction: "row",
1156
+ spacing: 0.5,
1157
+ onClick: onToggleExpand,
1158
+ sx: {
1159
+ alignItems: "center",
1160
+ px: compact ? 0.75 : 1.25,
1161
+ py: compact ? 0.375 : 0.75,
1162
+ cursor: "pointer",
1163
+ "&:hover": { bgcolor: alpha6(meta.color, 0.04) }
1164
+ },
1165
+ children: [
1166
+ /* @__PURE__ */ jsx7(Box5, { sx: { color: meta.color, display: "flex", alignItems: "center", flexShrink: 0 }, children: /* @__PURE__ */ jsx7(OperationIcon, { type: step.step.type, size: iconSz }) }),
1167
+ !compact && /* @__PURE__ */ jsx7(Typography4, { sx: {
1168
+ fontWeight: 600,
1169
+ fontSize: "0.75rem",
1170
+ whiteSpace: "nowrap"
1171
+ }, children: meta.shortLabel }),
1172
+ !expanded && /* @__PURE__ */ jsx7(
1173
+ Typography4,
1174
+ {
1175
+ sx: {
1176
+ color: "text.secondary",
1177
+ fontFamily: '"JetBrains Mono", monospace',
1178
+ fontSize: compact ? "0.62rem" : "0.68rem",
1179
+ overflow: "hidden",
1180
+ textOverflow: "ellipsis",
1181
+ whiteSpace: "nowrap",
1182
+ flex: 1,
1183
+ minWidth: 0
1184
+ },
1185
+ children: compact ? `${meta.shortLabel}: ${summary}` : summary
1186
+ }
1187
+ ),
1188
+ expanded && /* @__PURE__ */ jsx7(Box5, { sx: { flex: 1 } }),
1189
+ /* @__PURE__ */ jsxs6(
1190
+ Stack5,
1191
+ {
1192
+ direction: "row",
1193
+ spacing: 0,
1194
+ className: "step-actions",
1195
+ onClick: (e) => e.stopPropagation(),
1196
+ sx: { flexShrink: 0 },
1197
+ children: [
1198
+ /* @__PURE__ */ jsx7(IconButton3, { size: "small", onClick: onMoveUp, disabled: isFirst, sx: { p: compact ? 0.25 : 0.375 }, children: /* @__PURE__ */ jsx7(PrevIcon, { sx: { fontSize: actionIconSz } }) }),
1199
+ /* @__PURE__ */ jsx7(IconButton3, { size: "small", onClick: onMoveDown, disabled: isLast, sx: { p: compact ? 0.25 : 0.375 }, children: /* @__PURE__ */ jsx7(NextIcon, { sx: { fontSize: actionIconSz } }) }),
1200
+ /* @__PURE__ */ jsx7(IconButton3, { size: "small", onClick: onRemove, sx: { p: compact ? 0.25 : 0.375, color: "error.main" }, children: /* @__PURE__ */ jsx7(DeleteOutlineIcon2, { sx: { fontSize: actionIconSz } }) })
1201
+ ]
1202
+ }
1203
+ ),
1204
+ /* @__PURE__ */ jsx7(ExpandMoreIcon, { sx: {
1205
+ fontSize: compact ? 14 : 18,
1206
+ color: "text.secondary",
1207
+ transition: "transform 0.2s",
1208
+ transform: expanded ? "rotate(180deg)" : "rotate(0deg)",
1209
+ flexShrink: 0
1210
+ } })
1211
+ ]
1212
+ }
1213
+ ),
1214
+ /* @__PURE__ */ jsx7(Collapse, { in: expanded, timeout: 200, children: /* @__PURE__ */ jsx7(Box5, { sx: { px: compact ? 0.75 : 1.25, pb: compact ? 0.75 : 1.25 }, children: /* @__PURE__ */ jsx7(
1215
+ StepConfigForm,
1216
+ {
1217
+ step: step.step,
1218
+ availableFields,
1219
+ joinSources,
1220
+ compact,
1221
+ onChange: onUpdate
1222
+ }
1223
+ ) }) })
1224
+ ]
1225
+ }
1226
+ );
1227
+ }
1228
+
1229
+ // src/react/PipeQueryBuilder.tsx
1230
+ import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
1231
+ function PipeQueryBuilder({
1232
+ orientation,
1233
+ source,
1234
+ onSourceChange,
1235
+ availableSources,
1236
+ availableFields,
1237
+ onQueryChange,
1238
+ compact = false,
1239
+ maxSteps,
1240
+ initialSteps,
1241
+ showResult = true,
1242
+ joinSources = [],
1243
+ rowCount
1244
+ }) {
1245
+ const [steps, setSteps] = useState3(() => initialSteps ?? []);
1246
+ const [expandedId, setExpandedId] = useState3(null);
1247
+ const nextId = useRef(1);
1248
+ const isH = orientation === "horizontal";
1249
+ const generatedQuery = useMemo(
1250
+ () => generateQuery(source, steps),
1251
+ [source, steps]
1252
+ );
1253
+ useEffect(() => {
1254
+ onQueryChange(generatedQuery);
1255
+ }, [generatedQuery, onQueryChange]);
1256
+ const insertStepAt = useCallback((index, type) => {
1257
+ if (maxSteps != null && steps.length >= maxSteps) return;
1258
+ const newId = `pq_${nextId.current++}`;
1259
+ setSteps((prev) => {
1260
+ const next = [...prev];
1261
+ next.splice(index, 0, { id: newId, step: createDefaultConfig(type) });
1262
+ return next;
1263
+ });
1264
+ setExpandedId(newId);
1265
+ }, [maxSteps, steps.length]);
1266
+ const removeStep = useCallback((id) => {
1267
+ setSteps((prev) => prev.filter((s) => s.id !== id));
1268
+ setExpandedId((prev) => prev === id ? null : prev);
1269
+ }, []);
1270
+ const moveStep = useCallback((id, direction) => {
1271
+ setSteps((prev) => {
1272
+ const idx = prev.findIndex((s) => s.id === id);
1273
+ if (idx < 0) return prev;
1274
+ const newIdx = direction === "prev" ? idx - 1 : idx + 1;
1275
+ if (newIdx < 0 || newIdx >= prev.length) return prev;
1276
+ const next = [...prev];
1277
+ [next[idx], next[newIdx]] = [next[newIdx], next[idx]];
1278
+ return next;
1279
+ });
1280
+ }, []);
1281
+ const updateStep = useCallback((id, newStep) => {
1282
+ setSteps((prev) => prev.map((s) => s.id === id ? { ...s, step: newStep } : s));
1283
+ }, []);
1284
+ return /* @__PURE__ */ jsx8(Box6, { sx: {
1285
+ display: "flex",
1286
+ flexDirection: isH ? "row" : "column",
1287
+ alignItems: isH ? "stretch" : "center",
1288
+ overflowX: isH ? "auto" : "visible",
1289
+ overflowY: isH ? "visible" : "auto",
1290
+ flex: 1,
1291
+ py: compact ? 0.5 : 1,
1292
+ gap: 0,
1293
+ "&::-webkit-scrollbar": { height: 4 },
1294
+ "&::-webkit-scrollbar-thumb": { bgcolor: "rgba(255,255,255,0.15)", borderRadius: 2 }
1295
+ }, children: /* @__PURE__ */ jsxs7(Box6, { sx: {
1296
+ display: "flex",
1297
+ flexDirection: isH ? "row" : "column",
1298
+ alignItems: isH ? "stretch" : "stretch",
1299
+ ...isH ? {} : { width: "100%", maxWidth: compact ? 360 : 520 }
1300
+ }, children: [
1301
+ /* @__PURE__ */ jsx8(
1302
+ SourceNode,
1303
+ {
1304
+ source,
1305
+ onSourceChange,
1306
+ availableSources,
1307
+ rowCount,
1308
+ orientation,
1309
+ compact
1310
+ }
1311
+ ),
1312
+ /* @__PURE__ */ jsx8(
1313
+ PipelineConnector,
1314
+ {
1315
+ orientation,
1316
+ compact,
1317
+ onInsert: (type) => insertStepAt(0, type)
1318
+ }
1319
+ ),
1320
+ steps.map((step, idx) => /* @__PURE__ */ jsxs7(
1321
+ Box6,
1322
+ {
1323
+ sx: {
1324
+ display: "flex",
1325
+ flexDirection: isH ? "row" : "column",
1326
+ alignItems: isH ? "stretch" : "stretch"
1327
+ },
1328
+ children: [
1329
+ /* @__PURE__ */ jsx8(
1330
+ StepCard,
1331
+ {
1332
+ step,
1333
+ isFirst: idx === 0,
1334
+ isLast: idx === steps.length - 1,
1335
+ expanded: expandedId === step.id,
1336
+ onToggleExpand: () => setExpandedId(expandedId === step.id ? null : step.id),
1337
+ availableFields,
1338
+ joinSources,
1339
+ onUpdate: (newStep) => updateStep(step.id, newStep),
1340
+ onRemove: () => removeStep(step.id),
1341
+ onMoveUp: () => moveStep(step.id, "prev"),
1342
+ onMoveDown: () => moveStep(step.id, "next"),
1343
+ orientation,
1344
+ compact
1345
+ }
1346
+ ),
1347
+ /* @__PURE__ */ jsx8(
1348
+ PipelineConnector,
1349
+ {
1350
+ orientation,
1351
+ compact,
1352
+ onInsert: (type) => insertStepAt(idx + 1, type)
1353
+ }
1354
+ )
1355
+ ]
1356
+ },
1357
+ step.id
1358
+ )),
1359
+ showResult && /* @__PURE__ */ jsx8(
1360
+ ResultNode,
1361
+ {
1362
+ query: generatedQuery,
1363
+ orientation,
1364
+ compact
1365
+ }
1366
+ )
1367
+ ] }) });
1368
+ }
1369
+ export {
1370
+ PipeQueryBuilder,
1371
+ createDefaultConfig,
1372
+ generateQuery,
1373
+ getStepSummary
1374
+ };