@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.
@@ -5478,7 +5478,7 @@ function InlineClauseEditor({
5478
5478
  const meta = getClauseMeta(clause2.type);
5479
5479
  const current = clause2.value.trim();
5480
5480
  const typedIdx = filteredItems.length;
5481
- const shownValues = selectedValues.length > 0 ? selectedValues : !isMulti && current ? [current] : [];
5481
+ const shownValues = selectedValues;
5482
5482
  const reorder = (i, dir) => {
5483
5483
  if (!onReorder) return;
5484
5484
  const to = i + dir;
@@ -5490,24 +5490,15 @@ function InlineClauseEditor({
5490
5490
  return /* @__PURE__ */ jsxRuntime.jsxs(
5491
5491
  "div",
5492
5492
  {
5493
- className: "mx-0.5 rounded-xl border border-border bg-popover shadow-md p-1",
5493
+ className: "min-w-0 pt-0.5",
5494
5494
  "data-testid": "wql-clause-editor",
5495
5495
  "data-clause-type": clause2.type,
5496
5496
  children: [
5497
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 px-2 pt-1 pb-1.5", children: [
5498
- /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": true, className: "text-[11px]", children: meta.icon }),
5499
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs sm:text-[11px] font-semibold text-foreground font-mono", children: meta.label }),
5500
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "hidden sm:block ml-auto text-[10px] text-muted-foreground/70 font-mono truncate", children: [
5501
- "\u2191\u2193 move \xB7 Enter ",
5502
- isMulti ? "add/remove" : "set",
5503
- " \xB7 Esc done \xB7 Tab exits"
5504
- ] })
5505
- ] }),
5506
- shownValues.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("px-2 pb-1.5", isMulti && "border-b border-border/50"), children: [
5497
+ isMulti && shownValues.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "border-b border-border/50 px-2 pb-1.5", children: [
5507
5498
  /* @__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(
5508
5499
  "span",
5509
5500
  {
5510
- 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",
5501
+ 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",
5511
5502
  children: [
5512
5503
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "max-w-40 truncate", children: v }),
5513
5504
  onReorder && shownValues.length > 1 && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
@@ -5557,7 +5548,7 @@ function InlineClauseEditor({
5557
5548
  role: "listbox",
5558
5549
  "aria-label": `${meta.label} options`,
5559
5550
  "aria-multiselectable": isMulti || void 0,
5560
- className: "max-h-[min(45svh,20rem)] sm:max-h-44 overflow-y-auto overscroll-contain touch-pan-y flex flex-col gap-0.5",
5551
+ 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",
5561
5552
  children: [
5562
5553
  canCommitTyped && /* @__PURE__ */ jsxRuntime.jsxs(
5563
5554
  "button",
@@ -5571,19 +5562,19 @@ function InlineClauseEditor({
5571
5562
  onClick: () => onCommitTyped(typedValue),
5572
5563
  "data-testid": `clause-commit-typed-${clause2.type}`,
5573
5564
  className: cn(
5574
- "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",
5575
- highlightIdx === typedIdx && "bg-muted/60"
5565
+ "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",
5566
+ highlightIdx === typedIdx && "bg-muted/70 ring-1 ring-border"
5576
5567
  ),
5577
5568
  children: [
5578
5569
  /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "truncate", children: [
5579
5570
  "Search for ",
5580
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-semibold", children: [
5571
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-semibold text-foreground", children: [
5581
5572
  "\u201C",
5582
5573
  typedValue,
5583
5574
  "\u201D"
5584
5575
  ] })
5585
5576
  ] }),
5586
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground/60 ml-1 shrink-0", children: "\u21B5" })
5577
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground/60 ml-1 shrink-0 font-sans text-xs", children: "\u21B5" })
5587
5578
  ]
5588
5579
  }
5589
5580
  ),
@@ -5602,29 +5593,39 @@ function InlineClauseEditor({
5602
5593
  onMouseEnter: () => onHighlight(idx),
5603
5594
  onClick: () => onCommitValue(item.value),
5604
5595
  className: cn(
5605
- "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",
5606
- active ? "bg-primary/15 font-semibold" : "hover:bg-muted/60",
5607
- selected && "text-primary"
5596
+ "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",
5597
+ active ? "bg-primary/10 text-foreground font-medium" : "hover:bg-muted/60 text-muted-foreground hover:text-foreground",
5598
+ selected && "text-primary font-medium"
5608
5599
  ),
5609
5600
  children: [
5610
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: item.label }),
5611
- selected && /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { className: "w-3 h-3 shrink-0" })
5601
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-baseline justify-between gap-2 min-w-0 flex-1", children: [
5602
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: item.label }),
5603
+ 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 })
5604
+ ] }),
5605
+ selected && /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { className: "w-3.5 h-3.5 text-primary shrink-0 ml-1.5" })
5612
5606
  ]
5613
5607
  },
5614
5608
  item.value
5615
5609
  );
5616
5610
  }),
5617
- 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 })
5611
+ emptyText && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-2.5 py-2 text-sm sm:text-xs text-muted-foreground italic", children: emptyText })
5618
5612
  ]
5619
5613
  }
5620
5614
  ),
5621
- 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(
5615
+ (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: [
5616
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
5617
+ "\u2191\u2193 choose \xB7 Enter ",
5618
+ isMulti ? "toggle" : "set"
5619
+ ] }),
5620
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Tab next segment \xB7 Esc close" })
5621
+ ] }),
5622
+ isMulti && onDone && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pt-2 flex justify-end", children: /* @__PURE__ */ jsxRuntime.jsxs(
5622
5623
  "button",
5623
5624
  {
5624
5625
  type: "button",
5625
5626
  onClick: onDone,
5626
5627
  "data-testid": "wql-clause-editor-done",
5627
- 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",
5628
+ 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",
5628
5629
  children: [
5629
5630
  "Done",
5630
5631
  shownValues.length > 0 ? ` \xB7 ${shownValues.length} selected` : ""
@@ -5663,7 +5664,11 @@ var STATIC_OPTIONS = {
5663
5664
  groupby: GROUPBY_OPTIONS,
5664
5665
  metric: METRIC_OPTIONS,
5665
5666
  unit: UNIT_OPTIONS,
5666
- intensity: wodWikiWql.WQL_INTENSITY_TIERS.map((v) => ({ value: v, label: v }))
5667
+ intensity: wodWikiWql.WQL_INTENSITY_TIERS.map((v) => ({ value: v, label: v })),
5668
+ subscribed: [
5669
+ { value: "true", label: "Subscribed" },
5670
+ { value: "false", label: "All / unsubscribed" }
5671
+ ]
5667
5672
  };
5668
5673
  var CANONICAL_BLOCK_TYPES = ["wod", "query", "widget", "embed"];
5669
5674
  function canonicalSuggestionItems(values2) {
@@ -5909,7 +5914,7 @@ function WqlDiagnosticsStrip({
5909
5914
  "aria-live": "polite",
5910
5915
  className: cn(
5911
5916
  "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",
5912
- header ? valid ? "border-b border-primary/30 text-muted-foreground" : "border-b border-destructive/50 bg-destructive/5 text-destructive" : cn(
5917
+ header ? valid ? "border-b border-border/60 bg-muted/20 text-muted-foreground" : "border-b border-destructive/40 bg-destructive/5 text-destructive" : cn(
5913
5918
  "rounded-lg border",
5914
5919
  valid ? "bg-muted/30 border-border text-muted-foreground" : "bg-destructive/5 border-destructive/30 text-destructive"
5915
5920
  ),
@@ -6236,23 +6241,128 @@ var completionIcons = {
6236
6241
  value: server.renderToStaticMarkup(React2.createElement(lucideReact.Tag, { size: 16 }))
6237
6242
  };
6238
6243
  var SLOT_NODES = { Head: true, Filter: true, TagValue: true, Rollup: true };
6244
+ var activeSegmentEffect = state.StateEffect.define();
6245
+ var activeSegmentField = state.StateField.define({
6246
+ create: () => view.Decoration.none,
6247
+ update(decorations, transaction) {
6248
+ const mapped = decorations.map(transaction.changes);
6249
+ for (const effect of transaction.effects) {
6250
+ if (!effect.is(activeSegmentEffect)) continue;
6251
+ const range = effect.value;
6252
+ if (!range || range.from >= range.to) return view.Decoration.none;
6253
+ return view.Decoration.set([view.Decoration.mark({ class: "wql-active-segment" }).range(range.from, range.to)]);
6254
+ }
6255
+ return mapped;
6256
+ },
6257
+ provide: (field) => view.EditorView.decorations.from(field)
6258
+ });
6259
+ function findClauseRange(state, clause2) {
6260
+ if (!clause2) return void 0;
6261
+ let range;
6262
+ const tree = language.syntaxTree(state);
6263
+ if (clause2.filterIndex !== void 0) {
6264
+ let index = 0;
6265
+ tree.iterate({
6266
+ enter: (node) => {
6267
+ if (node.name === "Filter") {
6268
+ if (index === clause2.filterIndex) {
6269
+ range = { from: node.from, to: node.to };
6270
+ return false;
6271
+ }
6272
+ index++;
6273
+ }
6274
+ return true;
6275
+ }
6276
+ });
6277
+ return range;
6278
+ }
6279
+ if (["kind", "target", "agg", "metric"].includes(clause2.type)) {
6280
+ tree.iterate({
6281
+ enter: (node) => {
6282
+ if (node.name === "Head") {
6283
+ range = { from: node.from, to: node.to };
6284
+ return false;
6285
+ }
6286
+ return true;
6287
+ }
6288
+ });
6289
+ return range;
6290
+ }
6291
+ if (clause2.type === "groupby") {
6292
+ tree.iterate({
6293
+ enter: (node) => {
6294
+ if (node.name === "GroupBy") {
6295
+ range = { from: node.from, to: node.to };
6296
+ return false;
6297
+ }
6298
+ return true;
6299
+ }
6300
+ });
6301
+ return range;
6302
+ }
6303
+ if (clause2.type === "rollup") {
6304
+ tree.iterate({
6305
+ enter: (node) => {
6306
+ if (node.name === "Rollup") {
6307
+ range = { from: node.from, to: node.to };
6308
+ return false;
6309
+ }
6310
+ return true;
6311
+ }
6312
+ });
6313
+ return range;
6314
+ }
6315
+ if (clause2.type === "time") {
6316
+ const doc = state.doc.toString();
6317
+ const match = /\b(last\s+\d+[dw]|from\s+\S+(?:\s+to\s+\S+)?)/i.exec(doc);
6318
+ if (match) return { from: match.index, to: match.index + match[0].length };
6319
+ }
6320
+ return void 0;
6321
+ }
6239
6322
  function slotPositions(state) {
6240
6323
  const slots = [];
6241
6324
  language.syntaxTree(state).iterate({
6242
6325
  enter: (node) => {
6243
6326
  if (SLOT_NODES[node.name]) slots.push(node.from);
6244
- else if (node.name === "Word" && node.node.parent?.name === "GroupBy") slots.push(node.from);
6327
+ else if (node.name === "Word" && ["GroupBy", "Dimension"].includes(node.node.parent?.name ?? "")) slots.push(node.from);
6245
6328
  return true;
6246
6329
  }
6247
6330
  });
6248
6331
  return slots.sort((a, b) => a - b);
6249
6332
  }
6250
- function advanceSlot(view, dir) {
6333
+ function activateSlot(view, position, onActivate) {
6334
+ if (!onActivate) return;
6335
+ let node = language.syntaxTree(view.state).resolveInner(position, 1);
6336
+ while (node.parent && !["Head", "Filter", "GroupBy", "Rollup"].includes(node.name)) node = node.parent;
6337
+ if (!["Head", "Filter", "GroupBy", "Rollup"].includes(node.name)) return;
6338
+ let filterIndex;
6339
+ if (node.name === "Filter") {
6340
+ filterIndex = 0;
6341
+ language.syntaxTree(view.state).iterate({
6342
+ enter: (candidate) => {
6343
+ if (candidate.name === "Filter") {
6344
+ if (candidate.from === node.from) return false;
6345
+ if (candidate.from < node.from) filterIndex += 1;
6346
+ }
6347
+ return true;
6348
+ }
6349
+ });
6350
+ }
6351
+ let text = view.state.sliceDoc(node.from, node.to);
6352
+ if (node.name === "Head") {
6353
+ const line = view.state.doc.lineAt(position);
6354
+ const word = Array.from(line.text.matchAll(/[\w.*-]+/g)).find((match) => line.from + match.index <= position && position <= line.from + match.index + match[0].length);
6355
+ if (word) text = word[0];
6356
+ }
6357
+ onActivate({ node: node.name, filterIndex, from: node.from, to: node.to, text });
6358
+ }
6359
+ function advanceSlot(view, dir, onActivate) {
6251
6360
  const head = view.state.selection.main.head;
6252
6361
  const slots = slotPositions(view.state);
6253
6362
  const next = dir > 0 ? slots.find((slot) => slot > head) : [...slots].reverse().find((slot) => slot < head);
6254
6363
  if (next === void 0) return false;
6255
6364
  view.dispatch({ selection: { anchor: next }, scrollIntoView: true });
6365
+ activateSlot(view, next, onActivate);
6256
6366
  return true;
6257
6367
  }
6258
6368
  function wqlLint(view) {
@@ -6295,7 +6405,7 @@ function wqlLint(view) {
6295
6405
  });
6296
6406
  return out;
6297
6407
  }
6298
- function wqlEditorKeymap(onSubmit, onEscape) {
6408
+ function wqlEditorKeymap(onSubmit, onEscape, onSlotActivate) {
6299
6409
  return [
6300
6410
  { key: "Mod-Enter", run: () => {
6301
6411
  onSubmit();
@@ -6319,7 +6429,7 @@ function wqlEditorKeymap(onSubmit, onEscape) {
6319
6429
  run: (view) => {
6320
6430
  if (view.compositionStarted) return false;
6321
6431
  const accepted = autocomplete.acceptCompletion(view);
6322
- return advanceSlot(view, 1) || accepted;
6432
+ return advanceSlot(view, 1, onSlotActivate) || accepted;
6323
6433
  }
6324
6434
  },
6325
6435
  {
@@ -6327,7 +6437,7 @@ function wqlEditorKeymap(onSubmit, onEscape) {
6327
6437
  run: (view) => {
6328
6438
  if (view.compositionStarted) return false;
6329
6439
  const accepted = autocomplete.acceptCompletion(view);
6330
- return advanceSlot(view, -1) || accepted;
6440
+ return advanceSlot(view, -1, onSlotActivate) || accepted;
6331
6441
  }
6332
6442
  },
6333
6443
  {
@@ -6346,11 +6456,11 @@ function wqlEditorKeymap(onSubmit, onEscape) {
6346
6456
  ...commands.historyKeymap
6347
6457
  ];
6348
6458
  }
6349
- function WqlTextEditor({ value, onChange, onSubmit, onEscape, placeholder, autoFocus = false, dense = false }) {
6459
+ function WqlTextEditor({ value, onChange, onSubmit, onEscape, placeholder, autoFocus = false, dense = false, onSlotActivate, activeRange, activeClause, className }) {
6350
6460
  const host = React2.useRef(null);
6351
6461
  const view$1 = React2.useRef(void 0);
6352
- const callbacks = React2.useRef({ onChange, onSubmit, onEscape });
6353
- callbacks.current = { onChange, onSubmit, onEscape };
6462
+ const callbacks = React2.useRef({ onChange, onSubmit, onEscape, onSlotActivate });
6463
+ callbacks.current = { onChange, onSubmit, onEscape, onSlotActivate };
6354
6464
  const replacing = React2.useRef(false);
6355
6465
  const initialValue = React2.useRef(value);
6356
6466
  React2.useEffect(() => {
@@ -6367,6 +6477,7 @@ function WqlTextEditor({ value, onChange, onSubmit, onEscape, placeholder, autoF
6367
6477
  language.syntaxHighlighting(wodWikiWql.wqlHighlightStyle),
6368
6478
  commands.history(),
6369
6479
  view.EditorView.lineWrapping,
6480
+ activeSegmentField,
6370
6481
  ...placeholder ? [view.placeholder(placeholder)] : [],
6371
6482
  view.EditorView.contentAttributes.of({ "aria-label": "WQL", "data-testid": "wql-text-input" }),
6372
6483
  autocomplete.autocompletion({
@@ -6387,11 +6498,12 @@ function WqlTextEditor({ value, onChange, onSubmit, onEscape, placeholder, autoF
6387
6498
  lint.linter(wqlLint),
6388
6499
  view.keymap.of(wqlEditorKeymap(
6389
6500
  () => callbacks.current.onSubmit(),
6390
- () => callbacks.current.onEscape()
6501
+ () => callbacks.current.onEscape(),
6502
+ (slot) => callbacks.current.onSlotActivate?.(slot)
6391
6503
  )),
6392
6504
  view.EditorView.domEventHandlers({
6393
- // Clicking a head/target/filter token selects it and lists its alternatives;
6394
- // typing then replaces it, picking swaps it.
6505
+ // Host composers open the contextual writer; standalone editors
6506
+ // retain token selection and in-row completion.
6395
6507
  mouseup: (event, editor2) => {
6396
6508
  if (event.button !== 0) return false;
6397
6509
  let { from, to } = editor2.state.selection.main;
@@ -6402,11 +6514,18 @@ function WqlTextEditor({ value, onChange, onSubmit, onEscape, placeholder, autoF
6402
6514
  from = line.from + word.index;
6403
6515
  to = from + word[0].length;
6404
6516
  let node = language.syntaxTree(editor2.state).resolveInner(to, -1);
6405
- while (node.parent && !["Head", "Filter"].includes(node.name)) node = node.parent;
6406
- if (!["Head", "Filter"].includes(node.name)) return false;
6407
- editor2.dispatch({ selection: { anchor: from, head: to } });
6517
+ while (node.parent && !["Head", "Filter", "GroupBy", "Rollup"].includes(node.name)) node = node.parent;
6518
+ if (!["Head", "Filter", "GroupBy", "Rollup"].includes(node.name)) return false;
6519
+ if (!callbacks.current.onSlotActivate) {
6520
+ editor2.dispatch({ selection: { anchor: from, head: to } });
6521
+ }
6522
+ }
6523
+ if (callbacks.current.onSlotActivate) {
6524
+ autocomplete.closeCompletion(editor2);
6525
+ activateSlot(editor2, from, (slot) => callbacks.current.onSlotActivate?.(slot));
6526
+ } else {
6527
+ autocomplete.startCompletion(editor2);
6408
6528
  }
6409
- autocomplete.startCompletion(editor2);
6410
6529
  return false;
6411
6530
  },
6412
6531
  keydown: (event, editor2) => {
@@ -6426,7 +6545,8 @@ function WqlTextEditor({ value, onChange, onSubmit, onEscape, placeholder, autoF
6426
6545
  '.cm-line span[class*="tok-"]:hover, .cm-line span[class^="tok-"]:hover': { fontWeight: "700", textShadow: "0 0 0.5px currentColor", cursor: "pointer" },
6427
6546
  ".cm-scroller": { overflow: "auto" },
6428
6547
  ".cm-placeholder": { color: "hsl(var(--muted-foreground))" },
6429
- ".wql-completion-icon": { display: "inline-flex", flexShrink: "0", color: "hsl(var(--primary))" }
6548
+ ".wql-completion-icon": { display: "inline-flex", flexShrink: "0", color: "hsl(var(--primary))" },
6549
+ ".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" }
6430
6550
  }),
6431
6551
  autocompleteTooltipTheme
6432
6552
  ]
@@ -6446,7 +6566,13 @@ function WqlTextEditor({ value, onChange, onSubmit, onEscape, placeholder, autoF
6446
6566
  editor.dispatch({ changes: { from: 0, to: editor.state.doc.length, insert: value } });
6447
6567
  replacing.current = false;
6448
6568
  }, [value]);
6449
- 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" });
6569
+ React2.useEffect(() => {
6570
+ const editor = view$1.current;
6571
+ if (!editor) return;
6572
+ const resolvedRange = activeRange ?? findClauseRange(editor.state, activeClause);
6573
+ editor.dispatch({ effects: activeSegmentEffect.of(resolvedRange ?? null) });
6574
+ }, [activeRange, activeClause, value]);
6575
+ 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" });
6450
6576
  }
6451
6577
  function WqlComposer({
6452
6578
  initialQuery,
@@ -6468,7 +6594,8 @@ function WqlComposer({
6468
6594
  preferredChoices,
6469
6595
  autoFocus = false,
6470
6596
  placeholder = "Search text or enter WQL",
6471
- className
6597
+ className,
6598
+ segmentEditRequest
6472
6599
  }) {
6473
6600
  const [desktop, setDesktop] = React2.useState(false);
6474
6601
  const [base, setBase] = React2.useState(query ?? initialQuery ?? ":note");
@@ -6550,7 +6677,7 @@ function WqlComposer({
6550
6677
  }, [autoFocus]);
6551
6678
  React2.useEffect(() => {
6552
6679
  if (active.kind === "value") pickerRef.current?.focus();
6553
- }, [active.kind]);
6680
+ }, [active]);
6554
6681
  React2.useEffect(() => {
6555
6682
  if (highlight >= 0) document.getElementById(`${listId}-option-${highlight}`)?.scrollIntoView?.({ block: "nearest" });
6556
6683
  }, [highlight, listId]);
@@ -6576,14 +6703,14 @@ function WqlComposer({
6576
6703
  if (openerRef.current?.isConnected) openerRef.current.focus();
6577
6704
  else inputRef.current?.focus();
6578
6705
  };
6579
- const openEditor = (clause2) => {
6706
+ const openEditor = (clause2, range) => {
6580
6707
  if (pending && currentRef.current.valid) commit(currentRef.current);
6581
6708
  openerRef.current = document.activeElement instanceof HTMLElement ? document.activeElement : null;
6582
6709
  if (clause2.type === "where" || clause2.type === "pipes") {
6583
6710
  setActive({ kind: "wql" });
6584
6711
  commit(currentRef.current);
6585
6712
  } else {
6586
- setActive({ kind: "value", clause: clause2 });
6713
+ setActive({ kind: "value", clause: clause2, range });
6587
6714
  setSearch("");
6588
6715
  }
6589
6716
  setHighlight(-1);
@@ -6594,6 +6721,24 @@ function WqlComposer({
6594
6721
  if (pending) commit(resolveQueryDraft(base));
6595
6722
  openEditor(existing ?? { id: `new-${type}`, type, ...getClauseMeta(type), value: "" });
6596
6723
  };
6724
+ const openFieldRef = React2.useRef(openField);
6725
+ openFieldRef.current = openField;
6726
+ React2.useEffect(() => {
6727
+ if (segmentEditRequest) openFieldRef.current(segmentEditRequest.type);
6728
+ }, [segmentEditRequest]);
6729
+ const activateWqlSlot = (slot) => {
6730
+ 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;
6731
+ if (clause2) openEditor(clause2, { from: slot.from, to: slot.to });
6732
+ };
6733
+ const moveEditor = (direction) => {
6734
+ if (active.kind !== "value") return false;
6735
+ const visible = pills.filter((pill) => !hidden.has(pill.type));
6736
+ const index = visible.findIndex((pill) => pill.id === active.clause.id);
6737
+ const next = visible[index + direction];
6738
+ if (!next) return false;
6739
+ openEditor(next);
6740
+ return true;
6741
+ };
6597
6742
  const updateClause = (clause2, value) => {
6598
6743
  if (clause2.type === "kind" || clause2.type === "target") {
6599
6744
  const next2 = pivotQuery(currentRef.current.wql, clause2.type, value ?? "");
@@ -6645,6 +6790,13 @@ function WqlComposer({
6645
6790
  onSubmit?.(draft.wql);
6646
6791
  };
6647
6792
  const handleKeys = (event) => {
6793
+ if (active.kind === "value" && event.key === "Tab" && !event.nativeEvent.isComposing) {
6794
+ if (moveEditor(event.shiftKey ? -1 : 1)) {
6795
+ event.preventDefault();
6796
+ event.stopPropagation();
6797
+ }
6798
+ return;
6799
+ }
6648
6800
  if (event.nativeEvent.isComposing) return;
6649
6801
  if (event.key === "Enter" && (event.ctrlKey || event.metaKey)) {
6650
6802
  event.preventDefault();
@@ -6709,119 +6861,285 @@ function WqlComposer({
6709
6861
  diagnosticsActions
6710
6862
  ] });
6711
6863
  const diagnostics = { ...resolved, valid: resolved.valid && !error, error: error ?? resolved.error };
6712
- 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: [
6713
- diagnosticsPosition === "top" && showDiagnostics && /* @__PURE__ */ jsxRuntime.jsx(WqlDiagnosticsStrip, { diagnostics, stages, actions: action, variant: "header" }),
6714
- (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: () => {
6715
- if (active.kind === "closed") return false;
6716
- closeEditor();
6717
- return true;
6718
- }, autoFocus }) }),
6719
- /* @__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) => {
6720
- if (!["Delete", "Backspace"].includes(event.key) || !(event.target instanceof HTMLButtonElement)) return;
6721
- const buttons = Array.from(event.currentTarget.querySelectorAll('[data-testid^="token-slot-"]:not([data-testid^="token-slot-remove-"]):not([data-testid^="token-slot-value-"])'));
6722
- const pill = pills.filter((item) => !hidden.has(item.type))[buttons.indexOf(event.target)];
6723
- if (!pill || ["kind", "target", "agg", "metric"].includes(pill.type)) return;
6724
- event.preventDefault();
6725
- event.stopPropagation();
6726
- setUndo(currentRef.current.wql);
6727
- updateClause(pill, null);
6728
- inputRef.current?.focus();
6729
- }, children: [
6730
- 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 : () => {
6731
- setUndo(currentRef.current.wql);
6732
- updateClause(pill, null);
6733
- } }, pill.id)),
6734
- !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) => {
6735
- const text = event.target.value;
6736
- setPending(text);
6737
- setHighlight(-1);
6738
- setError(void 0);
6739
- notify(resolveQueryDraft(base, text));
6740
- }, 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" }),
6741
- customSlots
6742
- ] }),
6743
- (!desktop || undo) && /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-wql-inline": desktop && compact ? "" : void 0, className: "flex flex-wrap items-center gap-2", children: [
6744
- !desktop && /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", onClick: () => {
6745
- commit(currentRef.current);
6746
- if (active.kind === "wql") closeEditor();
6747
- else setActive({ kind: "wql" });
6748
- }, disabled: active.kind === "wql" && !guided, className: "min-h-12 rounded-lg px-3", children: active.kind === "wql" ? "Guided controls" : "Edit WQL" }),
6749
- undo && /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", onClick: () => {
6750
- commit(resolveQueryDraft(undo));
6751
- setUndo(void 0);
6752
- }, 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" })
6753
- ] }),
6754
- 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) => {
6755
- if (event.key === "Escape" || event.key === "Enter" && (event.ctrlKey || event.metaKey)) handleKeys(event);
6756
- }, children: [
6757
- /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", onClick: closeEditor, className: "min-h-12 px-3", children: "Back" }),
6758
- /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "block text-sm", children: [
6759
- "Search ",
6760
- getClauseMeta(active.clause.type).label,
6761
- /* @__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) => {
6762
- setSearch(event.target.value);
6763
- setHighlight(-1);
6764
- setError(void 0);
6765
- }, onKeyDown: handleKeys, className: "min-h-12 w-full rounded border border-border bg-background px-3 text-base", "data-testid": "wql-picker-search" })
6766
- ] }),
6767
- active.kind === "value" && active.clause.filterIndex !== void 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap gap-2", children: [
6768
- /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "flex min-h-12 items-center gap-2", children: [
6769
- /* @__PURE__ */ jsxRuntime.jsx("input", { type: "checkbox", checked: active.clause.negate ?? false, onChange: (event) => updateClause({ ...active.clause, negate: event.target.checked }, active.clause.value) }),
6770
- "Exclude"
6864
+ const visiblePills = pills.filter((pill) => !hidden.has(pill.type));
6865
+ const activePillIndex = active.kind === "value" ? visiblePills.findIndex((pill) => pill.id === active.clause.id) : -1;
6866
+ return /* @__PURE__ */ jsxRuntime.jsxs(
6867
+ "div",
6868
+ {
6869
+ className: cn(
6870
+ "min-w-0",
6871
+ 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",
6872
+ className
6873
+ ),
6874
+ "data-testid": "wql-composer-root",
6875
+ children: [
6876
+ diagnosticsPosition === "top" && showDiagnostics && /* @__PURE__ */ jsxRuntime.jsx(WqlDiagnosticsStrip, { diagnostics, stages, actions: action, variant: "header" }),
6877
+ (desktop || compact || active.kind === "wql" || !guided) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-w-0 w-full items-center gap-2", children: [
6878
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-w-0 flex-1", children: /* @__PURE__ */ jsxRuntime.jsx(
6879
+ WqlTextEditor,
6880
+ {
6881
+ value: resolved.wql,
6882
+ placeholder,
6883
+ dense: compact,
6884
+ className: compact ? "min-w-0 w-full bg-transparent" : void 0,
6885
+ activeRange: active.kind === "value" ? active.range : void 0,
6886
+ activeClause: active.kind === "value" ? active.clause : void 0,
6887
+ onSlotActivate: activateWqlSlot,
6888
+ onChange: (text) => commit(resolveQueryDraft(text)),
6889
+ onSubmit: submit,
6890
+ onEscape: () => {
6891
+ if (active.kind === "closed") return false;
6892
+ closeEditor();
6893
+ return true;
6894
+ },
6895
+ autoFocus
6896
+ }
6897
+ ) }),
6898
+ desktop && compact && undo && /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", onClick: () => {
6899
+ commit(resolveQueryDraft(undo));
6900
+ setUndo(void 0);
6901
+ }, className: "min-h-9 shrink-0 rounded-lg px-2.5 text-xs text-muted-foreground hover:text-foreground", children: "Undo removal" })
6771
6902
  ] }),
6772
- /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "flex min-h-12 items-center gap-2", children: [
6773
- /* @__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("|")) }),
6774
- "Starts with"
6775
- ] })
6776
- ] }),
6777
- active.kind === "value" && active.clause.type === "time" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-1 gap-2 sm:grid-cols-2", children: [
6778
- /* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
6779
- "Start date",
6780
- /* @__PURE__ */ jsxRuntime.jsx("input", { type: "date", "aria-label": "Start date", className: "min-h-12 w-full bg-background text-base", onChange: (event) => {
6781
- const end = draftWindow();
6782
- updateClause(active.clause, `from ${event.target.value}${end?.kind === "range" && end.end ? ` to ${end.end}` : ""}`);
6783
- }, value: astWindow?.kind === "range" ? astWindow.start : "" })
6903
+ /* @__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) => {
6904
+ if (!["Delete", "Backspace"].includes(event.key) || !(event.target instanceof HTMLButtonElement)) return;
6905
+ const buttons = Array.from(event.currentTarget.querySelectorAll('[data-testid^="token-slot-"]:not([data-testid^="token-slot-remove-"]):not([data-testid^="token-slot-value-"])'));
6906
+ const pill = visiblePills[buttons.indexOf(event.target)];
6907
+ if (!pill || ["kind", "target", "agg", "metric"].includes(pill.type)) return;
6908
+ event.preventDefault();
6909
+ event.stopPropagation();
6910
+ setUndo(currentRef.current.wql);
6911
+ updateClause(pill, null);
6912
+ inputRef.current?.focus();
6913
+ }, children: [
6914
+ 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 : () => {
6915
+ setUndo(currentRef.current.wql);
6916
+ updateClause(pill, null);
6917
+ } }, pill.id)),
6918
+ !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) => {
6919
+ const text = event.target.value;
6920
+ setPending(text);
6921
+ setHighlight(-1);
6922
+ setError(void 0);
6923
+ notify(resolveQueryDraft(base, text));
6924
+ }, 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" }),
6925
+ customSlots
6784
6926
  ] }),
6785
- /* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
6786
- "End date",
6787
- /* @__PURE__ */ jsxRuntime.jsx("input", { type: "date", "aria-label": "End date", className: "min-h-12 w-full bg-background text-base", onChange: (event) => {
6788
- const window2 = draftWindow();
6789
- if (window2?.kind === "range") updateClause(active.clause, `from ${window2.start}${event.target.value ? ` to ${event.target.value}` : ""}`);
6790
- else setError("Choose a start date first");
6791
- }, value: astWindow?.kind === "range" ? astWindow.end ?? "" : "" })
6792
- ] })
6793
- ] }),
6794
- 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("|")) }))
6795
- ] }),
6796
- 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: [
6797
- 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: [
6798
- /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
6799
- pills.some((pill) => pill.type === type) ? "Edit" : "Add",
6800
- " ",
6801
- getClauseMeta(type).label
6927
+ (!desktop && !compact || !compact && undo) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
6928
+ !desktop && !compact && /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", onClick: () => {
6929
+ commit(currentRef.current);
6930
+ if (active.kind === "wql") closeEditor();
6931
+ else setActive({ kind: "wql" });
6932
+ }, disabled: active.kind === "wql" && !guided, className: "min-h-12 rounded-lg px-3", children: active.kind === "wql" ? "Guided controls" : "Edit WQL" }),
6933
+ undo && /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", onClick: () => {
6934
+ commit(resolveQueryDraft(undo));
6935
+ setUndo(void 0);
6936
+ }, className: "min-h-12 rounded-lg px-3", children: "Undo removal" })
6802
6937
  ] }),
6803
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-muted-foreground", children: type })
6804
- ] }, type)),
6805
- 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: [
6806
- "Search text ",
6807
- pending
6808
- ] })
6809
- ] }),
6810
- pivot && /* @__PURE__ */ jsxRuntime.jsxs("div", { role: "alertdialog", "aria-label": "Confirm query pivot", className: "rounded-xl border border-border p-3", children: [
6811
- /* @__PURE__ */ jsxRuntime.jsx("p", { children: "Changing the query removes incompatible clauses:" }),
6812
- /* @__PURE__ */ jsxRuntime.jsx("ul", { children: pivot.removed.map((item) => /* @__PURE__ */ jsxRuntime.jsx("li", { children: item }, item)) }),
6813
- /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "min-h-12 px-3", onClick: () => setPivot(void 0), children: "Cancel" }),
6814
- /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "min-h-12 px-3", onClick: () => {
6815
- commit(pivot.draft);
6816
- setPivot(void 0);
6817
- closeEditor();
6818
- }, children: "Change query" })
6819
- ] }),
6820
- !showDiagnostics && (error ?? resolved.error) && /* @__PURE__ */ jsxRuntime.jsx("p", { role: "alert", className: "text-sm text-destructive", children: error ?? resolved.error }),
6821
- !guided && !resolved.error && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground", children: "Use Edit WQL to preserve every clause." }),
6822
- 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 }),
6823
- 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
6824
- ] });
6938
+ active.kind === "value" && /* @__PURE__ */ jsxRuntime.jsxs(
6939
+ "div",
6940
+ {
6941
+ "data-testid": "wql-field-picker",
6942
+ className: cn(
6943
+ "min-w-0",
6944
+ 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"
6945
+ ),
6946
+ onKeyDown: (event) => {
6947
+ if (event.key === "Escape" || event.key === "Enter" && (event.ctrlKey || event.metaKey)) handleKeys(event);
6948
+ },
6949
+ children: [
6950
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
6951
+ /* @__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: [
6952
+ /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true", className: "text-xs", children: getClauseMeta(active.clause.type).icon }),
6953
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: getClauseMeta(active.clause.type).label })
6954
+ ] }),
6955
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "sr-only", children: [
6956
+ "Search ",
6957
+ getClauseMeta(active.clause.type).label
6958
+ ] }),
6959
+ /* @__PURE__ */ jsxRuntime.jsx(
6960
+ "input",
6961
+ {
6962
+ ref: pickerRef,
6963
+ type: "search",
6964
+ role: "combobox",
6965
+ "aria-label": `Search ${getClauseMeta(active.clause.type).label}`,
6966
+ "aria-controls": listId,
6967
+ "aria-expanded": "true",
6968
+ "aria-activedescendant": highlight >= 0 ? `${listId}-option-${highlight}` : void 0,
6969
+ value: search,
6970
+ placeholder: active.clause.value ? `Current: ${active.clause.value}` : `Type value for ${getClauseMeta(active.clause.type).label}\u2026`,
6971
+ onChange: (event) => {
6972
+ setSearch(event.target.value);
6973
+ setHighlight(-1);
6974
+ setError(void 0);
6975
+ },
6976
+ onKeyDown: handleKeys,
6977
+ 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",
6978
+ "data-testid": "wql-picker-search"
6979
+ }
6980
+ ),
6981
+ 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: [
6982
+ /* @__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: [
6983
+ /* @__PURE__ */ jsxRuntime.jsx(
6984
+ "input",
6985
+ {
6986
+ type: "checkbox",
6987
+ checked: active.clause.negate ?? false,
6988
+ onChange: (event) => updateClause({ ...active.clause, negate: event.target.checked }, active.clause.value),
6989
+ className: "size-3 rounded border-border text-primary focus:ring-primary"
6990
+ }
6991
+ ),
6992
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Exclude" })
6993
+ ] }),
6994
+ /* @__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: [
6995
+ /* @__PURE__ */ jsxRuntime.jsx(
6996
+ "input",
6997
+ {
6998
+ type: "checkbox",
6999
+ checked: active.clause.value.split("|").every((value) => value.endsWith("*")),
7000
+ onChange: (event) => updateClause(active.clause, active.clause.value.split("|").map((value) => `${value.replace(/\*$/, "")}${event.target.checked ? "*" : ""}`).join("|")),
7001
+ className: "size-3 rounded border-border text-primary focus:ring-primary"
7002
+ }
7003
+ ),
7004
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Starts with" })
7005
+ ] })
7006
+ ] }),
7007
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex shrink-0 items-center gap-1", children: [
7008
+ /* @__PURE__ */ jsxRuntime.jsx(
7009
+ "button",
7010
+ {
7011
+ type: "button",
7012
+ "aria-label": "Previous WQL segment",
7013
+ "data-testid": "wql-segment-previous",
7014
+ disabled: activePillIndex <= 0,
7015
+ onClick: () => {
7016
+ if (moveEditor(-1)) pickerRef.current?.focus();
7017
+ },
7018
+ 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",
7019
+ children: "\u2190"
7020
+ }
7021
+ ),
7022
+ /* @__PURE__ */ jsxRuntime.jsx(
7023
+ "button",
7024
+ {
7025
+ type: "button",
7026
+ "aria-label": "Next WQL segment",
7027
+ "data-testid": "wql-segment-next",
7028
+ disabled: activePillIndex < 0 || activePillIndex >= visiblePills.length - 1,
7029
+ onClick: () => {
7030
+ if (moveEditor(1)) pickerRef.current?.focus();
7031
+ },
7032
+ 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",
7033
+ children: "\u2192"
7034
+ }
7035
+ ),
7036
+ /* @__PURE__ */ jsxRuntime.jsx(
7037
+ "button",
7038
+ {
7039
+ type: "button",
7040
+ "aria-label": "Close segment editor",
7041
+ onClick: closeEditor,
7042
+ 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",
7043
+ children: "\u2715"
7044
+ }
7045
+ )
7046
+ ] })
7047
+ ] }),
7048
+ 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: [
7049
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "block text-muted-foreground", children: [
7050
+ "Start date",
7051
+ /* @__PURE__ */ jsxRuntime.jsx(
7052
+ "input",
7053
+ {
7054
+ type: "date",
7055
+ "aria-label": "Start date",
7056
+ className: "mt-1 min-h-8 w-full rounded-md border border-border/80 bg-background px-2.5 text-xs text-foreground",
7057
+ onChange: (event) => {
7058
+ const end = draftWindow();
7059
+ updateClause(active.clause, `from ${event.target.value}${end?.kind === "range" && end.end ? ` to ${end.end}` : ""}`);
7060
+ },
7061
+ value: astWindow?.kind === "range" ? astWindow.start : ""
7062
+ }
7063
+ )
7064
+ ] }),
7065
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "block text-muted-foreground", children: [
7066
+ "End date",
7067
+ /* @__PURE__ */ jsxRuntime.jsx(
7068
+ "input",
7069
+ {
7070
+ type: "date",
7071
+ "aria-label": "End date",
7072
+ className: "mt-1 min-h-8 w-full rounded-md border border-border/80 bg-background px-2.5 text-xs text-foreground",
7073
+ onChange: (event) => {
7074
+ const window2 = draftWindow();
7075
+ if (window2?.kind === "range") updateClause(active.clause, `from ${window2.start}${event.target.value ? ` to ${event.target.value}` : ""}`);
7076
+ else setError("Choose a start date first");
7077
+ },
7078
+ value: astWindow?.kind === "range" ? astWindow.end ?? "" : ""
7079
+ }
7080
+ )
7081
+ ] })
7082
+ ] }),
7083
+ active.kind === "value" && (customEditor ? /* @__PURE__ */ jsxRuntime.jsx(
7084
+ customEditor.Editor,
7085
+ {
7086
+ value: customEditor.parseValue ? customEditor.parseValue(active.clause.value) : active.clause.value,
7087
+ onChange: (value) => updateClause(active.clause, customEditor.formatValue ? customEditor.formatValue(value) : String(value)),
7088
+ onClose: closeEditor
7089
+ }
7090
+ ) : /* @__PURE__ */ jsxRuntime.jsx(
7091
+ InlineClauseEditor,
7092
+ {
7093
+ clause: active.clause,
7094
+ filteredItems: preferredItems,
7095
+ selectedValues: values2.selectedValues,
7096
+ isMulti: values2.isMulti,
7097
+ typedValue: values2.typedValue,
7098
+ canCommitTyped: values2.canCommitTyped,
7099
+ emptyText: values2.emptyText,
7100
+ highlightIdx: highlight,
7101
+ onHighlight: setHighlight,
7102
+ onCommitValue: chooseValue,
7103
+ onCommitTyped: chooseValue,
7104
+ listId,
7105
+ onDone: closeEditor,
7106
+ onReorder: (dimensions) => updateClause(active.clause, dimensions.join("|"))
7107
+ }
7108
+ ))
7109
+ ]
7110
+ }
7111
+ ),
7112
+ 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: [
7113
+ 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: [
7114
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
7115
+ pills.some((pill) => pill.type === type) ? "Edit" : "Add",
7116
+ " ",
7117
+ getClauseMeta(type).label
7118
+ ] }),
7119
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-muted-foreground", children: type })
7120
+ ] }, type)),
7121
+ 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: [
7122
+ "Search text ",
7123
+ pending
7124
+ ] })
7125
+ ] }),
7126
+ pivot && /* @__PURE__ */ jsxRuntime.jsxs("div", { role: "alertdialog", "aria-label": "Confirm query pivot", className: "rounded-xl border border-border p-3", children: [
7127
+ /* @__PURE__ */ jsxRuntime.jsx("p", { children: "Changing the query removes incompatible clauses:" }),
7128
+ /* @__PURE__ */ jsxRuntime.jsx("ul", { children: pivot.removed.map((item) => /* @__PURE__ */ jsxRuntime.jsx("li", { children: item }, item)) }),
7129
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "min-h-12 px-3", onClick: () => setPivot(void 0), children: "Cancel" }),
7130
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "min-h-12 px-3", onClick: () => {
7131
+ commit(pivot.draft);
7132
+ setPivot(void 0);
7133
+ closeEditor();
7134
+ }, children: "Change query" })
7135
+ ] }),
7136
+ !showDiagnostics && (error ?? resolved.error) && /* @__PURE__ */ jsxRuntime.jsx("p", { role: "alert", className: "text-sm text-destructive", children: error ?? resolved.error }),
7137
+ !guided && !resolved.error && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground", children: "Use Edit WQL to preserve every clause." }),
7138
+ 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 }),
7139
+ 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
7140
+ ]
7141
+ }
7142
+ );
6825
7143
  }
6826
7144
  function WqlQueryInspectorModal({
6827
7145
  isOpen,