@reopt-ai/opt-ui 1.6.0 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/COMPONENT_CATALOG.md +95 -12
  2. package/dist/app.css +24 -0
  3. package/dist/core/index.cjs +3 -3
  4. package/dist/core/index.d.cts +1 -1
  5. package/dist/core/index.d.ts +1 -1
  6. package/dist/core/index.js +2 -2
  7. package/dist/docs/02-components/03-shells.md +104 -11
  8. package/dist/docs/02-components/index.md +3 -3
  9. package/dist/docs/05-migration/01-breaking-changes.md +18 -1
  10. package/dist/field-sidebar-BTZCBviA.d.cts +1031 -0
  11. package/dist/field-sidebar-BTZCBviA.d.cts.map +1 -0
  12. package/dist/field-sidebar-CB8vjvUM.d.ts +1031 -0
  13. package/dist/field-sidebar-CB8vjvUM.d.ts.map +1 -0
  14. package/dist/{workflow-canvas-BGBAcjf-.js → field-sidebar-Cs-J-5VE.js} +1882 -52
  15. package/dist/field-sidebar-Cs-J-5VE.js.map +1 -0
  16. package/dist/{workflow-canvas-3Jeevkgy.cjs → field-sidebar-CzIQX70-.cjs} +2010 -48
  17. package/dist/id-registry.cjs +41 -1
  18. package/dist/id-registry.d.cts +1 -1
  19. package/dist/id-registry.d.cts.map +1 -1
  20. package/dist/id-registry.d.ts +1 -1
  21. package/dist/id-registry.d.ts.map +1 -1
  22. package/dist/id-registry.js +41 -1
  23. package/dist/id-registry.js.map +1 -1
  24. package/dist/id-registry.json +85 -0
  25. package/dist/index.cjs +142 -56
  26. package/dist/index.d.cts +149 -80
  27. package/dist/index.d.cts.map +1 -1
  28. package/dist/index.d.ts +149 -80
  29. package/dist/index.d.ts.map +1 -1
  30. package/dist/index.js +73 -11
  31. package/dist/index.js.map +1 -1
  32. package/dist/meta.cjs +292 -0
  33. package/dist/meta.d.cts.map +1 -1
  34. package/dist/meta.d.ts.map +1 -1
  35. package/dist/meta.js +292 -0
  36. package/dist/meta.js.map +1 -1
  37. package/dist/{pagination-By7Ozv3U.js → pagination-DWbvACuN.js} +93 -31
  38. package/dist/pagination-DWbvACuN.js.map +1 -0
  39. package/dist/{pagination-Uv3CnGJz.cjs → pagination-DjfxBcjX.cjs} +98 -36
  40. package/dist/query.cjs +386 -0
  41. package/dist/query.d.cts +141 -0
  42. package/dist/query.d.cts.map +1 -0
  43. package/dist/query.d.ts +141 -0
  44. package/dist/query.d.ts.map +1 -0
  45. package/dist/query.js +371 -0
  46. package/dist/query.js.map +1 -0
  47. package/dist/{shader-surface-BWhO2xkk.js → shader-surface-BUPZ0P-n.js} +22 -83
  48. package/dist/shader-surface-BUPZ0P-n.js.map +1 -0
  49. package/dist/{shader-surface-DIVWtpFl.d.ts → shader-surface-BfTP9c1Q.d.ts} +139 -139
  50. package/dist/shader-surface-BfTP9c1Q.d.ts.map +1 -0
  51. package/dist/{shader-surface-BK1GMt5_.d.cts → shader-surface-DRrcLrWf.d.cts} +139 -139
  52. package/dist/shader-surface-DRrcLrWf.d.cts.map +1 -0
  53. package/dist/{shader-surface-DxLLfrE6.cjs → shader-surface-Fav5K9UC.cjs} +20 -87
  54. package/dist/shells/index.cjs +31 -24
  55. package/dist/shells/index.d.cts +2 -2
  56. package/dist/shells/index.d.ts +2 -2
  57. package/dist/shells/index.js +3 -2
  58. package/dist/theme/presets/default.css +2 -2
  59. package/dist/theme/presets/minimal.css +2 -2
  60. package/dist/theme/presets/mono-dark.css +2 -2
  61. package/dist/theme/presets/natural.css +1 -1
  62. package/dist/theme/presets/pro.css +2 -2
  63. package/package.json +17 -6
  64. package/dist/pagination-By7Ozv3U.js.map +0 -1
  65. package/dist/shader-surface-BK1GMt5_.d.cts.map +0 -1
  66. package/dist/shader-surface-BWhO2xkk.js.map +0 -1
  67. package/dist/shader-surface-DIVWtpFl.d.ts.map +0 -1
  68. package/dist/workflow-canvas-BGBAcjf-.js.map +0 -1
  69. package/dist/workflow-canvas-Bar_O2SQ.d.ts +0 -494
  70. package/dist/workflow-canvas-Bar_O2SQ.d.ts.map +0 -1
  71. package/dist/workflow-canvas-DghDRtHn.d.cts +0 -494
  72. package/dist/workflow-canvas-DghDRtHn.d.cts.map +0 -1
@@ -16,7 +16,7 @@ var __copyProps = (to, from, except, desc) => {
16
16
  }
17
17
  return to;
18
18
  };
19
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
19
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule || !__hasOwnProp.call(mod, "default") ? __defProp(target, "default", {
20
20
  value: mod,
21
21
  enumerable: true
22
22
  }) : target, mod));
@@ -45,7 +45,7 @@ function Kbd({ children, hint = false, className }) {
45
45
  });
46
46
  }
47
47
  //#endregion
48
- //#region \0@oxc-project+runtime@0.140.0/helpers/esm/objectWithoutPropertiesLoose.js
48
+ //#region \0@oxc-project+runtime@0.146.0/helpers/esm/objectWithoutPropertiesLoose.js
49
49
  function _objectWithoutPropertiesLoose(r, e) {
50
50
  if (null == r) return {};
51
51
  var t = {};
@@ -56,7 +56,7 @@ function _objectWithoutPropertiesLoose(r, e) {
56
56
  return t;
57
57
  }
58
58
  //#endregion
59
- //#region \0@oxc-project+runtime@0.140.0/helpers/esm/objectWithoutProperties.js
59
+ //#region \0@oxc-project+runtime@0.146.0/helpers/esm/objectWithoutProperties.js
60
60
  function _objectWithoutProperties(e, t) {
61
61
  if (null == e) return {};
62
62
  var o, r, i = _objectWithoutPropertiesLoose(e, t);
@@ -67,7 +67,7 @@ function _objectWithoutProperties(e, t) {
67
67
  return i;
68
68
  }
69
69
  //#endregion
70
- //#region \0@oxc-project+runtime@0.140.0/helpers/esm/typeof.js
70
+ //#region \0@oxc-project+runtime@0.146.0/helpers/esm/typeof.js
71
71
  function _typeof(o) {
72
72
  "@babel/helpers - typeof";
73
73
  return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function(o) {
@@ -77,7 +77,7 @@ function _typeof(o) {
77
77
  }, _typeof(o);
78
78
  }
79
79
  //#endregion
80
- //#region \0@oxc-project+runtime@0.140.0/helpers/esm/toPrimitive.js
80
+ //#region \0@oxc-project+runtime@0.146.0/helpers/esm/toPrimitive.js
81
81
  function toPrimitive(t, r) {
82
82
  if ("object" != _typeof(t) || !t) return t;
83
83
  var e = t[Symbol.toPrimitive];
@@ -89,13 +89,13 @@ function toPrimitive(t, r) {
89
89
  return ("string" === r ? String : Number)(t);
90
90
  }
91
91
  //#endregion
92
- //#region \0@oxc-project+runtime@0.140.0/helpers/esm/toPropertyKey.js
92
+ //#region \0@oxc-project+runtime@0.146.0/helpers/esm/toPropertyKey.js
93
93
  function toPropertyKey(t) {
94
94
  var i = toPrimitive(t, "string");
95
95
  return "symbol" == _typeof(i) ? i : i + "";
96
96
  }
97
97
  //#endregion
98
- //#region \0@oxc-project+runtime@0.140.0/helpers/esm/defineProperty.js
98
+ //#region \0@oxc-project+runtime@0.146.0/helpers/esm/defineProperty.js
99
99
  function _defineProperty(e, r, t) {
100
100
  return (r = toPropertyKey(r)) in e ? Object.defineProperty(e, r, {
101
101
  value: t,
@@ -105,7 +105,7 @@ function _defineProperty(e, r, t) {
105
105
  }) : e[r] = t, e;
106
106
  }
107
107
  //#endregion
108
- //#region \0@oxc-project+runtime@0.140.0/helpers/esm/objectSpread2.js
108
+ //#region \0@oxc-project+runtime@0.146.0/helpers/esm/objectSpread2.js
109
109
  function ownKeys(e, r) {
110
110
  var t = Object.keys(e);
111
111
  if (Object.getOwnPropertySymbols) {
@@ -286,7 +286,7 @@ function MiniSparkline({ data, color }) {
286
286
  const min = Math.min(...data);
287
287
  const range = Math.max(...data) - min || 1;
288
288
  const points = data.map((v, i) => {
289
- return `${padding + i / (data.length - 1) * (width - padding * 2)},${padding + (1 - (v - min) / range) * (height - padding * 2)}`;
289
+ return `${padding + i / (data.length - 1) * 76},${padding + (1 - (v - min) / range) * 28}`;
290
290
  }).join(" ");
291
291
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
292
292
  width,
@@ -329,7 +329,7 @@ const koBreadcrumbLabels = {
329
329
  navAriaLabel: "{label}(으)로 이동" };
330
330
  //#endregion
331
331
  //#region src/core/button.tsx
332
- const _excluded$14 = [
332
+ const _excluded$15 = [
333
333
  "variant",
334
334
  "size",
335
335
  "loading",
@@ -353,7 +353,7 @@ const sizeStyles$3 = {
353
353
  };
354
354
  /** Renders the button component. */
355
355
  function Button(_ref) {
356
- let { variant = "primary", size = "md", loading = false, leftIcon, rightIcon, className, disabled, children, "aria-busy": ariaBusy } = _ref, props = _objectWithoutProperties(_ref, _excluded$14);
356
+ let { variant = "primary", size = "md", loading = false, leftIcon, rightIcon, className, disabled, children, "aria-busy": ariaBusy } = _ref, props = _objectWithoutProperties(_ref, _excluded$15);
357
357
  const isDisabled = disabled || loading;
358
358
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", _objectSpread2(_objectSpread2({
359
359
  type: "button",
@@ -436,7 +436,7 @@ function EmptyState({ icon, title, description, actions, className, iconSize = "
436
436
  }
437
437
  //#endregion
438
438
  //#region src/core/badge.tsx
439
- const _excluded$13 = [
439
+ const _excluded$14 = [
440
440
  "variant",
441
441
  "size",
442
442
  "dot",
@@ -472,7 +472,7 @@ const sizeStyles$2 = {
472
472
  };
473
473
  /** Renders the badge component. */
474
474
  function Badge(_ref) {
475
- let { variant = "default", size = "md", dot = false, progress, progressSize = 16, className, children } = _ref, props = _objectWithoutProperties(_ref, _excluded$13);
475
+ let { variant = "default", size = "md", dot = false, progress, progressSize = 16, className, children } = _ref, props = _objectWithoutProperties(_ref, _excluded$14);
476
476
  const hasProgress = progress != null;
477
477
  const clampedProgress = hasProgress ? Math.min(100, Math.max(0, progress)) : 0;
478
478
  const radius = (progressSize - 3) / 2;
@@ -518,7 +518,7 @@ function Badge(_ref) {
518
518
  }
519
519
  //#endregion
520
520
  //#region src/core/spinner.tsx
521
- const _excluded$12 = [
521
+ const _excluded$13 = [
522
522
  "size",
523
523
  "className",
524
524
  "role",
@@ -532,7 +532,7 @@ const sizeStyles$1 = {
532
532
  };
533
533
  /** Renders the spinner component. */
534
534
  function Spinner(_ref) {
535
- let { size = "md", className, role, "aria-hidden": ariaHidden, "aria-label": ariaLabel } = _ref, props = _objectWithoutProperties(_ref, _excluded$12);
535
+ let { size = "md", className, role, "aria-hidden": ariaHidden, "aria-label": ariaLabel } = _ref, props = _objectWithoutProperties(_ref, _excluded$13);
536
536
  const isDecorative = ariaHidden === true || ariaHidden === "true";
537
537
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", _objectSpread2(_objectSpread2({
538
538
  className: cn("animate-spin text-current", sizeStyles$1[size], className),
@@ -560,7 +560,7 @@ function Spinner(_ref) {
560
560
  }
561
561
  //#endregion
562
562
  //#region src/core/alert.tsx
563
- const _excluded$11 = [
563
+ const _excluded$12 = [
564
564
  "variant",
565
565
  "title",
566
566
  "icon",
@@ -629,7 +629,7 @@ const defaultIcons = {
629
629
  };
630
630
  /** Renders the alert component. */
631
631
  function Alert(_ref) {
632
- let { variant = "info", title, icon, dismissible = false, onDismiss, className, children } = _ref, props = _objectWithoutProperties(_ref, _excluded$11);
632
+ let { variant = "info", title, icon, dismissible = false, onDismiss, className, children } = _ref, props = _objectWithoutProperties(_ref, _excluded$12);
633
633
  const displayIcon = icon !== null && icon !== void 0 ? icon : defaultIcons[variant];
634
634
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", _objectSpread2(_objectSpread2({
635
635
  role: "alert",
@@ -684,6 +684,68 @@ function useInputId(id, label, prefix) {
684
684
  return id !== null && id !== void 0 ? id : label ? `${prefix}-${label.replace(/\s+/g, "-").toLowerCase()}` : void 0;
685
685
  }
686
686
  //#endregion
687
+ //#region src/core/input.tsx
688
+ const _excluded$11 = [
689
+ "label",
690
+ "error",
691
+ "hint",
692
+ "leftIcon",
693
+ "rightIcon",
694
+ "fullWidth",
695
+ "size",
696
+ "required",
697
+ "className",
698
+ "id"
699
+ ];
700
+ const inputSizeStyles = OPT_INPUT_SIZE;
701
+ const Input = (0, react.forwardRef)((_ref, ref) => {
702
+ var _useInputId;
703
+ let { label, error, hint, leftIcon, rightIcon, fullWidth = false, size = "md", required, className, id } = _ref, props = _objectWithoutProperties(_ref, _excluded$11);
704
+ const fallbackId = (0, react.useId)().replace(/:/g, "");
705
+ const inputId = (_useInputId = useInputId(id, label, "input")) !== null && _useInputId !== void 0 ? _useInputId : `input-${fallbackId}`;
706
+ const hasError = Boolean(error);
707
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
708
+ className: cn("flex flex-col gap-1.5", fullWidth && "w-full"),
709
+ "data-opt-id": "7BY1Q",
710
+ "data-opt-slug": "input.Input",
711
+ children: [
712
+ label && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
713
+ htmlFor: inputId,
714
+ className: "text-text-primary text-sm font-medium",
715
+ children: [label, required && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RequiredMark, {})]
716
+ }),
717
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
718
+ className: "relative",
719
+ children: [
720
+ leftIcon && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
721
+ className: "text-text-tertiary pointer-events-none absolute top-1/2 left-3 -translate-y-1/2",
722
+ children: leftIcon
723
+ }),
724
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", _objectSpread2({
725
+ ref,
726
+ id: inputId,
727
+ className: cn(OPT_INPUT, inputSizeStyles[size], leftIcon && (size === "sm" ? "pl-8" : "pl-10"), rightIcon && (size === "sm" ? "pr-8" : "pr-10"), hasError && "border-danger focus-visible:outline-danger", !fullWidth && "w-auto", className),
728
+ "aria-invalid": hasError,
729
+ "aria-required": required,
730
+ "aria-describedby": getDescribedBy(inputId, error, hint),
731
+ required
732
+ }, props)),
733
+ rightIcon && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
734
+ className: "text-text-tertiary pointer-events-none absolute top-1/2 right-3 -translate-y-1/2",
735
+ children: rightIcon
736
+ })
737
+ ]
738
+ }),
739
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FieldMessages, {
740
+ id: inputId,
741
+ error,
742
+ hint
743
+ })
744
+ ]
745
+ });
746
+ });
747
+ Input.displayName = "Input";
748
+ //#endregion
687
749
  //#region src/core/star-rating.tsx
688
750
  function interpolate(template, values) {
689
751
  return template.replace(/\{(\w+)\}/g, (_, key) => key in values ? String(values[key]) : `{${key}}`);
@@ -1523,13 +1585,10 @@ function NumberInput({ value: controlledValue, defaultValue, onChange, nullable,
1523
1585
  setTo(min);
1524
1586
  }
1525
1587
  break;
1526
- case "End":
1527
- if (max !== void 0) {
1528
- e.preventDefault();
1529
- setTo(max);
1530
- }
1531
- break;
1532
- default: break;
1588
+ case "End": if (max !== void 0) {
1589
+ e.preventDefault();
1590
+ setTo(max);
1591
+ }
1533
1592
  }
1534
1593
  }, [
1535
1594
  interactionDisabled,
@@ -3166,12 +3225,13 @@ function FormSelect({ name, children, className, placeholder, minWidth = 140, "a
3166
3225
  const error = form === null || form === void 0 ? void 0 : form.getError(name);
3167
3226
  const touched = form === null || form === void 0 ? void 0 : form.getFieldTouched(name);
3168
3227
  const hasError = Boolean(error && touched);
3228
+ const handleSetValue = (0, react.useCallback)((v) => {
3229
+ var _v$;
3230
+ return form.setValue(name, Array.isArray(v) ? (_v$ = v[0]) !== null && _v$ !== void 0 ? _v$ : "" : v);
3231
+ }, [form, name]);
3169
3232
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(SelectRoot, {
3170
3233
  value,
3171
- onValueChange: (0, react.useCallback)((v) => {
3172
- var _v$;
3173
- return form.setValue(name, Array.isArray(v) ? (_v$ = v[0]) !== null && _v$ !== void 0 ? _v$ : "" : v);
3174
- }, [form, name]),
3234
+ onValueChange: handleSetValue,
3175
3235
  "data-opt-id": "A2XF7",
3176
3236
  "data-opt-slug": "form.FormSelect",
3177
3237
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(SelectTrigger, {
@@ -3377,8 +3437,9 @@ function FormSlider(_ref25) {
3377
3437
  var _ref26;
3378
3438
  let { name } = _ref25, props = _objectWithoutProperties(_ref25, _excluded22);
3379
3439
  const form = useRequiredFormContext("FormSlider");
3440
+ const value = (_ref26 = (0, _reopt_ai_opt_ui_primitives.useFieldValue)(form, name)) !== null && _ref26 !== void 0 ? _ref26 : 0;
3380
3441
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Slider, _objectSpread2(_objectSpread2({
3381
- value: (_ref26 = (0, _reopt_ai_opt_ui_primitives.useFieldValue)(form, name)) !== null && _ref26 !== void 0 ? _ref26 : 0,
3442
+ value,
3382
3443
  onChange: (v) => form.setValue(name, v)
3383
3444
  }, props), {}, {
3384
3445
  "data-opt-id": "3XQ3A",
@@ -3390,8 +3451,9 @@ function FormStarRating(_ref27) {
3390
3451
  var _ref28;
3391
3452
  let { name } = _ref27, props = _objectWithoutProperties(_ref27, _excluded23);
3392
3453
  const form = useRequiredFormContext("FormStarRating");
3454
+ const value = (_ref28 = (0, _reopt_ai_opt_ui_primitives.useFieldValue)(form, name)) !== null && _ref28 !== void 0 ? _ref28 : 0;
3393
3455
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StarRating, _objectSpread2(_objectSpread2({
3394
- value: (_ref28 = (0, _reopt_ai_opt_ui_primitives.useFieldValue)(form, name)) !== null && _ref28 !== void 0 ? _ref28 : 0,
3456
+ value,
3395
3457
  onChange: (v) => form.setValue(name, v)
3396
3458
  }, props), {}, {
3397
3459
  "data-opt-id": "EKNJJ",
@@ -4171,6 +4233,12 @@ Object.defineProperty(exports, "IconPicker", {
4171
4233
  return IconPicker;
4172
4234
  }
4173
4235
  });
4236
+ Object.defineProperty(exports, "Input", {
4237
+ enumerable: true,
4238
+ get: function() {
4239
+ return Input;
4240
+ }
4241
+ });
4174
4242
  Object.defineProperty(exports, "Kbd", {
4175
4243
  enumerable: true,
4176
4244
  get: function() {
@@ -4369,12 +4437,6 @@ Object.defineProperty(exports, "OPT_INPUT_ERROR", {
4369
4437
  return OPT_INPUT_ERROR;
4370
4438
  }
4371
4439
  });
4372
- Object.defineProperty(exports, "OPT_INPUT_SIZE", {
4373
- enumerable: true,
4374
- get: function() {
4375
- return OPT_INPUT_SIZE;
4376
- }
4377
- });
4378
4440
  Object.defineProperty(exports, "OPT_ITEM_BASE", {
4379
4441
  enumerable: true,
4380
4442
  get: function() {
package/dist/query.cjs ADDED
@@ -0,0 +1,386 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ //#region src/lib/query/query-ast.ts
3
+ const EMPTY_QUERY = { clauses: [] };
4
+ /** Whether a query constrains anything at all. */
5
+ function isEmptyQuery(query) {
6
+ return !query || query.clauses.length === 0;
7
+ }
8
+ function sameClause(a, b) {
9
+ if (a.type !== b.type) return false;
10
+ if (a.type === "term" && b.type === "term") return a.value === b.value && a.negated === b.negated;
11
+ const fieldA = a;
12
+ const fieldB = b;
13
+ return fieldA.field === fieldB.field && fieldA.operator === fieldB.operator && fieldA.negated === fieldB.negated && fieldA.values.length === fieldB.values.length && fieldA.values.every((value, index) => value === fieldB.values[index]);
14
+ }
15
+ /** Add a clause unless an identical one is already there. */
16
+ function addClause(query, clause) {
17
+ if (query.clauses.some((existing) => sameClause(existing, clause))) return query;
18
+ return { clauses: [...query.clauses, clause] };
19
+ }
20
+ /**
21
+ * Constrain a field to a value — what a cell's "filter for this" action does.
22
+ *
23
+ * Applying the opposite of a constraint already present replaces it rather than
24
+ * adding it: `status:200` then "filter out 200" should read as one changed
25
+ * mind, not as a query that can never match.
26
+ */
27
+ function withFieldValue(query, field, value, options = {}) {
28
+ var _options$negated, _options$operator;
29
+ const negated = (_options$negated = options.negated) !== null && _options$negated !== void 0 ? _options$negated : false;
30
+ const operator = (_options$operator = options.operator) !== null && _options$operator !== void 0 ? _options$operator : "eq";
31
+ return { clauses: [...query.clauses.filter((clause) => !(clause.type === "field" && clause.field === field && clause.operator === operator && clause.values.length === 1 && clause.values[0] === value)), {
32
+ type: "field",
33
+ field,
34
+ operator,
35
+ values: [value],
36
+ negated
37
+ }] };
38
+ }
39
+ /** Drop every clause naming `field`. */
40
+ function withoutField(query, field) {
41
+ return { clauses: query.clauses.filter((clause) => clause.type !== "field" || clause.field !== field) };
42
+ }
43
+ /** Drop the clause at `index`, for a removable chip. */
44
+ function removeClause(query, index) {
45
+ return { clauses: query.clauses.filter((_, at) => at !== index) };
46
+ }
47
+ /** Every field the query names, in the order they first appear. */
48
+ function queryFields(query) {
49
+ const seen = [];
50
+ for (const clause of query.clauses) if (clause.type === "field" && !seen.includes(clause.field)) seen.push(clause.field);
51
+ return seen;
52
+ }
53
+ /** The values a field is constrained to, if any — for showing a filter as active. */
54
+ function fieldValues(query, field) {
55
+ return query.clauses.flatMap((clause) => clause.type === "field" && clause.field === field && !clause.negated ? clause.values : []);
56
+ }
57
+ /**
58
+ * Fold a cell action into the query.
59
+ *
60
+ * The pair of buttons on a log cell is the fastest path from "I noticed this
61
+ * value" to a filtered view, and the reason it works is that the result is a
62
+ * query the reader can then see, edit and share — not a hidden filter stack
63
+ * with its own rules.
64
+ */
65
+ function applyCellAction(query, action) {
66
+ return withFieldValue(query, action.field, action.value, { negated: action.intent === "exclude" });
67
+ }
68
+ //#endregion
69
+ //#region src/lib/query/query-parse.ts
70
+ const FIELD_NAME = /^[A-Za-z_][A-Za-z0-9_.-]*$/;
71
+ /**
72
+ * Split on whitespace, but keep quoted runs and parenthesised groups whole.
73
+ *
74
+ * An unterminated quote or group swallows the rest of the line rather than
75
+ * erroring: the reader is mid-word, and the alternative is the query flickering
76
+ * between two different meanings as they type the closing character.
77
+ */
78
+ function tokenize(input) {
79
+ const tokens = [];
80
+ let index = 0;
81
+ while (index < input.length) {
82
+ while (index < input.length && /\s/.test(input[index])) index += 1;
83
+ if (index >= input.length) break;
84
+ const start = index;
85
+ let text = "";
86
+ let quoted = false;
87
+ let atTokenStart = true;
88
+ let depth = 0;
89
+ while (index < input.length) {
90
+ const char = input[index];
91
+ if (char === "\"") {
92
+ if (atTokenStart) quoted = true;
93
+ atTokenStart = false;
94
+ index += 1;
95
+ while (index < input.length && input[index] !== "\"") {
96
+ text += input[index];
97
+ index += 1;
98
+ }
99
+ index += 1;
100
+ continue;
101
+ }
102
+ if (char === "(") depth += 1;
103
+ if (char === ")") depth = Math.max(0, depth - 1);
104
+ if (/\s/.test(char) && depth === 0) break;
105
+ atTokenStart = false;
106
+ text += char;
107
+ index += 1;
108
+ }
109
+ if (text.length > 0 || quoted) tokens.push({
110
+ text,
111
+ at: start,
112
+ quoted
113
+ });
114
+ }
115
+ return tokens;
116
+ }
117
+ /** `(a or b)` → `["a", "b"]`; anything else is a single value. */
118
+ function readValues(raw) {
119
+ const trimmed = raw.trim();
120
+ if (!trimmed.startsWith("(")) return [stripQuotes(trimmed)];
121
+ return trimmed.replace(/^\(/, "").replace(/\)$/, "").split(/\s+or\s+/i).map((part) => stripQuotes(part.trim())).filter((part) => part.length > 0);
122
+ }
123
+ function stripQuotes(value) {
124
+ if (value.length >= 2 && value.startsWith("\"") && value.endsWith("\"")) return value.slice(1, -1);
125
+ return value.replace(/^"/, "");
126
+ }
127
+ const OPERATORS = [
128
+ {
129
+ symbol: ">=",
130
+ operator: "gte"
131
+ },
132
+ {
133
+ symbol: "<=",
134
+ operator: "lte"
135
+ },
136
+ {
137
+ symbol: ":",
138
+ operator: "eq"
139
+ },
140
+ {
141
+ symbol: ">",
142
+ operator: "gt"
143
+ },
144
+ {
145
+ symbol: "<",
146
+ operator: "lt"
147
+ }
148
+ ];
149
+ /** Where a field operator starts in the token, or `-1`. */
150
+ function findOperator(text) {
151
+ let best = null;
152
+ for (const { symbol, operator } of OPERATORS) {
153
+ const index = text.indexOf(symbol);
154
+ if (index <= 0) continue;
155
+ if (!best || index < best.index || index === best.index && symbol.length > best.symbol.length) best = {
156
+ index,
157
+ symbol,
158
+ operator
159
+ };
160
+ }
161
+ return best;
162
+ }
163
+ /** Parse, keeping whatever could not be understood as a plain term. */
164
+ function parseQueryWithDiagnostics(input) {
165
+ const diagnostics = [];
166
+ const clauses = [];
167
+ let trailing = null;
168
+ for (const token of tokenize(input)) {
169
+ let text = token.text;
170
+ let negated = false;
171
+ if (!token.quoted && text.startsWith("-") && text.length > 1) {
172
+ negated = true;
173
+ text = text.slice(1);
174
+ }
175
+ const operator = token.quoted ? null : findOperator(text);
176
+ if (!operator) {
177
+ if (text.length > 0) clauses.push({
178
+ type: "term",
179
+ value: text,
180
+ negated
181
+ });
182
+ continue;
183
+ }
184
+ const field = text.slice(0, operator.index);
185
+ const rawValue = text.slice(operator.index + operator.symbol.length);
186
+ if (!FIELD_NAME.test(field)) {
187
+ clauses.push({
188
+ type: "term",
189
+ value: text,
190
+ negated
191
+ });
192
+ continue;
193
+ }
194
+ if (rawValue === "") {
195
+ trailing = {
196
+ field,
197
+ operator: operator.operator,
198
+ value: ""
199
+ };
200
+ continue;
201
+ }
202
+ if (rawValue === "*" && operator.operator === "eq") {
203
+ clauses.push({
204
+ type: "field",
205
+ field,
206
+ operator: "exists",
207
+ values: [],
208
+ negated
209
+ });
210
+ continue;
211
+ }
212
+ if (operator.operator === "eq" && rawValue.length > 2 && rawValue.startsWith("*") && rawValue.endsWith("*")) {
213
+ clauses.push({
214
+ type: "field",
215
+ field,
216
+ operator: "contains",
217
+ values: [stripQuotes(rawValue.slice(1, -1))],
218
+ negated
219
+ });
220
+ continue;
221
+ }
222
+ const values = readValues(rawValue);
223
+ if (values.length === 0) {
224
+ diagnostics.push({
225
+ at: token.at,
226
+ text: token.text,
227
+ message: "Empty value group"
228
+ });
229
+ continue;
230
+ }
231
+ if (operator.operator !== "eq" && values.length > 1) diagnostics.push({
232
+ at: token.at,
233
+ text: token.text,
234
+ message: "A comparison takes a single value"
235
+ });
236
+ const clause = {
237
+ type: "field",
238
+ field,
239
+ operator: operator.operator,
240
+ values: operator.operator === "eq" ? values : values.slice(0, 1),
241
+ negated
242
+ };
243
+ clauses.push(clause);
244
+ if (!rawValue.startsWith("(")) {
245
+ var _values$;
246
+ trailing = {
247
+ field,
248
+ operator: operator.operator,
249
+ value: (_values$ = values[0]) !== null && _values$ !== void 0 ? _values$ : ""
250
+ };
251
+ }
252
+ }
253
+ return {
254
+ query: { clauses },
255
+ diagnostics,
256
+ trailing
257
+ };
258
+ }
259
+ /** Parse, discarding diagnostics. */
260
+ function parseQuery(input) {
261
+ return parseQueryWithDiagnostics(input).query;
262
+ }
263
+ //#endregion
264
+ //#region src/lib/query/query-format.ts
265
+ const OPERATOR_SYMBOL = {
266
+ eq: ":",
267
+ contains: ":",
268
+ gt: ">",
269
+ gte: ">=",
270
+ lt: "<",
271
+ lte: "<=",
272
+ exists: ":"
273
+ };
274
+ /** Quote a value that would otherwise re-parse as something else. */
275
+ function quoteValue(value) {
276
+ return /[\s"()]/.test(value) || value === "" ? `"${value.replace(/"/g, "")}"` : value;
277
+ }
278
+ /** One clause as text. */
279
+ function formatClause(clause) {
280
+ var _clause$values$2;
281
+ const prefix = clause.negated ? "-" : "";
282
+ if (clause.type === "term") return `${prefix}${quoteValue(clause.value)}`;
283
+ if (clause.operator === "exists") return `${prefix}${clause.field}:*`;
284
+ if (clause.operator === "contains") {
285
+ var _clause$values$;
286
+ return `${prefix}${clause.field}:*${(_clause$values$ = clause.values[0]) !== null && _clause$values$ !== void 0 ? _clause$values$ : ""}*`;
287
+ }
288
+ const symbol = OPERATOR_SYMBOL[clause.operator];
289
+ if (clause.values.length > 1) return `${prefix}${clause.field}${symbol}(${clause.values.map(quoteValue).join(" or ")})`;
290
+ return `${prefix}${clause.field}${symbol}${quoteValue((_clause$values$2 = clause.values[0]) !== null && _clause$values$2 !== void 0 ? _clause$values$2 : "")}`;
291
+ }
292
+ /** The whole query as text. */
293
+ function formatQuery(query) {
294
+ return query.clauses.map(formatClause).join(" ");
295
+ }
296
+ /** A short human label for a clause chip. */
297
+ function describeClause(clause) {
298
+ if (clause.type === "term") return clause.negated ? `not "${clause.value}"` : `"${clause.value}"`;
299
+ if (clause.operator === "exists") return clause.negated ? `${clause.field} missing` : `${clause.field} present`;
300
+ if (clause.operator === "contains") {
301
+ var _clause$values$3;
302
+ return `${clause.negated ? "not " : ""}${clause.field} ⊃ ${(_clause$values$3 = clause.values[0]) !== null && _clause$values$3 !== void 0 ? _clause$values$3 : ""}`;
303
+ }
304
+ const values = clause.values.join(", ");
305
+ const symbol = OPERATOR_SYMBOL[clause.operator];
306
+ return `${clause.negated ? "not " : ""}${clause.field} ${symbol === ":" ? "=" : symbol} ${values}`;
307
+ }
308
+ //#endregion
309
+ //#region src/lib/query/query-match.ts
310
+ function toText(value) {
311
+ if (value === null || value === void 0) return "";
312
+ if (typeof value === "string") return value;
313
+ if (typeof value === "number" || typeof value === "boolean") return String(value);
314
+ return JSON.stringify(value);
315
+ }
316
+ function compare(left, right) {
317
+ const leftNumber = typeof left === "number" ? left : Number(toText(left));
318
+ const rightNumber = Number(right);
319
+ if (Number.isFinite(leftNumber) && Number.isFinite(rightNumber)) return leftNumber - rightNumber;
320
+ return toText(left).localeCompare(right);
321
+ }
322
+ function matchesClause(clause, read, searchable) {
323
+ if (clause.type === "term") return searchable().toLowerCase().includes(clause.value.toLowerCase());
324
+ const value = read(clause.field);
325
+ switch (clause.operator) {
326
+ case "exists": return value !== void 0 && value !== null && value !== "";
327
+ case "eq": return clause.values.some((candidate) => toText(value).toLowerCase() === candidate.toLowerCase());
328
+ case "contains": return clause.values.some((candidate) => toText(value).toLowerCase().includes(candidate.toLowerCase()));
329
+ case "gt":
330
+ var _clause$values$;
331
+ return compare(value, (_clause$values$ = clause.values[0]) !== null && _clause$values$ !== void 0 ? _clause$values$ : "") > 0;
332
+ case "gte":
333
+ var _clause$values$2;
334
+ return compare(value, (_clause$values$2 = clause.values[0]) !== null && _clause$values$2 !== void 0 ? _clause$values$2 : "") >= 0;
335
+ case "lt":
336
+ var _clause$values$3;
337
+ return compare(value, (_clause$values$3 = clause.values[0]) !== null && _clause$values$3 !== void 0 ? _clause$values$3 : "") < 0;
338
+ case "lte":
339
+ var _clause$values$4;
340
+ return compare(value, (_clause$values$4 = clause.values[0]) !== null && _clause$values$4 !== void 0 ? _clause$values$4 : "") <= 0;
341
+ default: return true;
342
+ }
343
+ }
344
+ /**
345
+ * Whether one row satisfies every clause.
346
+ *
347
+ * `searchable` is lazy because most queries are field constraints, and
348
+ * flattening a row to text for a term match nobody asked for is the expensive
349
+ * part of filtering a live tail.
350
+ */
351
+ function matchesQuery(query, read, searchable) {
352
+ return query.clauses.every((clause) => {
353
+ const matched = matchesClause(clause, read, searchable);
354
+ return clause.negated ? !matched : matched;
355
+ });
356
+ }
357
+ /** Convenience for plain objects, including one level of nested properties. */
358
+ function matchesQueryObject(query, row, options = {}) {
359
+ const nested = options.nested ? row[options.nested] : void 0;
360
+ const read = (field) => field in row ? row[field] : nested === null || nested === void 0 ? void 0 : nested[field];
361
+ let cached = null;
362
+ const searchable = () => {
363
+ var _cached;
364
+ (_cached = cached) !== null && _cached !== void 0 || (cached = [...Object.values(row).filter((value) => typeof value !== "object"), ...Object.values(nested !== null && nested !== void 0 ? nested : {})].map(toText).join(" "));
365
+ return cached;
366
+ };
367
+ return matchesQuery(query, read, searchable);
368
+ }
369
+ //#endregion
370
+ exports.EMPTY_QUERY = EMPTY_QUERY;
371
+ exports.addClause = addClause;
372
+ exports.applyCellAction = applyCellAction;
373
+ exports.describeClause = describeClause;
374
+ exports.fieldValues = fieldValues;
375
+ exports.formatClause = formatClause;
376
+ exports.formatQuery = formatQuery;
377
+ exports.isEmptyQuery = isEmptyQuery;
378
+ exports.matchesQuery = matchesQuery;
379
+ exports.matchesQueryObject = matchesQueryObject;
380
+ exports.parseQuery = parseQuery;
381
+ exports.parseQueryWithDiagnostics = parseQueryWithDiagnostics;
382
+ exports.queryFields = queryFields;
383
+ exports.quoteValue = quoteValue;
384
+ exports.removeClause = removeClause;
385
+ exports.withFieldValue = withFieldValue;
386
+ exports.withoutField = withoutField;