@bitcobblers/wod-wiki-ui 0.48.2175 → 0.49.2177

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/index.cjs CHANGED
@@ -2555,7 +2555,7 @@ function InlineClauseEditor({
2555
2555
  const meta = getClauseMeta(clause2.type);
2556
2556
  const current = clause2.value.trim();
2557
2557
  const typedIdx = filteredItems.length;
2558
- const shownValues = selectedValues.length > 0 ? selectedValues : !isMulti && current ? [current] : [];
2558
+ const shownValues = selectedValues;
2559
2559
  const reorder = (i, dir) => {
2560
2560
  if (!onReorder) return;
2561
2561
  const to = i + dir;
@@ -2567,24 +2567,15 @@ function InlineClauseEditor({
2567
2567
  return /* @__PURE__ */ jsxRuntime.jsxs(
2568
2568
  "div",
2569
2569
  {
2570
- className: "mx-0.5 rounded-xl border border-border bg-popover shadow-md p-1",
2570
+ className: "min-w-0 pt-0.5",
2571
2571
  "data-testid": "wql-clause-editor",
2572
2572
  "data-clause-type": clause2.type,
2573
2573
  children: [
2574
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 px-2 pt-1 pb-1.5", children: [
2575
- /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": true, className: "text-[11px]", children: meta.icon }),
2576
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs sm:text-[11px] font-semibold text-foreground font-mono", children: meta.label }),
2577
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "hidden sm:block ml-auto text-[10px] text-muted-foreground/70 font-mono truncate", children: [
2578
- "\u2191\u2193 move \xB7 Enter ",
2579
- isMulti ? "add/remove" : "set",
2580
- " \xB7 Esc done \xB7 Tab exits"
2581
- ] })
2582
- ] }),
2583
- shownValues.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("px-2 pb-1.5", isMulti && "border-b border-border/50"), children: [
2574
+ isMulti && shownValues.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "border-b border-border/50 px-2 pb-1.5", children: [
2584
2575
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap gap-1.5 sm:gap-1 max-h-24 overflow-y-auto overscroll-contain touch-pan-y sm:max-h-none sm:overflow-visible", children: shownValues.map((v, i) => /* @__PURE__ */ jsxRuntime.jsxs(
2585
2576
  "span",
2586
2577
  {
2587
- className: "inline-flex items-center gap-1 px-1.5 py-0.5 rounded bg-primary/20 text-primary text-xs sm:text-[10px] font-mono min-h-11 sm:min-h-0",
2578
+ className: "inline-flex items-center gap-1 px-1.5 py-0.5 rounded bg-primary/15 text-primary text-xs sm:text-[10px] font-mono min-h-8 sm:min-h-0",
2588
2579
  children: [
2589
2580
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "max-w-40 truncate", children: v }),
2590
2581
  onReorder && shownValues.length > 1 && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
@@ -2634,7 +2625,7 @@ function InlineClauseEditor({
2634
2625
  role: "listbox",
2635
2626
  "aria-label": `${meta.label} options`,
2636
2627
  "aria-multiselectable": isMulti || void 0,
2637
- className: "max-h-[min(45svh,20rem)] sm:max-h-44 overflow-y-auto overscroll-contain touch-pan-y flex flex-col gap-0.5",
2628
+ className: "max-h-[min(38svh,16rem)] sm:max-h-36 overflow-y-auto overscroll-contain touch-pan-y flex flex-col gap-0.5 pt-1",
2638
2629
  children: [
2639
2630
  canCommitTyped && /* @__PURE__ */ jsxRuntime.jsxs(
2640
2631
  "button",
@@ -2648,19 +2639,19 @@ function InlineClauseEditor({
2648
2639
  onClick: () => onCommitTyped(typedValue),
2649
2640
  "data-testid": `clause-commit-typed-${clause2.type}`,
2650
2641
  className: cn(
2651
- "flex items-center justify-between px-2 py-2 sm:py-1 text-sm sm:text-xs text-left rounded hover:bg-muted/60 transition-colors font-mono min-h-12 sm:min-h-0",
2652
- highlightIdx === typedIdx && "bg-muted/60"
2642
+ "flex items-center justify-between px-2.5 py-2 sm:py-1.5 text-sm sm:text-xs text-left rounded-md hover:bg-muted/60 transition-colors font-mono min-h-11 sm:min-h-0",
2643
+ highlightIdx === typedIdx && "bg-muted/70 ring-1 ring-border"
2653
2644
  ),
2654
2645
  children: [
2655
2646
  /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "truncate", children: [
2656
2647
  "Search for ",
2657
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-semibold", children: [
2648
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-semibold text-foreground", children: [
2658
2649
  "\u201C",
2659
2650
  typedValue,
2660
2651
  "\u201D"
2661
2652
  ] })
2662
2653
  ] }),
2663
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground/60 ml-1 shrink-0", children: "\u21B5" })
2654
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground/60 ml-1 shrink-0 font-sans text-xs", children: "\u21B5" })
2664
2655
  ]
2665
2656
  }
2666
2657
  ),
@@ -2679,29 +2670,39 @@ function InlineClauseEditor({
2679
2670
  onMouseEnter: () => onHighlight(idx),
2680
2671
  onClick: () => onCommitValue(item.value),
2681
2672
  className: cn(
2682
- "flex items-center justify-between px-2 py-2 sm:py-1 text-sm sm:text-xs text-left rounded transition-colors font-mono min-h-12 sm:min-h-0",
2683
- active ? "bg-primary/15 font-semibold" : "hover:bg-muted/60",
2684
- selected && "text-primary"
2673
+ "flex items-center justify-between px-2.5 py-2 sm:py-1.5 text-sm sm:text-xs text-left rounded-md transition-colors min-h-11 sm:min-h-0",
2674
+ active ? "bg-primary/10 text-foreground font-medium" : "hover:bg-muted/60 text-muted-foreground hover:text-foreground",
2675
+ selected && "text-primary font-medium"
2685
2676
  ),
2686
2677
  children: [
2687
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: item.label }),
2688
- selected && /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { className: "w-3 h-3 shrink-0" })
2678
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-baseline justify-between gap-2 min-w-0 flex-1", children: [
2679
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: item.label }),
2680
+ item.label !== item.value && /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true", className: "font-mono text-[11px] text-muted-foreground/70 shrink-0", children: item.value })
2681
+ ] }),
2682
+ selected && /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { className: "w-3.5 h-3.5 text-primary shrink-0 ml-1.5" })
2689
2683
  ]
2690
2684
  },
2691
2685
  item.value
2692
2686
  );
2693
2687
  }),
2694
- emptyText && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-2 py-2 sm:py-1.5 text-sm sm:text-xs text-muted-foreground italic", children: emptyText })
2688
+ emptyText && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-2.5 py-2 text-sm sm:text-xs text-muted-foreground italic", children: emptyText })
2695
2689
  ]
2696
2690
  }
2697
2691
  ),
2698
- isMulti && onDone && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "sticky bottom-0 -mx-1 -mb-1 px-3 pt-1.5 pb-[max(0.375rem,env(safe-area-inset-bottom))] bg-popover rounded-b-xl flex justify-end", children: /* @__PURE__ */ jsxRuntime.jsxs(
2692
+ (filteredItems.length > 0 || canCommitTyped) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "hidden sm:flex items-center justify-between px-2 pt-2 text-[10px] text-muted-foreground/70 font-mono border-t border-border/40 mt-1", children: [
2693
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
2694
+ "\u2191\u2193 choose \xB7 Enter ",
2695
+ isMulti ? "toggle" : "set"
2696
+ ] }),
2697
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Tab next segment \xB7 Esc close" })
2698
+ ] }),
2699
+ isMulti && onDone && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pt-2 flex justify-end", children: /* @__PURE__ */ jsxRuntime.jsxs(
2699
2700
  "button",
2700
2701
  {
2701
2702
  type: "button",
2702
2703
  onClick: onDone,
2703
2704
  "data-testid": "wql-clause-editor-done",
2704
- className: "inline-flex w-full sm:w-auto items-center justify-center min-h-12 sm:min-h-9 px-3 rounded-lg bg-primary text-primary-foreground text-sm sm:text-xs font-semibold hover:bg-primary/90 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
2705
+ className: "inline-flex w-full sm:w-auto items-center justify-center min-h-10 sm:min-h-7 px-3 rounded-md bg-primary text-primary-foreground text-xs font-semibold hover:bg-primary/90 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
2705
2706
  children: [
2706
2707
  "Done",
2707
2708
  shownValues.length > 0 ? ` \xB7 ${shownValues.length} selected` : ""
@@ -2740,7 +2741,11 @@ var STATIC_OPTIONS = {
2740
2741
  groupby: GROUPBY_OPTIONS,
2741
2742
  metric: METRIC_OPTIONS,
2742
2743
  unit: UNIT_OPTIONS,
2743
- intensity: wodWikiWql.WQL_INTENSITY_TIERS.map((v) => ({ value: v, label: v }))
2744
+ intensity: wodWikiWql.WQL_INTENSITY_TIERS.map((v) => ({ value: v, label: v })),
2745
+ subscribed: [
2746
+ { value: "true", label: "Subscribed" },
2747
+ { value: "false", label: "All / unsubscribed" }
2748
+ ]
2744
2749
  };
2745
2750
  function tagsFromStaticBlocks(blocks) {
2746
2751
  const set = /* @__PURE__ */ new Set();
@@ -3038,7 +3043,7 @@ function WqlDiagnosticsStrip({
3038
3043
  "aria-live": "polite",
3039
3044
  className: cn(
3040
3045
  "flex flex-wrap items-center justify-between gap-x-4 gap-y-1.5 px-3 py-1.5 text-xs font-mono transition-colors",
3041
- header ? valid ? "border-b border-primary/30 text-muted-foreground" : "border-b border-destructive/50 bg-destructive/5 text-destructive" : cn(
3046
+ header ? valid ? "border-b border-border/60 bg-muted/20 text-muted-foreground" : "border-b border-destructive/40 bg-destructive/5 text-destructive" : cn(
3042
3047
  "rounded-lg border",
3043
3048
  valid ? "bg-muted/30 border-border text-muted-foreground" : "bg-destructive/5 border-destructive/30 text-destructive"
3044
3049
  ),
@@ -3432,23 +3437,128 @@ var completionIcons = {
3432
3437
  value: server.renderToStaticMarkup(React2.createElement(lucideReact.Tag, { size: 16 }))
3433
3438
  };
3434
3439
  var SLOT_NODES = { Head: true, Filter: true, TagValue: true, Rollup: true };
3440
+ var activeSegmentEffect = state.StateEffect.define();
3441
+ var activeSegmentField = state.StateField.define({
3442
+ create: () => view.Decoration.none,
3443
+ update(decorations, transaction) {
3444
+ const mapped = decorations.map(transaction.changes);
3445
+ for (const effect of transaction.effects) {
3446
+ if (!effect.is(activeSegmentEffect)) continue;
3447
+ const range = effect.value;
3448
+ if (!range || range.from >= range.to) return view.Decoration.none;
3449
+ return view.Decoration.set([view.Decoration.mark({ class: "wql-active-segment" }).range(range.from, range.to)]);
3450
+ }
3451
+ return mapped;
3452
+ },
3453
+ provide: (field) => view.EditorView.decorations.from(field)
3454
+ });
3455
+ function findClauseRange(state, clause2) {
3456
+ if (!clause2) return void 0;
3457
+ let range;
3458
+ const tree = language.syntaxTree(state);
3459
+ if (clause2.filterIndex !== void 0) {
3460
+ let index = 0;
3461
+ tree.iterate({
3462
+ enter: (node) => {
3463
+ if (node.name === "Filter") {
3464
+ if (index === clause2.filterIndex) {
3465
+ range = { from: node.from, to: node.to };
3466
+ return false;
3467
+ }
3468
+ index++;
3469
+ }
3470
+ return true;
3471
+ }
3472
+ });
3473
+ return range;
3474
+ }
3475
+ if (["kind", "target", "agg", "metric"].includes(clause2.type)) {
3476
+ tree.iterate({
3477
+ enter: (node) => {
3478
+ if (node.name === "Head") {
3479
+ range = { from: node.from, to: node.to };
3480
+ return false;
3481
+ }
3482
+ return true;
3483
+ }
3484
+ });
3485
+ return range;
3486
+ }
3487
+ if (clause2.type === "groupby") {
3488
+ tree.iterate({
3489
+ enter: (node) => {
3490
+ if (node.name === "GroupBy") {
3491
+ range = { from: node.from, to: node.to };
3492
+ return false;
3493
+ }
3494
+ return true;
3495
+ }
3496
+ });
3497
+ return range;
3498
+ }
3499
+ if (clause2.type === "rollup") {
3500
+ tree.iterate({
3501
+ enter: (node) => {
3502
+ if (node.name === "Rollup") {
3503
+ range = { from: node.from, to: node.to };
3504
+ return false;
3505
+ }
3506
+ return true;
3507
+ }
3508
+ });
3509
+ return range;
3510
+ }
3511
+ if (clause2.type === "time") {
3512
+ const doc = state.doc.toString();
3513
+ const match = /\b(last\s+\d+[dw]|from\s+\S+(?:\s+to\s+\S+)?)/i.exec(doc);
3514
+ if (match) return { from: match.index, to: match.index + match[0].length };
3515
+ }
3516
+ return void 0;
3517
+ }
3435
3518
  function slotPositions(state) {
3436
3519
  const slots = [];
3437
3520
  language.syntaxTree(state).iterate({
3438
3521
  enter: (node) => {
3439
3522
  if (SLOT_NODES[node.name]) slots.push(node.from);
3440
- else if (node.name === "Word" && node.node.parent?.name === "GroupBy") slots.push(node.from);
3523
+ else if (node.name === "Word" && ["GroupBy", "Dimension"].includes(node.node.parent?.name ?? "")) slots.push(node.from);
3441
3524
  return true;
3442
3525
  }
3443
3526
  });
3444
3527
  return slots.sort((a, b) => a - b);
3445
3528
  }
3446
- function advanceSlot(view, dir) {
3529
+ function activateSlot(view, position, onActivate) {
3530
+ if (!onActivate) return;
3531
+ let node = language.syntaxTree(view.state).resolveInner(position, 1);
3532
+ while (node.parent && !["Head", "Filter", "GroupBy", "Rollup"].includes(node.name)) node = node.parent;
3533
+ if (!["Head", "Filter", "GroupBy", "Rollup"].includes(node.name)) return;
3534
+ let filterIndex;
3535
+ if (node.name === "Filter") {
3536
+ filterIndex = 0;
3537
+ language.syntaxTree(view.state).iterate({
3538
+ enter: (candidate) => {
3539
+ if (candidate.name === "Filter") {
3540
+ if (candidate.from === node.from) return false;
3541
+ if (candidate.from < node.from) filterIndex += 1;
3542
+ }
3543
+ return true;
3544
+ }
3545
+ });
3546
+ }
3547
+ let text = view.state.sliceDoc(node.from, node.to);
3548
+ if (node.name === "Head") {
3549
+ const line = view.state.doc.lineAt(position);
3550
+ const word = Array.from(line.text.matchAll(/[\w.*-]+/g)).find((match) => line.from + match.index <= position && position <= line.from + match.index + match[0].length);
3551
+ if (word) text = word[0];
3552
+ }
3553
+ onActivate({ node: node.name, filterIndex, from: node.from, to: node.to, text });
3554
+ }
3555
+ function advanceSlot(view, dir, onActivate) {
3447
3556
  const head = view.state.selection.main.head;
3448
3557
  const slots = slotPositions(view.state);
3449
3558
  const next = dir > 0 ? slots.find((slot) => slot > head) : [...slots].reverse().find((slot) => slot < head);
3450
3559
  if (next === void 0) return false;
3451
3560
  view.dispatch({ selection: { anchor: next }, scrollIntoView: true });
3561
+ activateSlot(view, next, onActivate);
3452
3562
  return true;
3453
3563
  }
3454
3564
  function wqlLint(view) {
@@ -3491,7 +3601,7 @@ function wqlLint(view) {
3491
3601
  });
3492
3602
  return out;
3493
3603
  }
3494
- function wqlEditorKeymap(onSubmit, onEscape) {
3604
+ function wqlEditorKeymap(onSubmit, onEscape, onSlotActivate) {
3495
3605
  return [
3496
3606
  { key: "Mod-Enter", run: () => {
3497
3607
  onSubmit();
@@ -3515,7 +3625,7 @@ function wqlEditorKeymap(onSubmit, onEscape) {
3515
3625
  run: (view) => {
3516
3626
  if (view.compositionStarted) return false;
3517
3627
  const accepted = autocomplete.acceptCompletion(view);
3518
- return advanceSlot(view, 1) || accepted;
3628
+ return advanceSlot(view, 1, onSlotActivate) || accepted;
3519
3629
  }
3520
3630
  },
3521
3631
  {
@@ -3523,7 +3633,7 @@ function wqlEditorKeymap(onSubmit, onEscape) {
3523
3633
  run: (view) => {
3524
3634
  if (view.compositionStarted) return false;
3525
3635
  const accepted = autocomplete.acceptCompletion(view);
3526
- return advanceSlot(view, -1) || accepted;
3636
+ return advanceSlot(view, -1, onSlotActivate) || accepted;
3527
3637
  }
3528
3638
  },
3529
3639
  {
@@ -3542,11 +3652,11 @@ function wqlEditorKeymap(onSubmit, onEscape) {
3542
3652
  ...commands.historyKeymap
3543
3653
  ];
3544
3654
  }
3545
- function WqlTextEditor({ value, onChange, onSubmit, onEscape, placeholder, autoFocus = false, dense = false }) {
3655
+ function WqlTextEditor({ value, onChange, onSubmit, onEscape, placeholder, autoFocus = false, dense = false, onSlotActivate, activeRange, activeClause, className }) {
3546
3656
  const host = React2.useRef(null);
3547
3657
  const view$1 = React2.useRef(void 0);
3548
- const callbacks = React2.useRef({ onChange, onSubmit, onEscape });
3549
- callbacks.current = { onChange, onSubmit, onEscape };
3658
+ const callbacks = React2.useRef({ onChange, onSubmit, onEscape, onSlotActivate });
3659
+ callbacks.current = { onChange, onSubmit, onEscape, onSlotActivate };
3550
3660
  const replacing = React2.useRef(false);
3551
3661
  const initialValue = React2.useRef(value);
3552
3662
  React2.useEffect(() => {
@@ -3563,6 +3673,7 @@ function WqlTextEditor({ value, onChange, onSubmit, onEscape, placeholder, autoF
3563
3673
  language.syntaxHighlighting(wodWikiWql.wqlHighlightStyle),
3564
3674
  commands.history(),
3565
3675
  view.EditorView.lineWrapping,
3676
+ activeSegmentField,
3566
3677
  ...placeholder ? [view.placeholder(placeholder)] : [],
3567
3678
  view.EditorView.contentAttributes.of({ "aria-label": "WQL", "data-testid": "wql-text-input" }),
3568
3679
  autocomplete.autocompletion({
@@ -3583,11 +3694,12 @@ function WqlTextEditor({ value, onChange, onSubmit, onEscape, placeholder, autoF
3583
3694
  lint.linter(wqlLint),
3584
3695
  view.keymap.of(wqlEditorKeymap(
3585
3696
  () => callbacks.current.onSubmit(),
3586
- () => callbacks.current.onEscape()
3697
+ () => callbacks.current.onEscape(),
3698
+ (slot) => callbacks.current.onSlotActivate?.(slot)
3587
3699
  )),
3588
3700
  view.EditorView.domEventHandlers({
3589
- // Clicking a head/target/filter token selects it and lists its alternatives;
3590
- // typing then replaces it, picking swaps it.
3701
+ // Host composers open the contextual writer; standalone editors
3702
+ // retain token selection and in-row completion.
3591
3703
  mouseup: (event, editor2) => {
3592
3704
  if (event.button !== 0) return false;
3593
3705
  let { from, to } = editor2.state.selection.main;
@@ -3598,11 +3710,18 @@ function WqlTextEditor({ value, onChange, onSubmit, onEscape, placeholder, autoF
3598
3710
  from = line.from + word.index;
3599
3711
  to = from + word[0].length;
3600
3712
  let node = language.syntaxTree(editor2.state).resolveInner(to, -1);
3601
- while (node.parent && !["Head", "Filter"].includes(node.name)) node = node.parent;
3602
- if (!["Head", "Filter"].includes(node.name)) return false;
3603
- editor2.dispatch({ selection: { anchor: from, head: to } });
3713
+ while (node.parent && !["Head", "Filter", "GroupBy", "Rollup"].includes(node.name)) node = node.parent;
3714
+ if (!["Head", "Filter", "GroupBy", "Rollup"].includes(node.name)) return false;
3715
+ if (!callbacks.current.onSlotActivate) {
3716
+ editor2.dispatch({ selection: { anchor: from, head: to } });
3717
+ }
3718
+ }
3719
+ if (callbacks.current.onSlotActivate) {
3720
+ autocomplete.closeCompletion(editor2);
3721
+ activateSlot(editor2, from, (slot) => callbacks.current.onSlotActivate?.(slot));
3722
+ } else {
3723
+ autocomplete.startCompletion(editor2);
3604
3724
  }
3605
- autocomplete.startCompletion(editor2);
3606
3725
  return false;
3607
3726
  },
3608
3727
  keydown: (event, editor2) => {
@@ -3622,7 +3741,8 @@ function WqlTextEditor({ value, onChange, onSubmit, onEscape, placeholder, autoF
3622
3741
  '.cm-line span[class*="tok-"]:hover, .cm-line span[class^="tok-"]:hover': { fontWeight: "700", textShadow: "0 0 0.5px currentColor", cursor: "pointer" },
3623
3742
  ".cm-scroller": { overflow: "auto" },
3624
3743
  ".cm-placeholder": { color: "hsl(var(--muted-foreground))" },
3625
- ".wql-completion-icon": { display: "inline-flex", flexShrink: "0", color: "hsl(var(--primary))" }
3744
+ ".wql-completion-icon": { display: "inline-flex", flexShrink: "0", color: "hsl(var(--primary))" },
3745
+ ".wql-active-segment": { backgroundColor: "hsl(var(--primary) / 0.16)", boxShadow: "inset 0 -2px hsl(var(--primary))", borderRadius: "4px", padding: "1px 2px", margin: "0 -2px", fontWeight: "600" }
3626
3746
  }),
3627
3747
  autocompleteTooltipTheme
3628
3748
  ]
@@ -3642,7 +3762,13 @@ function WqlTextEditor({ value, onChange, onSubmit, onEscape, placeholder, autoF
3642
3762
  editor.dispatch({ changes: { from: 0, to: editor.state.doc.length, insert: value } });
3643
3763
  replacing.current = false;
3644
3764
  }, [value]);
3645
- return /* @__PURE__ */ jsxRuntime.jsx("div", { ref: host, className: "min-w-0 w-full rounded-lg border border-border bg-background focus-within:ring-2 focus-within:ring-ring", "data-testid": "wql-text-editor" });
3765
+ React2.useEffect(() => {
3766
+ const editor = view$1.current;
3767
+ if (!editor) return;
3768
+ const resolvedRange = activeRange ?? findClauseRange(editor.state, activeClause);
3769
+ editor.dispatch({ effects: activeSegmentEffect.of(resolvedRange ?? null) });
3770
+ }, [activeRange, activeClause, value]);
3771
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { ref: host, className: className ?? "min-w-0 w-full rounded-lg border border-border bg-background focus-within:ring-2 focus-within:ring-ring", "data-testid": "wql-text-editor" });
3646
3772
  }
3647
3773
  function WqlComposer({
3648
3774
  initialQuery,
@@ -3664,7 +3790,8 @@ function WqlComposer({
3664
3790
  preferredChoices,
3665
3791
  autoFocus = false,
3666
3792
  placeholder = "Search text or enter WQL",
3667
- className
3793
+ className,
3794
+ segmentEditRequest
3668
3795
  }) {
3669
3796
  const [desktop, setDesktop] = React2.useState(false);
3670
3797
  const [base, setBase] = React2.useState(query ?? initialQuery ?? ":note");
@@ -3746,7 +3873,7 @@ function WqlComposer({
3746
3873
  }, [autoFocus]);
3747
3874
  React2.useEffect(() => {
3748
3875
  if (active.kind === "value") pickerRef.current?.focus();
3749
- }, [active.kind]);
3876
+ }, [active]);
3750
3877
  React2.useEffect(() => {
3751
3878
  if (highlight >= 0) document.getElementById(`${listId}-option-${highlight}`)?.scrollIntoView?.({ block: "nearest" });
3752
3879
  }, [highlight, listId]);
@@ -3772,14 +3899,14 @@ function WqlComposer({
3772
3899
  if (openerRef.current?.isConnected) openerRef.current.focus();
3773
3900
  else inputRef.current?.focus();
3774
3901
  };
3775
- const openEditor = (clause2) => {
3902
+ const openEditor = (clause2, range) => {
3776
3903
  if (pending && currentRef.current.valid) commit(currentRef.current);
3777
3904
  openerRef.current = document.activeElement instanceof HTMLElement ? document.activeElement : null;
3778
3905
  if (clause2.type === "where" || clause2.type === "pipes") {
3779
3906
  setActive({ kind: "wql" });
3780
3907
  commit(currentRef.current);
3781
3908
  } else {
3782
- setActive({ kind: "value", clause: clause2 });
3909
+ setActive({ kind: "value", clause: clause2, range });
3783
3910
  setSearch("");
3784
3911
  }
3785
3912
  setHighlight(-1);
@@ -3790,6 +3917,24 @@ function WqlComposer({
3790
3917
  if (pending) commit(resolveQueryDraft(base));
3791
3918
  openEditor(existing ?? { id: `new-${type}`, type, ...getClauseMeta(type), value: "" });
3792
3919
  };
3920
+ const openFieldRef = React2.useRef(openField);
3921
+ openFieldRef.current = openField;
3922
+ React2.useEffect(() => {
3923
+ if (segmentEditRequest) openFieldRef.current(segmentEditRequest.type);
3924
+ }, [segmentEditRequest]);
3925
+ const activateWqlSlot = (slot) => {
3926
+ const clause2 = slot.filterIndex !== void 0 ? pills.find((pill) => pill.filterIndex === slot.filterIndex) : slot.node === "GroupBy" ? pills.find((pill) => pill.type === "groupby") : slot.node === "Rollup" ? pills.find((pill) => pill.type === "rollup") : slot.node === "Head" ? pills.find((pill) => ["kind", "target", "agg", "metric"].includes(pill.type) && pill.value === slot.text) : void 0;
3927
+ if (clause2) openEditor(clause2, { from: slot.from, to: slot.to });
3928
+ };
3929
+ const moveEditor = (direction) => {
3930
+ if (active.kind !== "value") return false;
3931
+ const visible = pills.filter((pill) => !hidden.has(pill.type));
3932
+ const index = visible.findIndex((pill) => pill.id === active.clause.id);
3933
+ const next = visible[index + direction];
3934
+ if (!next) return false;
3935
+ openEditor(next);
3936
+ return true;
3937
+ };
3793
3938
  const updateClause = (clause2, value) => {
3794
3939
  if (clause2.type === "kind" || clause2.type === "target") {
3795
3940
  const next2 = pivotQuery(currentRef.current.wql, clause2.type, value ?? "");
@@ -3841,6 +3986,13 @@ function WqlComposer({
3841
3986
  onSubmit?.(draft.wql);
3842
3987
  };
3843
3988
  const handleKeys = (event) => {
3989
+ if (active.kind === "value" && event.key === "Tab" && !event.nativeEvent.isComposing) {
3990
+ if (moveEditor(event.shiftKey ? -1 : 1)) {
3991
+ event.preventDefault();
3992
+ event.stopPropagation();
3993
+ }
3994
+ return;
3995
+ }
3844
3996
  if (event.nativeEvent.isComposing) return;
3845
3997
  if (event.key === "Enter" && (event.ctrlKey || event.metaKey)) {
3846
3998
  event.preventDefault();
@@ -3905,119 +4057,285 @@ function WqlComposer({
3905
4057
  diagnosticsActions
3906
4058
  ] });
3907
4059
  const diagnostics = { ...resolved, valid: resolved.valid && !error, error: error ?? resolved.error };
3908
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("min-w-0 space-y-2", desktop && compact && "flex flex-wrap items-center gap-2 space-y-0 [&>:not([data-wql-inline])]:w-full", className), "data-testid": "wql-composer-root", children: [
3909
- diagnosticsPosition === "top" && showDiagnostics && /* @__PURE__ */ jsxRuntime.jsx(WqlDiagnosticsStrip, { diagnostics, stages, actions: action, variant: "header" }),
3910
- (desktop || active.kind === "wql" || !guided) && /* @__PURE__ */ jsxRuntime.jsx("div", { "data-wql-inline": true, className: cn("min-w-0", desktop && compact ? "flex-1" : "w-full"), children: /* @__PURE__ */ jsxRuntime.jsx(WqlTextEditor, { value: resolved.wql, placeholder, dense: desktop && compact, onChange: (text) => commit(resolveQueryDraft(text)), onSubmit: submit, onEscape: () => {
3911
- if (active.kind === "closed") return false;
3912
- closeEditor();
3913
- return true;
3914
- }, autoFocus }) }),
3915
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("flex min-w-0 flex-wrap items-center gap-1", !desktop && "rounded-xl border border-border p-2", desktop && compact && "hidden"), "data-testid": "wql-composer", onKeyDown: (event) => {
3916
- if (!["Delete", "Backspace"].includes(event.key) || !(event.target instanceof HTMLButtonElement)) return;
3917
- const buttons = Array.from(event.currentTarget.querySelectorAll('[data-testid^="token-slot-"]:not([data-testid^="token-slot-remove-"]):not([data-testid^="token-slot-value-"])'));
3918
- const pill = pills.filter((item) => !hidden.has(item.type))[buttons.indexOf(event.target)];
3919
- if (!pill || ["kind", "target", "agg", "metric"].includes(pill.type)) return;
3920
- event.preventDefault();
3921
- event.stopPropagation();
3922
- setUndo(currentRef.current.wql);
3923
- updateClause(pill, null);
3924
- inputRef.current?.focus();
3925
- }, children: [
3926
- guided && active.kind !== "wql" && pills.filter((pill) => !hidden.has(pill.type)).map((pill) => /* @__PURE__ */ jsxRuntime.jsx(TokenSlotPill, { clause: pill, compact, isActive: active.kind === "value" && active.clause.id === pill.id, onClick: () => openEditor(pill), onRemove: ["kind", "target", "agg", "metric"].includes(pill.type) ? void 0 : () => {
3927
- setUndo(currentRef.current.wql);
3928
- updateClause(pill, null);
3929
- } }, pill.id)),
3930
- !desktop && active.kind !== "wql" && guided && /* @__PURE__ */ jsxRuntime.jsx("input", { ref: inputRef, type: "text", role: "combobox", "aria-label": "Search text or WQL", "aria-controls": suggesting ? listId : void 0, "aria-expanded": suggesting, "aria-activedescendant": suggesting && highlight >= 0 ? `${listId}-option-${highlight}` : void 0, value: pending, placeholder, onChange: (event) => {
3931
- const text = event.target.value;
3932
- setPending(text);
3933
- setHighlight(-1);
3934
- setError(void 0);
3935
- notify(resolveQueryDraft(base, text));
3936
- }, onKeyDown: handleKeys, className: "min-h-12 min-w-0 flex-[1_1_140px] bg-transparent font-mono text-base outline-none", "data-testid": "wql-composer-input" }),
3937
- customSlots
3938
- ] }),
3939
- (!desktop || undo) && /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-wql-inline": desktop && compact ? "" : void 0, className: "flex flex-wrap items-center gap-2", children: [
3940
- !desktop && /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", onClick: () => {
3941
- commit(currentRef.current);
3942
- if (active.kind === "wql") closeEditor();
3943
- else setActive({ kind: "wql" });
3944
- }, disabled: active.kind === "wql" && !guided, className: "min-h-12 rounded-lg px-3", children: active.kind === "wql" ? "Guided controls" : "Edit WQL" }),
3945
- undo && /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", onClick: () => {
3946
- commit(resolveQueryDraft(undo));
3947
- setUndo(void 0);
3948
- }, className: cn("rounded-lg", desktop && compact ? "min-h-9 px-2.5 text-xs text-muted-foreground hover:text-foreground" : "min-h-12 px-3"), children: "Undo removal" })
3949
- ] }),
3950
- active.kind === "value" && /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-testid": "wql-field-picker", className: "min-w-0 rounded-xl border border-border p-2 focus-within:ring-2 focus-within:ring-ring", onKeyDown: (event) => {
3951
- if (event.key === "Escape" || event.key === "Enter" && (event.ctrlKey || event.metaKey)) handleKeys(event);
3952
- }, children: [
3953
- /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", onClick: closeEditor, className: "min-h-12 px-3", children: "Back" }),
3954
- /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "block text-sm", children: [
3955
- "Search ",
3956
- getClauseMeta(active.clause.type).label,
3957
- /* @__PURE__ */ jsxRuntime.jsx("input", { ref: pickerRef, type: "search", role: "combobox", "aria-label": `Search ${getClauseMeta(active.clause.type).label}`, "aria-controls": listId, "aria-expanded": "true", "aria-activedescendant": highlight >= 0 ? `${listId}-option-${highlight}` : void 0, value: search, onChange: (event) => {
3958
- setSearch(event.target.value);
3959
- setHighlight(-1);
3960
- setError(void 0);
3961
- }, onKeyDown: handleKeys, className: "min-h-12 w-full rounded border border-border bg-background px-3 text-base", "data-testid": "wql-picker-search" })
3962
- ] }),
3963
- active.kind === "value" && active.clause.filterIndex !== void 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap gap-2", children: [
3964
- /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "flex min-h-12 items-center gap-2", children: [
3965
- /* @__PURE__ */ jsxRuntime.jsx("input", { type: "checkbox", checked: active.clause.negate ?? false, onChange: (event) => updateClause({ ...active.clause, negate: event.target.checked }, active.clause.value) }),
3966
- "Exclude"
4060
+ const visiblePills = pills.filter((pill) => !hidden.has(pill.type));
4061
+ const activePillIndex = active.kind === "value" ? visiblePills.findIndex((pill) => pill.id === active.clause.id) : -1;
4062
+ return /* @__PURE__ */ jsxRuntime.jsxs(
4063
+ "div",
4064
+ {
4065
+ className: cn(
4066
+ "min-w-0",
4067
+ compact ? "flex flex-col overflow-hidden rounded-xl border border-border bg-card shadow-xs transition-shadow focus-within:ring-2 focus-within:ring-ring" : "space-y-2",
4068
+ className
4069
+ ),
4070
+ "data-testid": "wql-composer-root",
4071
+ children: [
4072
+ diagnosticsPosition === "top" && showDiagnostics && /* @__PURE__ */ jsxRuntime.jsx(WqlDiagnosticsStrip, { diagnostics, stages, actions: action, variant: "header" }),
4073
+ (desktop || compact || active.kind === "wql" || !guided) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-w-0 w-full items-center gap-2", children: [
4074
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-w-0 flex-1", children: /* @__PURE__ */ jsxRuntime.jsx(
4075
+ WqlTextEditor,
4076
+ {
4077
+ value: resolved.wql,
4078
+ placeholder,
4079
+ dense: compact,
4080
+ className: compact ? "min-w-0 w-full bg-transparent" : void 0,
4081
+ activeRange: active.kind === "value" ? active.range : void 0,
4082
+ activeClause: active.kind === "value" ? active.clause : void 0,
4083
+ onSlotActivate: activateWqlSlot,
4084
+ onChange: (text) => commit(resolveQueryDraft(text)),
4085
+ onSubmit: submit,
4086
+ onEscape: () => {
4087
+ if (active.kind === "closed") return false;
4088
+ closeEditor();
4089
+ return true;
4090
+ },
4091
+ autoFocus
4092
+ }
4093
+ ) }),
4094
+ desktop && compact && undo && /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", onClick: () => {
4095
+ commit(resolveQueryDraft(undo));
4096
+ setUndo(void 0);
4097
+ }, className: "min-h-9 shrink-0 rounded-lg px-2.5 text-xs text-muted-foreground hover:text-foreground", children: "Undo removal" })
3967
4098
  ] }),
3968
- /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "flex min-h-12 items-center gap-2", children: [
3969
- /* @__PURE__ */ jsxRuntime.jsx("input", { type: "checkbox", checked: active.clause.value.split("|").every((value) => value.endsWith("*")), onChange: (event) => updateClause(active.clause, active.clause.value.split("|").map((value) => `${value.replace(/\*$/, "")}${event.target.checked ? "*" : ""}`).join("|")) }),
3970
- "Starts with"
3971
- ] })
3972
- ] }),
3973
- active.kind === "value" && active.clause.type === "time" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-1 gap-2 sm:grid-cols-2", children: [
3974
- /* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
3975
- "Start date",
3976
- /* @__PURE__ */ jsxRuntime.jsx("input", { type: "date", "aria-label": "Start date", className: "min-h-12 w-full bg-background text-base", onChange: (event) => {
3977
- const end = draftWindow();
3978
- updateClause(active.clause, `from ${event.target.value}${end?.kind === "range" && end.end ? ` to ${end.end}` : ""}`);
3979
- }, value: astWindow?.kind === "range" ? astWindow.start : "" })
4099
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("flex min-w-0 flex-wrap items-center gap-1", !desktop && !compact && "rounded-xl border border-border p-2", compact && "hidden"), "data-testid": "wql-composer", onKeyDown: (event) => {
4100
+ if (!["Delete", "Backspace"].includes(event.key) || !(event.target instanceof HTMLButtonElement)) return;
4101
+ const buttons = Array.from(event.currentTarget.querySelectorAll('[data-testid^="token-slot-"]:not([data-testid^="token-slot-remove-"]):not([data-testid^="token-slot-value-"])'));
4102
+ const pill = visiblePills[buttons.indexOf(event.target)];
4103
+ if (!pill || ["kind", "target", "agg", "metric"].includes(pill.type)) return;
4104
+ event.preventDefault();
4105
+ event.stopPropagation();
4106
+ setUndo(currentRef.current.wql);
4107
+ updateClause(pill, null);
4108
+ inputRef.current?.focus();
4109
+ }, children: [
4110
+ guided && active.kind !== "wql" && visiblePills.map((pill) => /* @__PURE__ */ jsxRuntime.jsx(TokenSlotPill, { clause: pill, compact, isActive: active.kind === "value" && active.clause.id === pill.id, onClick: () => openEditor(pill), onRemove: ["kind", "target", "agg", "metric"].includes(pill.type) ? void 0 : () => {
4111
+ setUndo(currentRef.current.wql);
4112
+ updateClause(pill, null);
4113
+ } }, pill.id)),
4114
+ !desktop && !compact && active.kind !== "wql" && guided && /* @__PURE__ */ jsxRuntime.jsx("input", { ref: inputRef, type: "text", role: "combobox", "aria-label": "Search text or WQL", "aria-controls": suggesting ? listId : void 0, "aria-expanded": suggesting, "aria-activedescendant": suggesting && highlight >= 0 ? `${listId}-option-${highlight}` : void 0, value: pending, placeholder, onChange: (event) => {
4115
+ const text = event.target.value;
4116
+ setPending(text);
4117
+ setHighlight(-1);
4118
+ setError(void 0);
4119
+ notify(resolveQueryDraft(base, text));
4120
+ }, onKeyDown: handleKeys, className: "min-h-12 min-w-0 flex-[1_1_140px] bg-transparent font-mono text-base outline-none", "data-testid": "wql-composer-input" }),
4121
+ customSlots
3980
4122
  ] }),
3981
- /* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
3982
- "End date",
3983
- /* @__PURE__ */ jsxRuntime.jsx("input", { type: "date", "aria-label": "End date", className: "min-h-12 w-full bg-background text-base", onChange: (event) => {
3984
- const window2 = draftWindow();
3985
- if (window2?.kind === "range") updateClause(active.clause, `from ${window2.start}${event.target.value ? ` to ${event.target.value}` : ""}`);
3986
- else setError("Choose a start date first");
3987
- }, value: astWindow?.kind === "range" ? astWindow.end ?? "" : "" })
3988
- ] })
3989
- ] }),
3990
- active.kind === "value" && (customEditor ? /* @__PURE__ */ jsxRuntime.jsx(customEditor.Editor, { value: customEditor.parseValue ? customEditor.parseValue(active.clause.value) : active.clause.value, onChange: (value) => updateClause(active.clause, customEditor.formatValue ? customEditor.formatValue(value) : String(value)), onClose: closeEditor }) : /* @__PURE__ */ jsxRuntime.jsx(InlineClauseEditor, { clause: active.clause, filteredItems: preferredItems, selectedValues: values2.selectedValues, isMulti: values2.isMulti, typedValue: values2.typedValue, canCommitTyped: values2.canCommitTyped, emptyText: values2.emptyText, highlightIdx: highlight, onHighlight: setHighlight, onCommitValue: chooseValue, onCommitTyped: chooseValue, listId, onDone: closeEditor, onReorder: (dimensions) => updateClause(active.clause, dimensions.join("|")) }))
3991
- ] }),
3992
- suggesting && /* @__PURE__ */ jsxRuntime.jsxs("div", { id: listId, role: "listbox", "aria-label": "Query conditions", className: "max-h-64 overflow-y-auto rounded-xl border border-border p-1", "data-testid": "wql-filter-typeahead", children: [
3993
- catalog.map((type, index) => /* @__PURE__ */ jsxRuntime.jsxs("button", { id: `${listId}-option-${index}`, type: "button", role: "option", "aria-selected": false, onMouseEnter: () => setHighlight(index), onClick: () => openField(type), className: cn("flex min-h-12 w-full items-center gap-2 rounded px-3 text-left", index === highlight && "bg-muted"), "data-testid": `wql-filter-typeahead-${type}`, children: [
3994
- /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
3995
- pills.some((pill) => pill.type === type) ? "Edit" : "Add",
3996
- " ",
3997
- getClauseMeta(type).label
4123
+ (!desktop && !compact || !compact && undo) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
4124
+ !desktop && !compact && /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", onClick: () => {
4125
+ commit(currentRef.current);
4126
+ if (active.kind === "wql") closeEditor();
4127
+ else setActive({ kind: "wql" });
4128
+ }, disabled: active.kind === "wql" && !guided, className: "min-h-12 rounded-lg px-3", children: active.kind === "wql" ? "Guided controls" : "Edit WQL" }),
4129
+ undo && /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", onClick: () => {
4130
+ commit(resolveQueryDraft(undo));
4131
+ setUndo(void 0);
4132
+ }, className: "min-h-12 rounded-lg px-3", children: "Undo removal" })
3998
4133
  ] }),
3999
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-muted-foreground", children: type })
4000
- ] }, type)),
4001
- textAction && /* @__PURE__ */ jsxRuntime.jsxs("button", { id: `${listId}-option-${catalog.length}`, type: "button", role: "option", "aria-selected": false, onMouseEnter: () => setHighlight(catalog.length), className: cn("min-h-12 w-full rounded px-3 text-left", highlight === catalog.length && "bg-muted"), onClick: submit, children: [
4002
- "Search text ",
4003
- pending
4004
- ] })
4005
- ] }),
4006
- pivot && /* @__PURE__ */ jsxRuntime.jsxs("div", { role: "alertdialog", "aria-label": "Confirm query pivot", className: "rounded-xl border border-border p-3", children: [
4007
- /* @__PURE__ */ jsxRuntime.jsx("p", { children: "Changing the query removes incompatible clauses:" }),
4008
- /* @__PURE__ */ jsxRuntime.jsx("ul", { children: pivot.removed.map((item) => /* @__PURE__ */ jsxRuntime.jsx("li", { children: item }, item)) }),
4009
- /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "min-h-12 px-3", onClick: () => setPivot(void 0), children: "Cancel" }),
4010
- /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "min-h-12 px-3", onClick: () => {
4011
- commit(pivot.draft);
4012
- setPivot(void 0);
4013
- closeEditor();
4014
- }, children: "Change query" })
4015
- ] }),
4016
- !showDiagnostics && (error ?? resolved.error) && /* @__PURE__ */ jsxRuntime.jsx("p", { role: "alert", className: "text-sm text-destructive", children: error ?? resolved.error }),
4017
- !guided && !resolved.error && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground", children: "Use Edit WQL to preserve every clause." }),
4018
- pending && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground", "data-testid": "wql-composer-pending", children: resolved.valid ? "Current draft includes this text" : resolved.error }),
4019
- showDiagnostics && diagnosticsPosition === "bottom" ? /* @__PURE__ */ jsxRuntime.jsx(WqlDiagnosticsStrip, { diagnostics, stages, actions: action }) : diagnosticsPosition !== "top" || !showDiagnostics ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap items-center justify-end gap-2", children: action }) : null
4020
- ] });
4134
+ active.kind === "value" && /* @__PURE__ */ jsxRuntime.jsxs(
4135
+ "div",
4136
+ {
4137
+ "data-testid": "wql-field-picker",
4138
+ className: cn(
4139
+ "min-w-0",
4140
+ compact ? "border-t border-border/70 bg-muted/20 px-2.5 py-2 space-y-1.5" : "space-y-2 rounded-xl border border-border/80 bg-muted/30 p-2 sm:p-2.5 shadow-xs backdrop-blur-sm"
4141
+ ),
4142
+ onKeyDown: (event) => {
4143
+ if (event.key === "Escape" || event.key === "Enter" && (event.ctrlKey || event.metaKey)) handleKeys(event);
4144
+ },
4145
+ children: [
4146
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
4147
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "inline-flex shrink-0 items-center gap-1.5 rounded-md border border-border/70 bg-background/80 px-2.5 py-1 text-xs font-semibold text-foreground font-mono", children: [
4148
+ /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true", className: "text-xs", children: getClauseMeta(active.clause.type).icon }),
4149
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: getClauseMeta(active.clause.type).label })
4150
+ ] }),
4151
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "sr-only", children: [
4152
+ "Search ",
4153
+ getClauseMeta(active.clause.type).label
4154
+ ] }),
4155
+ /* @__PURE__ */ jsxRuntime.jsx(
4156
+ "input",
4157
+ {
4158
+ ref: pickerRef,
4159
+ type: "search",
4160
+ role: "combobox",
4161
+ "aria-label": `Search ${getClauseMeta(active.clause.type).label}`,
4162
+ "aria-controls": listId,
4163
+ "aria-expanded": "true",
4164
+ "aria-activedescendant": highlight >= 0 ? `${listId}-option-${highlight}` : void 0,
4165
+ value: search,
4166
+ placeholder: active.clause.value ? `Current: ${active.clause.value}` : `Type value for ${getClauseMeta(active.clause.type).label}\u2026`,
4167
+ onChange: (event) => {
4168
+ setSearch(event.target.value);
4169
+ setHighlight(-1);
4170
+ setError(void 0);
4171
+ },
4172
+ onKeyDown: handleKeys,
4173
+ className: "min-h-10 sm:min-h-8 min-w-0 flex-1 rounded-lg border border-border/80 bg-background px-2.5 font-mono text-sm sm:text-xs text-foreground placeholder:text-muted-foreground/60 focus:outline-none focus:ring-1 focus:ring-primary shadow-xs",
4174
+ "data-testid": "wql-picker-search"
4175
+ }
4176
+ ),
4177
+ active.clause.filterIndex !== void 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "order-last flex w-full items-center gap-1.5 sm:order-none sm:w-auto", children: [
4178
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "inline-flex cursor-pointer select-none items-center gap-1.5 px-2 py-0.5 rounded-md border border-border/60 bg-background/70 text-[11px] text-muted-foreground hover:text-foreground transition-colors", children: [
4179
+ /* @__PURE__ */ jsxRuntime.jsx(
4180
+ "input",
4181
+ {
4182
+ type: "checkbox",
4183
+ checked: active.clause.negate ?? false,
4184
+ onChange: (event) => updateClause({ ...active.clause, negate: event.target.checked }, active.clause.value),
4185
+ className: "size-3 rounded border-border text-primary focus:ring-primary"
4186
+ }
4187
+ ),
4188
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Exclude" })
4189
+ ] }),
4190
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "inline-flex cursor-pointer select-none items-center gap-1.5 px-2 py-0.5 rounded-md border border-border/60 bg-background/70 text-[11px] text-muted-foreground hover:text-foreground transition-colors", children: [
4191
+ /* @__PURE__ */ jsxRuntime.jsx(
4192
+ "input",
4193
+ {
4194
+ type: "checkbox",
4195
+ checked: active.clause.value.split("|").every((value) => value.endsWith("*")),
4196
+ onChange: (event) => updateClause(active.clause, active.clause.value.split("|").map((value) => `${value.replace(/\*$/, "")}${event.target.checked ? "*" : ""}`).join("|")),
4197
+ className: "size-3 rounded border-border text-primary focus:ring-primary"
4198
+ }
4199
+ ),
4200
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Starts with" })
4201
+ ] })
4202
+ ] }),
4203
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex shrink-0 items-center gap-1", children: [
4204
+ /* @__PURE__ */ jsxRuntime.jsx(
4205
+ "button",
4206
+ {
4207
+ type: "button",
4208
+ "aria-label": "Previous WQL segment",
4209
+ "data-testid": "wql-segment-previous",
4210
+ disabled: activePillIndex <= 0,
4211
+ onClick: () => {
4212
+ if (moveEditor(-1)) pickerRef.current?.focus();
4213
+ },
4214
+ className: "flex items-center justify-center min-h-9 min-w-9 sm:min-h-7 sm:min-w-7 rounded-md border border-border/70 bg-background/80 text-muted-foreground hover:bg-muted hover:text-foreground disabled:opacity-40 disabled:pointer-events-none text-xs transition-colors",
4215
+ children: "\u2190"
4216
+ }
4217
+ ),
4218
+ /* @__PURE__ */ jsxRuntime.jsx(
4219
+ "button",
4220
+ {
4221
+ type: "button",
4222
+ "aria-label": "Next WQL segment",
4223
+ "data-testid": "wql-segment-next",
4224
+ disabled: activePillIndex < 0 || activePillIndex >= visiblePills.length - 1,
4225
+ onClick: () => {
4226
+ if (moveEditor(1)) pickerRef.current?.focus();
4227
+ },
4228
+ className: "flex items-center justify-center min-h-9 min-w-9 sm:min-h-7 sm:min-w-7 rounded-md border border-border/70 bg-background/80 text-muted-foreground hover:bg-muted hover:text-foreground disabled:opacity-40 disabled:pointer-events-none text-xs transition-colors",
4229
+ children: "\u2192"
4230
+ }
4231
+ ),
4232
+ /* @__PURE__ */ jsxRuntime.jsx(
4233
+ "button",
4234
+ {
4235
+ type: "button",
4236
+ "aria-label": "Close segment editor",
4237
+ onClick: closeEditor,
4238
+ className: "flex items-center justify-center min-h-9 min-w-9 sm:min-h-7 sm:min-w-7 rounded-md text-muted-foreground hover:bg-muted hover:text-foreground text-xs transition-colors",
4239
+ children: "\u2715"
4240
+ }
4241
+ )
4242
+ ] })
4243
+ ] }),
4244
+ active.kind === "value" && active.clause.type === "time" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-1 gap-2 sm:grid-cols-2 pt-0.5 text-xs", children: [
4245
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "block text-muted-foreground", children: [
4246
+ "Start date",
4247
+ /* @__PURE__ */ jsxRuntime.jsx(
4248
+ "input",
4249
+ {
4250
+ type: "date",
4251
+ "aria-label": "Start date",
4252
+ className: "mt-1 min-h-8 w-full rounded-md border border-border/80 bg-background px-2.5 text-xs text-foreground",
4253
+ onChange: (event) => {
4254
+ const end = draftWindow();
4255
+ updateClause(active.clause, `from ${event.target.value}${end?.kind === "range" && end.end ? ` to ${end.end}` : ""}`);
4256
+ },
4257
+ value: astWindow?.kind === "range" ? astWindow.start : ""
4258
+ }
4259
+ )
4260
+ ] }),
4261
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "block text-muted-foreground", children: [
4262
+ "End date",
4263
+ /* @__PURE__ */ jsxRuntime.jsx(
4264
+ "input",
4265
+ {
4266
+ type: "date",
4267
+ "aria-label": "End date",
4268
+ className: "mt-1 min-h-8 w-full rounded-md border border-border/80 bg-background px-2.5 text-xs text-foreground",
4269
+ onChange: (event) => {
4270
+ const window2 = draftWindow();
4271
+ if (window2?.kind === "range") updateClause(active.clause, `from ${window2.start}${event.target.value ? ` to ${event.target.value}` : ""}`);
4272
+ else setError("Choose a start date first");
4273
+ },
4274
+ value: astWindow?.kind === "range" ? astWindow.end ?? "" : ""
4275
+ }
4276
+ )
4277
+ ] })
4278
+ ] }),
4279
+ active.kind === "value" && (customEditor ? /* @__PURE__ */ jsxRuntime.jsx(
4280
+ customEditor.Editor,
4281
+ {
4282
+ value: customEditor.parseValue ? customEditor.parseValue(active.clause.value) : active.clause.value,
4283
+ onChange: (value) => updateClause(active.clause, customEditor.formatValue ? customEditor.formatValue(value) : String(value)),
4284
+ onClose: closeEditor
4285
+ }
4286
+ ) : /* @__PURE__ */ jsxRuntime.jsx(
4287
+ InlineClauseEditor,
4288
+ {
4289
+ clause: active.clause,
4290
+ filteredItems: preferredItems,
4291
+ selectedValues: values2.selectedValues,
4292
+ isMulti: values2.isMulti,
4293
+ typedValue: values2.typedValue,
4294
+ canCommitTyped: values2.canCommitTyped,
4295
+ emptyText: values2.emptyText,
4296
+ highlightIdx: highlight,
4297
+ onHighlight: setHighlight,
4298
+ onCommitValue: chooseValue,
4299
+ onCommitTyped: chooseValue,
4300
+ listId,
4301
+ onDone: closeEditor,
4302
+ onReorder: (dimensions) => updateClause(active.clause, dimensions.join("|"))
4303
+ }
4304
+ ))
4305
+ ]
4306
+ }
4307
+ ),
4308
+ suggesting && /* @__PURE__ */ jsxRuntime.jsxs("div", { id: listId, role: "listbox", "aria-label": "Query conditions", className: "max-h-64 overflow-y-auto rounded-xl border border-border p-1", "data-testid": "wql-filter-typeahead", children: [
4309
+ catalog.map((type, index) => /* @__PURE__ */ jsxRuntime.jsxs("button", { id: `${listId}-option-${index}`, type: "button", role: "option", "aria-selected": false, onMouseEnter: () => setHighlight(index), onClick: () => openField(type), className: cn("flex min-h-12 w-full items-center gap-2 rounded px-3 text-left", index === highlight && "bg-muted"), "data-testid": `wql-filter-typeahead-${type}`, children: [
4310
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
4311
+ pills.some((pill) => pill.type === type) ? "Edit" : "Add",
4312
+ " ",
4313
+ getClauseMeta(type).label
4314
+ ] }),
4315
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-muted-foreground", children: type })
4316
+ ] }, type)),
4317
+ textAction && /* @__PURE__ */ jsxRuntime.jsxs("button", { id: `${listId}-option-${catalog.length}`, type: "button", role: "option", "aria-selected": false, onMouseEnter: () => setHighlight(catalog.length), className: cn("min-h-12 w-full rounded px-3 text-left", highlight === catalog.length && "bg-muted"), onClick: submit, children: [
4318
+ "Search text ",
4319
+ pending
4320
+ ] })
4321
+ ] }),
4322
+ pivot && /* @__PURE__ */ jsxRuntime.jsxs("div", { role: "alertdialog", "aria-label": "Confirm query pivot", className: "rounded-xl border border-border p-3", children: [
4323
+ /* @__PURE__ */ jsxRuntime.jsx("p", { children: "Changing the query removes incompatible clauses:" }),
4324
+ /* @__PURE__ */ jsxRuntime.jsx("ul", { children: pivot.removed.map((item) => /* @__PURE__ */ jsxRuntime.jsx("li", { children: item }, item)) }),
4325
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "min-h-12 px-3", onClick: () => setPivot(void 0), children: "Cancel" }),
4326
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "min-h-12 px-3", onClick: () => {
4327
+ commit(pivot.draft);
4328
+ setPivot(void 0);
4329
+ closeEditor();
4330
+ }, children: "Change query" })
4331
+ ] }),
4332
+ !showDiagnostics && (error ?? resolved.error) && /* @__PURE__ */ jsxRuntime.jsx("p", { role: "alert", className: "text-sm text-destructive", children: error ?? resolved.error }),
4333
+ !guided && !resolved.error && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground", children: "Use Edit WQL to preserve every clause." }),
4334
+ pending && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground", "data-testid": "wql-composer-pending", children: resolved.valid ? "Current draft includes this text" : resolved.error }),
4335
+ showDiagnostics && diagnosticsPosition === "bottom" ? /* @__PURE__ */ jsxRuntime.jsx(WqlDiagnosticsStrip, { diagnostics, stages, actions: action }) : diagnosticsPosition !== "top" || !showDiagnostics ? Boolean(actionLabel || diagnosticsActions) && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap items-center justify-end gap-2", children: action }) : null
4336
+ ]
4337
+ }
4338
+ );
4021
4339
  }
4022
4340
 
4023
4341
  // src/composer/filterTypeahead.ts
@@ -14041,13 +14359,13 @@ function editorPreset(optionsOrDialect = "markdown") {
14041
14359
  }
14042
14360
 
14043
14361
  // src/version.ts
14044
- var VERSION = "0.48.2175";
14045
- var GIT_SHA = "7e7372c";
14046
- var BUILD_TIME = "2026-10-27T13:28:33.155Z";
14362
+ var VERSION = "0.49.2177";
14363
+ var GIT_SHA = "2966790";
14364
+ var BUILD_TIME = "2026-10-29T03:03:43.665Z";
14047
14365
  var SEMVER = {
14048
- version: "0.48.2175",
14049
- gitSha: "7e7372c",
14050
- buildTime: "2026-10-27T13:28:33.155Z"
14366
+ version: "0.49.2177",
14367
+ gitSha: "2966790",
14368
+ buildTime: "2026-10-29T03:03:43.665Z"
14051
14369
  };
14052
14370
 
14053
14371
  Object.defineProperty(exports, "CONTENT_GROUPING_DIMENSIONS", {