myoperator-mcp 0.2.349 → 0.2.351

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 (2) hide show
  1. package/dist/index.js +606 -19
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -710,6 +710,7 @@ const badgeVariants = cva(
710
710
  variant: {
711
711
  // Status-based variants (existing)
712
712
  active: "bg-semantic-success-surface text-semantic-success-primary",
713
+ warning: "bg-semantic-warning-surface text-semantic-warning-primary",
713
714
  failed: "bg-semantic-error-surface text-semantic-error-primary",
714
715
  disabled: "bg-semantic-bg-ui text-semantic-text-muted",
715
716
  default: "bg-semantic-bg-ui text-semantic-text-primary",
@@ -739,6 +740,7 @@ const badgeVariants = cva(
739
740
  * @example
740
741
  * \`\`\`tsx
741
742
  * <Badge variant="active">Active</Badge>
743
+ * <Badge variant="warning">Warning</Badge>
742
744
  * <Badge variant="failed">Failed</Badge>
743
745
  * <Badge variant="disabled">Disabled</Badge>
744
746
  * <Badge variant="default">Default</Badge>
@@ -1511,6 +1513,11 @@ export interface CreatableMultiSelectProps extends Omit<
1511
1513
  * If the raw value differs from the sanitized value, \`onInvalidCharacters\` is called.
1512
1514
  */
1513
1515
  sanitizeInput?: (raw: string) => string;
1516
+ /**
1517
+ * Applied after \`sanitizeInput\` on typed draft values (e.g. collapse spaces).
1518
+ * Does not affect invalid-character detection.
1519
+ */
1520
+ normalizeInput?: (sanitized: string) => string;
1514
1521
  /** Fired when \`sanitizeInput\` removed one or more characters from the raw input. */
1515
1522
  onInvalidCharacters?: () => void;
1516
1523
  /**
@@ -1600,6 +1607,12 @@ function isValueAlreadySelected(
1600
1607
  });
1601
1608
  }
1602
1609
 
1610
+ function restoreInputCursor(input: HTMLInputElement, cursorPosition: number) {
1611
+ window.requestAnimationFrame(() => {
1612
+ input.setSelectionRange(cursorPosition, cursorPosition);
1613
+ });
1614
+ }
1615
+
1603
1616
  const CreatableMultiSelect = React.forwardRef(
1604
1617
  (
1605
1618
  {
@@ -1617,6 +1630,7 @@ const CreatableMultiSelect = React.forwardRef(
1617
1630
  showPerItemCharacterCounter = true,
1618
1631
  triggerDisplay = "chips",
1619
1632
  sanitizeInput,
1633
+ normalizeInput,
1620
1634
  onInvalidCharacters,
1621
1635
  onValidInput,
1622
1636
  onInputValueChange,
@@ -1641,6 +1655,17 @@ const CreatableMultiSelect = React.forwardRef(
1641
1655
  maxLengthPerItem
1642
1656
  );
1643
1657
 
1658
+ const normalizeDraftValue = React.useCallback(
1659
+ (raw: string) => {
1660
+ const sanitized = sanitizeInput ? sanitizeInput(raw) : raw;
1661
+ const normalized = normalizeInput ? normalizeInput(sanitized) : sanitized;
1662
+ return maxLengthPerItem != null
1663
+ ? normalized.slice(0, maxLengthPerItem)
1664
+ : normalized;
1665
+ },
1666
+ [maxLengthPerItem, normalizeInput, sanitizeInput]
1667
+ );
1668
+
1644
1669
  const addValue = (val: string) => {
1645
1670
  const isPreset = options.some((o) => o.value === val);
1646
1671
  const afterSanitize = isPreset
@@ -1648,7 +1673,9 @@ const CreatableMultiSelect = React.forwardRef(
1648
1673
  : sanitizeInput
1649
1674
  ? sanitizeInput(val)
1650
1675
  : val;
1651
- const trimmed = afterSanitize.trim();
1676
+ const afterNormalize =
1677
+ isPreset || !normalizeInput ? afterSanitize : normalizeInput(afterSanitize);
1678
+ const trimmed = afterNormalize.trim();
1652
1679
  if (
1653
1680
  !trimmed ||
1654
1681
  isValueAlreadySelected(
@@ -1837,12 +1864,19 @@ const CreatableMultiSelect = React.forwardRef(
1837
1864
  if (raw !== sanitized) onInvalidCharacters?.();
1838
1865
  else onValidInput?.();
1839
1866
  }
1840
- const nextInput =
1841
- maxLengthPerItem != null
1842
- ? sanitized.slice(0, maxLengthPerItem)
1843
- : sanitized;
1867
+ const nextInput = normalizeDraftValue(raw);
1844
1868
  setInputValue(nextInput);
1845
1869
  onInputValueChange?.(nextInput);
1870
+
1871
+ if (nextInput !== raw) {
1872
+ const input = e.currentTarget;
1873
+ const rawCursor = input.selectionStart ?? raw.length;
1874
+ const nextCursor = Math.min(
1875
+ normalizeDraftValue(raw.slice(0, rawCursor)).length,
1876
+ nextInput.length
1877
+ );
1878
+ restoreInputCursor(input, nextCursor);
1879
+ }
1846
1880
  }}
1847
1881
  maxLength={maxLengthPerItem}
1848
1882
  onKeyDown={handleKeyDown}
@@ -2104,6 +2138,12 @@ export const creatableToneHintRowClassName = cn(
2104
2138
  "flex shrink-0 items-center justify-between gap-2 px-4 py-2"
2105
2139
  )
2106
2140
 
2141
+ function restoreInputCursor(input: HTMLInputElement, cursorPosition: number) {
2142
+ window.requestAnimationFrame(() => {
2143
+ input.setSelectionRange(cursorPosition, cursorPosition)
2144
+ })
2145
+ }
2146
+
2107
2147
  export interface CreatableSelectOption {
2108
2148
  value: string
2109
2149
  label: string
@@ -2183,6 +2223,17 @@ const CreatableSelect = React.forwardRef(
2183
2223
  // Merge forwarded ref with internal ref
2184
2224
  React.useImperativeHandle(ref, () => containerRef.current!)
2185
2225
 
2226
+ const normalizeSearchValue = React.useCallback(
2227
+ (raw: string) => {
2228
+ const sanitized = sanitizeInput ? sanitizeInput(raw) : raw
2229
+ const normalized = normalizeComboboxInput
2230
+ ? normalizeComboboxInput(sanitized)
2231
+ : sanitized
2232
+ return maxLength != null ? normalized.slice(0, maxLength) : normalized
2233
+ },
2234
+ [maxLength, normalizeComboboxInput, sanitizeInput]
2235
+ )
2236
+
2186
2237
  const selectedLabel = React.useMemo(() => {
2187
2238
  const found = options.find((o) => o.value === value)
2188
2239
  return found ? found.label : value || ""
@@ -2335,13 +2386,19 @@ const CreatableSelect = React.forwardRef(
2335
2386
  if (raw !== sanitized) onInvalidCharacters?.()
2336
2387
  else onValidInput?.()
2337
2388
  }
2338
- const next = normalizeComboboxInput
2339
- ? normalizeComboboxInput(sanitized)
2340
- : sanitized
2341
- const nextSearch =
2342
- maxLength != null ? next.slice(0, maxLength) : next
2389
+ const nextSearch = normalizeSearchValue(raw)
2343
2390
  setSearch(nextSearch)
2344
2391
  onInputValueChange?.(nextSearch)
2392
+
2393
+ if (nextSearch !== raw) {
2394
+ const input = e.currentTarget
2395
+ const rawCursor = input.selectionStart ?? raw.length
2396
+ const nextCursor = Math.min(
2397
+ normalizeSearchValue(raw.slice(0, rawCursor)).length,
2398
+ nextSearch.length
2399
+ )
2400
+ restoreInputCursor(input, nextCursor)
2401
+ }
2345
2402
  }}
2346
2403
  maxLength={maxLength}
2347
2404
  onKeyDown={handleKeyDown}
@@ -4060,7 +4117,7 @@ const DateTimePicker = React.forwardRef<HTMLDivElement, DateTimePickerProps>(
4060
4117
  {weekDays.map((day) => (
4061
4118
  <div
4062
4119
  key={day}
4063
- className="flex size-8 items-center justify-center text-xs font-medium text-semantic-text-muted"
4120
+ className="flex size-8 items-center justify-center text-xs font-semibold text-semantic-text-muted"
4064
4121
  >
4065
4122
  {day}
4066
4123
  </div>
@@ -4444,7 +4501,7 @@ const DeleteConfirmationModal = React.forwardRef(
4444
4501
  <div className="grid gap-2 py-4">
4445
4502
  <label
4446
4503
  htmlFor="delete-confirmation-input"
4447
- className="text-sm text-semantic-text-secondary"
4504
+ className="text-sm font-semibold text-semantic-text-secondary"
4448
4505
  >
4449
4506
  Enter "{confirmText}" in uppercase to confirm
4450
4507
  </label>
@@ -5179,7 +5236,7 @@ function getCollapsedCursorPosition(value: string, cursorPosition: number) {
5179
5236
  * Input variants for different visual states
5180
5237
  */
5181
5238
  const inputVariants = cva(
5182
- "h-[42px] w-full rounded bg-semantic-bg-primary px-4 py-2 text-base text-semantic-text-primary outline-none transition-all file:border-0 file:bg-transparent file:text-base file:font-medium file:text-semantic-text-primary placeholder:text-semantic-text-placeholder disabled:cursor-not-allowed disabled:opacity-50 disabled:bg-[var(--color-neutral-50)]",
5239
+ "h-[42px] w-full rounded bg-semantic-bg-primary px-4 py-2 text-base text-semantic-text-primary outline-none transition-all file:border-0 file:bg-transparent file:text-base file:font-semibold file:text-semantic-text-primary placeholder:text-semantic-text-placeholder disabled:cursor-not-allowed disabled:opacity-50 disabled:bg-[var(--color-neutral-50)]",
5183
5240
  {
5184
5241
  variants: {
5185
5242
  state: {
@@ -5888,7 +5945,7 @@ const MultiSelect = React.forwardRef(
5888
5945
  <label
5889
5946
  htmlFor={selectId}
5890
5947
  className={cn(
5891
- "text-sm font-medium text-semantic-text-secondary",
5948
+ "text-sm font-semibold text-semantic-text-secondary",
5892
5949
  labelClassName
5893
5950
  )}
5894
5951
  >
@@ -7457,7 +7514,7 @@ export const ReadableField = React.forwardRef(
7457
7514
  >
7458
7515
  {/* Header Row: Label + Optional Action */}
7459
7516
  <div className="flex items-start justify-between">
7460
- <span className="text-sm text-semantic-text-secondary tracking-[0.035px]">
7517
+ <span className="text-sm font-semibold text-semantic-text-secondary tracking-[0.035px]">
7461
7518
  {label}
7462
7519
  </span>
7463
7520
  {headerAction && (
@@ -7709,6 +7766,517 @@ const ReplyQuote = React.forwardRef<HTMLDivElement, ReplyQuoteProps>(
7709
7766
  ReplyQuote.displayName = "ReplyQuote";
7710
7767
 
7711
7768
  export { ReplyQuote };`,
7769
+ "search-filter": `import * as React from "react";
7770
+ import { createPortal } from "react-dom";
7771
+ import {
7772
+ autoUpdate,
7773
+ flip,
7774
+ offset,
7775
+ shift,
7776
+ size as floatingSize,
7777
+ useFloating,
7778
+ } from "@floating-ui/react-dom";
7779
+ import { cva, type VariantProps } from "class-variance-authority";
7780
+ import { Search, X } from "lucide-react";
7781
+
7782
+ import { cn } from "@/lib/utils";
7783
+ import { Button } from "./button";
7784
+ import { Checkbox } from "./checkbox";
7785
+ import { Input, type InputProps } from "./input";
7786
+
7787
+ const searchFilterVariants = cva(
7788
+ "relative flex min-w-0 flex-col",
7789
+ {
7790
+ variants: {
7791
+ size: {
7792
+ sm: "w-full max-w-80",
7793
+ default: "w-full max-w-[360px]",
7794
+ lg: "w-full max-w-[420px]",
7795
+ },
7796
+ },
7797
+ defaultVariants: {
7798
+ size: "default",
7799
+ },
7800
+ }
7801
+ );
7802
+
7803
+ const searchFilterDropdownVariants = cva(
7804
+ "flex flex-col overflow-hidden p-0"
7805
+ );
7806
+
7807
+ export type SearchFilterSearchMode = "text" | "numeric";
7808
+
7809
+ function normalizeSearchText(value: string, searchMode: SearchFilterSearchMode) {
7810
+ if (searchMode === "numeric") {
7811
+ return value.replace(/\\D/g, "");
7812
+ }
7813
+
7814
+ return value.trim().toLowerCase();
7815
+ }
7816
+
7817
+ function renderHighlightedNumericLabel(label: string, query: string) {
7818
+ if (!query) return label;
7819
+
7820
+ const digitCharacterIndexes: number[] = [];
7821
+ const labelDigits = Array.from(label)
7822
+ .filter((character, index) => {
7823
+ const isDigit = /\\d/.test(character);
7824
+ if (isDigit) {
7825
+ digitCharacterIndexes.push(index);
7826
+ }
7827
+ return isDigit;
7828
+ })
7829
+ .join("");
7830
+ const matchStart = labelDigits.indexOf(query);
7831
+
7832
+ if (matchStart === -1) return label;
7833
+
7834
+ const highlightedIndexes = new Set(
7835
+ digitCharacterIndexes.slice(matchStart, matchStart + query.length)
7836
+ );
7837
+
7838
+ const chunks: Array<{ text: string; highlighted: boolean }> = [];
7839
+
7840
+ Array.from(label).forEach((character, index) => {
7841
+ const highlighted = highlightedIndexes.has(index);
7842
+ const previousChunk = chunks[chunks.length - 1];
7843
+
7844
+ if (previousChunk && previousChunk.highlighted === highlighted) {
7845
+ previousChunk.text += character;
7846
+ return;
7847
+ }
7848
+
7849
+ chunks.push({ text: character, highlighted });
7850
+ });
7851
+
7852
+ return chunks.map((chunk, index) => (
7853
+ <span
7854
+ key={\`\${chunk.text}-\${index}\`}
7855
+ className={chunk.highlighted ? "font-semibold" : undefined}
7856
+ >
7857
+ {chunk.text}
7858
+ </span>
7859
+ ));
7860
+ }
7861
+
7862
+ export interface SearchFilterOption {
7863
+ /** Unique option value returned in selection callbacks */
7864
+ value: string;
7865
+ /** Display label shown beside the checkbox */
7866
+ label: string;
7867
+ /** Disables selection for this row */
7868
+ disabled?: boolean;
7869
+ }
7870
+
7871
+ export interface SearchFilterProps
7872
+ extends Omit<React.HTMLAttributes<HTMLDivElement>, "onChange">,
7873
+ VariantProps<typeof searchFilterVariants> {
7874
+ /** Options displayed in the filter list */
7875
+ options: SearchFilterOption[];
7876
+ /** Controlled search text */
7877
+ searchValue?: string;
7878
+ /** Initial search text for uncontrolled usage */
7879
+ defaultSearchValue?: string;
7880
+ /** Called whenever search text changes */
7881
+ onSearchChange?: (value: string) => void;
7882
+ /** Placeholder for the search input */
7883
+ searchPlaceholder?: string;
7884
+ /** Search input behavior. Defaults to "numeric" for phone-number filtering. */
7885
+ searchMode?: SearchFilterSearchMode;
7886
+ /** Message shown when filtering returns no options */
7887
+ emptyMessage?: string;
7888
+ /** Cancel button label */
7889
+ cancelLabel?: string;
7890
+ /** Apply button label */
7891
+ applyLabel?: string;
7892
+ /** Called when Cancel is clicked */
7893
+ onCancel?: () => void;
7894
+ /** Called with the current selection when Apply is clicked */
7895
+ onApply?: (value: string[]) => void;
7896
+ /** Disables the whole filter */
7897
+ disabled?: boolean;
7898
+ /** Disables only the search input */
7899
+ searchDisabled?: boolean;
7900
+ /** Disables the Cancel button */
7901
+ cancelDisabled?: boolean;
7902
+ /** Disables the Apply button */
7903
+ applyDisabled?: boolean;
7904
+ /** Class name for the scrollable options region */
7905
+ listClassName?: string;
7906
+ /** Additional props for the search input */
7907
+ inputProps?: Omit<InputProps, "value" | "defaultValue" | "type">;
7908
+ }
7909
+
7910
+ const SearchFilter = React.forwardRef<HTMLDivElement, SearchFilterProps>(
7911
+ (
7912
+ {
7913
+ className,
7914
+ size,
7915
+ options,
7916
+ searchValue,
7917
+ defaultSearchValue = "",
7918
+ onSearchChange,
7919
+ searchPlaceholder = "Search...",
7920
+ searchMode = "numeric",
7921
+ emptyMessage = "No options found",
7922
+ cancelLabel = "Cancel",
7923
+ applyLabel = "Apply",
7924
+ onCancel,
7925
+ onApply,
7926
+ disabled = false,
7927
+ searchDisabled = false,
7928
+ cancelDisabled = false,
7929
+ applyDisabled = false,
7930
+ listClassName,
7931
+ inputProps,
7932
+ ...props
7933
+ },
7934
+ ref
7935
+ ) => {
7936
+ const {
7937
+ className: inputClassName,
7938
+ onChange: inputOnChange,
7939
+ onClick: inputOnClick,
7940
+ onFocus: inputOnFocus,
7941
+ onKeyDown: inputOnKeyDown,
7942
+ inputMode: inputInputMode,
7943
+ pattern: inputPattern,
7944
+ ...searchInputProps
7945
+ } = inputProps ?? {};
7946
+ const searchInputRef = React.useRef<HTMLInputElement | null>(null);
7947
+ const inputId = React.useId();
7948
+ const dropdownId = React.useId();
7949
+ const optionIdPrefix = React.useId();
7950
+ const [selectedValues, setSelectedValues] = React.useState<string[]>([]);
7951
+ const [internalSearchValue, setInternalSearchValue] =
7952
+ React.useState(defaultSearchValue);
7953
+ const [isOpen, setIsOpen] = React.useState(false);
7954
+ const currentSearchValue = searchValue ?? internalSearchValue;
7955
+ const displaySearchValue =
7956
+ searchMode === "numeric"
7957
+ ? normalizeSearchText(currentSearchValue, searchMode)
7958
+ : currentSearchValue;
7959
+ const normalizedSearchQuery = normalizeSearchText(
7960
+ currentSearchValue,
7961
+ searchMode
7962
+ );
7963
+
7964
+ const containerRef = React.useRef<HTMLDivElement | null>(null);
7965
+
7966
+ const { refs, floatingStyles } = useFloating({
7967
+ open: isOpen,
7968
+ placement: "bottom-start",
7969
+ strategy: "fixed",
7970
+ middleware: [
7971
+ offset(8),
7972
+ flip({ padding: 8 }),
7973
+ shift({ padding: 8 }),
7974
+ floatingSize({
7975
+ padding: 8,
7976
+ apply({ availableHeight, rects, elements }) {
7977
+ elements.floating.style.width = \`\${rects.reference.width}px\`;
7978
+ elements.floating.style.maxHeight = \`\${Math.min(
7979
+ 368,
7980
+ availableHeight
7981
+ )}px\`;
7982
+ },
7983
+ }),
7984
+ ],
7985
+ whileElementsMounted: (reference, floating, update) =>
7986
+ autoUpdate(reference, floating, update, { animationFrame: true }),
7987
+ });
7988
+
7989
+ const setOpen = React.useCallback(
7990
+ (nextOpen: boolean) => {
7991
+ if (disabled) return;
7992
+
7993
+ setIsOpen(nextOpen);
7994
+ },
7995
+ [disabled]
7996
+ );
7997
+
7998
+ const focusSearchInput = React.useCallback(() => {
7999
+ window.requestAnimationFrame(() => {
8000
+ searchInputRef.current?.focus();
8001
+ });
8002
+ }, []);
8003
+
8004
+ const setRootRef = React.useCallback(
8005
+ (node: HTMLDivElement | null) => {
8006
+ containerRef.current = node;
8007
+
8008
+ if (typeof ref === "function") {
8009
+ ref(node);
8010
+ } else if (ref) {
8011
+ ref.current = node;
8012
+ }
8013
+ },
8014
+ [ref]
8015
+ );
8016
+
8017
+ const setAnchorRef = React.useCallback(
8018
+ (node: HTMLDivElement | null) => {
8019
+ refs.setReference(node);
8020
+ },
8021
+ [refs]
8022
+ );
8023
+
8024
+ const setDropdownRef = React.useCallback(
8025
+ (node: HTMLDivElement | null) => {
8026
+ refs.setFloating(node);
8027
+ },
8028
+ [refs]
8029
+ );
8030
+
8031
+ React.useEffect(() => {
8032
+ if (!isOpen) return;
8033
+
8034
+ const handleClickOutside = (event: MouseEvent) => {
8035
+ const target = event.target as Node;
8036
+ if (
8037
+ containerRef.current?.contains(target) ||
8038
+ refs.floating.current?.contains(target)
8039
+ ) {
8040
+ return;
8041
+ }
8042
+ setOpen(false);
8043
+ };
8044
+
8045
+ const timeoutId = window.setTimeout(() => {
8046
+ document.addEventListener("mousedown", handleClickOutside);
8047
+ }, 0);
8048
+
8049
+ return () => {
8050
+ window.clearTimeout(timeoutId);
8051
+ document.removeEventListener("mousedown", handleClickOutside);
8052
+ };
8053
+ }, [isOpen, refs.floating, setOpen]);
8054
+
8055
+ const filteredOptions = React.useMemo(() => {
8056
+ if (!normalizedSearchQuery) return options;
8057
+
8058
+ return options.filter((option) =>
8059
+ normalizeSearchText(option.label, searchMode).includes(
8060
+ normalizedSearchQuery
8061
+ )
8062
+ );
8063
+ }, [normalizedSearchQuery, options, searchMode]);
8064
+
8065
+ const toggleOption = React.useCallback(
8066
+ (option: SearchFilterOption) => {
8067
+ if (disabled || option.disabled) return;
8068
+
8069
+ const isSelected = selectedValues.includes(option.value);
8070
+ setSelectedValues(
8071
+ isSelected
8072
+ ? selectedValues.filter((item) => item !== option.value)
8073
+ : [...selectedValues, option.value]
8074
+ );
8075
+ },
8076
+ [disabled, selectedValues, setSelectedValues]
8077
+ );
8078
+
8079
+ const handleSearchChange = (event: React.ChangeEvent<HTMLInputElement>) => {
8080
+ const nextSearchValue = normalizeSearchText(
8081
+ event.target.value,
8082
+ searchMode
8083
+ );
8084
+ event.target.value = nextSearchValue;
8085
+
8086
+ if (searchValue === undefined) {
8087
+ setInternalSearchValue(nextSearchValue);
8088
+ }
8089
+ setOpen(true);
8090
+ onSearchChange?.(nextSearchValue);
8091
+ };
8092
+
8093
+ const handleClearSearch = () => {
8094
+ if (searchValue === undefined) {
8095
+ setInternalSearchValue("");
8096
+ }
8097
+ onSearchChange?.("");
8098
+ setOpen(true);
8099
+ focusSearchInput();
8100
+ };
8101
+
8102
+ const handleCancel = () => {
8103
+ onCancel?.();
8104
+ setOpen(false);
8105
+ };
8106
+
8107
+ const handleApply = () => {
8108
+ onApply?.(selectedValues);
8109
+ setOpen(false);
8110
+ };
8111
+
8112
+ const dropdownPanel = (
8113
+ <>
8114
+ <div
8115
+ className={cn(
8116
+ "max-h-60 overflow-auto p-1",
8117
+ disabled && "pointer-events-none opacity-60",
8118
+ listClassName
8119
+ )}
8120
+ role="group"
8121
+ aria-label="Filter options"
8122
+ >
8123
+ {filteredOptions.length > 0 ? (
8124
+ filteredOptions.map((option, index) => {
8125
+ const isSelected = selectedValues.includes(option.value);
8126
+ const optionDisabled = disabled || option.disabled;
8127
+
8128
+ const optionLabelId = \`\${optionIdPrefix}-\${index}\`;
8129
+
8130
+ return (
8131
+ <div
8132
+ key={option.value}
8133
+ role="option"
8134
+ aria-selected={isSelected}
8135
+ aria-disabled={optionDisabled}
8136
+ className={cn(
8137
+ "relative flex w-full min-w-0 cursor-pointer select-none items-center gap-2 rounded-sm px-2 py-2 text-left text-sm text-semantic-text-primary outline-none",
8138
+ !isSelected &&
8139
+ "hover:bg-semantic-bg-ui focus:bg-semantic-bg-ui",
8140
+ optionDisabled && "cursor-not-allowed opacity-50"
8141
+ )}
8142
+ onClick={() => toggleOption(option)}
8143
+ >
8144
+ <Checkbox
8145
+ size="sm"
8146
+ checked={isSelected}
8147
+ disabled={optionDisabled}
8148
+ aria-label={option.label}
8149
+ className="shrink-0"
8150
+ onClick={(event) => event.stopPropagation()}
8151
+ onCheckedChange={() => toggleOption(option)}
8152
+ />
8153
+ <span id={optionLabelId} className="min-w-0 flex-1 truncate text-left">
8154
+ {searchMode === "numeric"
8155
+ ? renderHighlightedNumericLabel(
8156
+ option.label,
8157
+ normalizedSearchQuery
8158
+ )
8159
+ : option.label}
8160
+ </span>
8161
+ </div>
8162
+ );
8163
+ })
8164
+ ) : (
8165
+ <div className="px-4 py-6 text-center text-sm text-semantic-text-muted">
8166
+ {emptyMessage}
8167
+ </div>
8168
+ )}
8169
+ </div>
8170
+
8171
+ <div className="flex flex-wrap items-center justify-end gap-4 border-t border-solid border-semantic-border-layout p-4">
8172
+ <Button
8173
+ type="button"
8174
+ variant="outline"
8175
+ size="lg"
8176
+ className="min-w-0 px-4"
8177
+ disabled={disabled || cancelDisabled}
8178
+ onClick={handleCancel}
8179
+ >
8180
+ {cancelLabel}
8181
+ </Button>
8182
+ <Button
8183
+ type="button"
8184
+ size="lg"
8185
+ className="min-w-0 px-4"
8186
+ disabled={disabled || applyDisabled}
8187
+ onClick={handleApply}
8188
+ >
8189
+ {applyLabel}
8190
+ </Button>
8191
+ </div>
8192
+ </>
8193
+ );
8194
+
8195
+ return (
8196
+ <div
8197
+ ref={setRootRef}
8198
+ className={cn(searchFilterVariants({ size }), className)}
8199
+ {...props}
8200
+ >
8201
+ <div ref={setAnchorRef} className="relative">
8202
+ <Search className="pointer-events-none absolute left-4 top-1/2 size-4 -translate-y-1/2 text-semantic-text-muted" />
8203
+ <Input
8204
+ ref={searchInputRef}
8205
+ id={inputId}
8206
+ type="text"
8207
+ role="searchbox"
8208
+ placeholder={searchPlaceholder}
8209
+ value={displaySearchValue}
8210
+ disabled={disabled || searchDisabled}
8211
+ inputMode={searchMode === "numeric" ? "numeric" : inputInputMode}
8212
+ pattern={searchMode === "numeric" ? "[0-9]*" : inputPattern}
8213
+ aria-controls={dropdownId}
8214
+ aria-expanded={isOpen}
8215
+ aria-haspopup="listbox"
8216
+ className={cn("h-10 pl-10 pr-9", inputClassName)}
8217
+ {...searchInputProps}
8218
+ onClick={(event) => {
8219
+ setOpen(true);
8220
+ focusSearchInput();
8221
+ inputOnClick?.(event);
8222
+ }}
8223
+ onFocus={(event) => {
8224
+ setOpen(true);
8225
+ focusSearchInput();
8226
+ inputOnFocus?.(event);
8227
+ }}
8228
+ onKeyDown={(event) => {
8229
+ if (event.key === "Escape") {
8230
+ setOpen(false);
8231
+ }
8232
+ inputOnKeyDown?.(event);
8233
+ }}
8234
+ onChange={(event) => {
8235
+ handleSearchChange(event);
8236
+ inputOnChange?.(event);
8237
+ }}
8238
+ />
8239
+ {displaySearchValue && !disabled && !searchDisabled ? (
8240
+ <button
8241
+ type="button"
8242
+ aria-label="Clear search"
8243
+ className="absolute right-3 top-1/2 flex size-5 -translate-y-1/2 items-center justify-center rounded text-semantic-text-muted transition-colors hover:text-semantic-text-primary focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-semantic-primary focus-visible:ring-offset-1"
8244
+ onClick={handleClearSearch}
8245
+ >
8246
+ <X className="size-4" />
8247
+ </button>
8248
+ ) : null}
8249
+ </div>
8250
+ {isOpen &&
8251
+ typeof document !== "undefined" &&
8252
+ createPortal(
8253
+ <div
8254
+ ref={setDropdownRef}
8255
+ id={dropdownId}
8256
+ className={cn(
8257
+ "rounded border border-solid border-semantic-border-layout bg-semantic-bg-primary text-semantic-text-primary shadow-md",
8258
+ searchFilterDropdownVariants()
8259
+ )}
8260
+ style={{
8261
+ ...floatingStyles,
8262
+ zIndex: 10050,
8263
+ }}
8264
+ role="dialog"
8265
+ aria-labelledby={inputId}
8266
+ onMouseDown={(event) => event.stopPropagation()}
8267
+ >
8268
+ {dropdownPanel}
8269
+ </div>,
8270
+ document.body
8271
+ )}
8272
+ </div>
8273
+ );
8274
+ }
8275
+ );
8276
+ SearchFilter.displayName = "SearchFilter";
8277
+
8278
+ export { SearchFilter, searchFilterVariants };
8279
+ `,
7712
8280
  "select-field": `import * as React from "react";
7713
8281
  import { Loader2, Search } from "lucide-react";
7714
8282
 
@@ -8020,7 +8588,7 @@ const SelectField = React.forwardRef(
8020
8588
  <label
8021
8589
  htmlFor={selectId}
8022
8590
  className={cn(
8023
- "text-sm font-medium text-semantic-text-secondary",
8591
+ "text-sm font-semibold text-semantic-text-secondary",
8024
8592
  labelClassName
8025
8593
  )}
8026
8594
  >
@@ -8423,7 +8991,7 @@ const SelectLabel = React.forwardRef(({ className, ...props }: React.ComponentPr
8423
8991
  <SelectPrimitive.Label
8424
8992
  ref={ref}
8425
8993
  className={cn(
8426
- "px-4 py-1.5 text-xs font-medium text-semantic-text-muted",
8994
+ "px-4 py-1.5 text-xs font-semibold text-semantic-text-muted",
8427
8995
  className
8428
8996
  )}
8429
8997
  {...props}
@@ -9742,7 +10310,7 @@ const TextField = React.forwardRef(
9742
10310
  <label
9743
10311
  htmlFor={inputId}
9744
10312
  className={cn(
9745
- "text-sm font-medium text-semantic-text-secondary",
10313
+ "text-sm font-semibold text-semantic-text-secondary",
9746
10314
  labelClassName
9747
10315
  )}
9748
10316
  >
@@ -10079,7 +10647,7 @@ const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
10079
10647
  <label
10080
10648
  htmlFor={textareaId}
10081
10649
  className={cn(
10082
- "text-sm font-medium text-semantic-text-secondary",
10650
+ "text-sm font-semibold text-semantic-text-secondary",
10083
10651
  labelClassName
10084
10652
  )}
10085
10653
  >
@@ -11958,6 +12526,25 @@ var componentMetadata = {
11958
12526
  }
11959
12527
  ]
11960
12528
  },
12529
+ "search-filter": {
12530
+ "name": "SearchFilter",
12531
+ "description": "A search filter component.",
12532
+ "dependencies": [
12533
+ "class-variance-authority",
12534
+ "clsx",
12535
+ "tailwind-merge",
12536
+ "lucide-react"
12537
+ ],
12538
+ "props": [],
12539
+ "variants": [],
12540
+ "examples": [
12541
+ {
12542
+ "title": "Basic SearchFilter",
12543
+ "code": "<SearchFilter>Content</SearchFilter>",
12544
+ "description": "Simple search filter usage"
12545
+ }
12546
+ ]
12547
+ },
11961
12548
  "select-field": {
11962
12549
  "name": "SelectField",
11963
12550
  "description": "A form-ready select component with label, helper text, error handling, and grouped options support.",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "myoperator-mcp",
3
- "version": "0.2.349",
3
+ "version": "0.2.351",
4
4
  "description": "MCP server for myOperator UI components - enables AI assistants to access component metadata, examples, and design tokens",
5
5
  "type": "module",
6
6
  "bin": "./dist/index.js",