myoperator-mcp 0.2.267 → 0.2.269

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 +159 -105
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1457,7 +1457,7 @@ import { cn } from "@/lib/utils"
1457
1457
  import {
1458
1458
  creatableSelectTriggerVariants,
1459
1459
  creatableEnterHintKbdClassName,
1460
- creatableToneHintRowClassName,
1460
+ creatablePrimaryRoleHintRowClassName,
1461
1461
  } from "./creatable-select"
1462
1462
 
1463
1463
  /** @deprecated Use \`creatableSelectTriggerVariants\` from \`./creatable-select\` \u2014 aliases the same trigger styles as Primary Role. */
@@ -1629,13 +1629,20 @@ const CreatableMultiSelect = React.forwardRef(
1629
1629
  const availablePresets = options.filter(
1630
1630
  (o) => !value.includes(o.value) && !o.disabled
1631
1631
  )
1632
- const filteredPresets = inputValue.trim()
1632
+ const trimmedInput = inputValue.trim()
1633
+ const filteredPresets = trimmedInput
1633
1634
  ? availablePresets.filter((o) =>
1634
- o.label.toLowerCase().includes(inputValue.trim().toLowerCase())
1635
+ o.label.toLowerCase().includes(trimmedInput.toLowerCase())
1635
1636
  )
1636
1637
  : availablePresets
1637
1638
 
1638
- const panelInputPlaceholder = createHintText ?? placeholder
1639
+ const isCustomDraft =
1640
+ trimmedInput.length > 0 &&
1641
+ !options.some(
1642
+ (o) => o.label.toLowerCase() === trimmedInput.toLowerCase()
1643
+ ) &&
1644
+ !value.includes(trimmedInput) &&
1645
+ (maxItems == null || value.length < maxItems)
1639
1646
 
1640
1647
  const summaryTriggerLabel =
1641
1648
  value.length === 0 ? placeholder : selectedSummary
@@ -1662,27 +1669,7 @@ const CreatableMultiSelect = React.forwardRef(
1662
1669
  }}
1663
1670
  >
1664
1671
  <div className="flex min-h-0 min-w-0 flex-1 flex-wrap content-start items-center gap-1.5">
1665
- {triggerDisplay === "summary" ? (
1666
- <span
1667
- className={cn(
1668
- "line-clamp-2 flex-1 text-base",
1669
- value.length === 0
1670
- ? "text-semantic-text-muted"
1671
- : "text-semantic-text-primary"
1672
- )}
1673
- >
1674
- {summaryTriggerLabel}
1675
- </span>
1676
- ) : value.length === 0 ? (
1677
- <span
1678
- className={cn(
1679
- "line-clamp-2 flex-1 text-base",
1680
- "text-semantic-text-muted"
1681
- )}
1682
- >
1683
- {placeholder}
1684
- </span>
1685
- ) : (
1672
+ {triggerDisplay === "chips" &&
1686
1673
  value.map((val) => (
1687
1674
  <span
1688
1675
  key={val}
@@ -1713,9 +1700,45 @@ const CreatableMultiSelect = React.forwardRef(
1713
1700
  <X className="size-3.5" strokeWidth={2} aria-hidden />
1714
1701
  </button>
1715
1702
  </span>
1716
- ))
1717
- )}
1703
+ ))}
1704
+ {triggerDisplay === "summary" && value.length > 0 ? (
1705
+ <span className="line-clamp-2 text-base text-semantic-text-primary">
1706
+ {selectedSummary}
1707
+ </span>
1708
+ ) : null}
1709
+ <input
1710
+ ref={inputRef}
1711
+ type="text"
1712
+ value={inputValue}
1713
+ onChange={(e) => {
1714
+ const raw = e.target.value
1715
+ const sanitized = sanitizeInput ? sanitizeInput(raw) : raw
1716
+ if (sanitizeInput) {
1717
+ if (raw !== sanitized) onInvalidCharacters?.()
1718
+ else onValidInput?.()
1719
+ }
1720
+ setInputValue(
1721
+ maxLengthPerItem != null
1722
+ ? sanitized.slice(0, maxLengthPerItem)
1723
+ : sanitized
1724
+ )
1725
+ }}
1726
+ maxLength={maxLengthPerItem}
1727
+ onKeyDown={handleKeyDown}
1728
+ disabled={disabled}
1729
+ className="min-w-[120px] flex-1 bg-transparent text-base text-semantic-text-primary outline-none placeholder:text-semantic-text-muted"
1730
+ role="combobox"
1731
+ aria-expanded={isOpen}
1732
+ aria-controls={listboxId}
1733
+ aria-haspopup="listbox"
1734
+ aria-autocomplete="list"
1735
+ />
1718
1736
  </div>
1737
+ {maxLengthPerItem != null && showPerItemCharacterCounter ? (
1738
+ <span className="mr-2 mt-1 shrink-0 self-start text-sm text-semantic-text-muted">
1739
+ {inputValue.length}/{maxLengthPerItem}
1740
+ </span>
1741
+ ) : null}
1719
1742
  <ChevronDown
1720
1743
  className="mt-1 size-4 shrink-0 self-start rotate-180 text-semantic-text-muted opacity-70 transition-transform"
1721
1744
  aria-hidden
@@ -1813,104 +1836,94 @@ const CreatableMultiSelect = React.forwardRef(
1813
1836
  </div>
1814
1837
  )}
1815
1838
 
1816
- {/* Dropdown panel: input + limits + presets (Figma: summary row stays above; type-to-create lives here) */}
1839
+ {/* Dropdown panel: decorative hint row, counter, max-selections, presets. Input lives in the trigger above. */}
1817
1840
  {isOpen && (
1818
1841
  <div
1819
1842
  className="absolute left-0 top-full z-[9999] mt-1 flex w-full flex-col overflow-hidden rounded border border-solid border-semantic-border-layout bg-semantic-bg-primary shadow-sm animate-in fade-in-0 zoom-in-95 slide-in-from-top-2 duration-200"
1820
1843
  >
1821
- <div className="-mx-0 flex shrink-0 flex-col border-b border-solid border-semantic-border-layout">
1822
- <div className={creatableToneHintRowClassName}>
1823
- <input
1824
- ref={inputRef}
1825
- type="text"
1826
- value={inputValue}
1827
- onChange={(e) => {
1828
- const raw = e.target.value
1829
- const sanitized = sanitizeInput ? sanitizeInput(raw) : raw
1830
- if (sanitizeInput) {
1831
- if (raw !== sanitized) onInvalidCharacters?.()
1832
- else onValidInput?.()
1833
- }
1834
- setInputValue(
1835
- maxLengthPerItem != null
1836
- ? sanitized.slice(0, maxLengthPerItem)
1837
- : sanitized
1838
- )
1839
- }}
1840
- maxLength={maxLengthPerItem}
1841
- onKeyDown={handleKeyDown}
1842
- disabled={disabled}
1843
- placeholder={panelInputPlaceholder}
1844
- className="min-w-0 flex-1 bg-transparent text-sm text-semantic-text-primary outline-none placeholder:text-semantic-text-muted"
1845
- role="combobox"
1846
- aria-expanded={isOpen}
1847
- aria-controls={listboxId}
1848
- aria-haspopup="listbox"
1849
- aria-autocomplete="list"
1850
- />
1851
- <kbd className={cn(creatableEnterHintKbdClassName, "shrink-0")}>
1844
+ {createHintText ? (
1845
+ <div className={creatablePrimaryRoleHintRowClassName}>
1846
+ <span className="text-sm text-semantic-text-muted">
1847
+ {createHintText}
1848
+ </span>
1849
+ <kbd className={creatableEnterHintKbdClassName}>
1852
1850
  Enter \u21B5
1853
1851
  </kbd>
1854
1852
  </div>
1855
- </div>
1856
-
1857
- <div className="flex flex-col gap-2.5 px-4 pb-4 pt-0">
1858
- {maxItems != null ? (
1859
- <p className="m-0 py-1 text-sm text-semantic-text-muted">
1860
- Max selections allowed: {maxItems}
1861
- </p>
1862
- ) : null}
1853
+ ) : null}
1863
1854
 
1855
+ {(filteredPresets.length > 0 || isCustomDraft) && (
1864
1856
  <div
1865
- id={listboxId}
1866
- role="listbox"
1867
- className="flex flex-wrap gap-1.5"
1857
+ className={cn(
1858
+ "flex flex-col px-4",
1859
+ filteredPresets.length > 0
1860
+ ? "gap-2.5 pb-4 pt-2.5"
1861
+ : "py-1"
1862
+ )}
1868
1863
  >
1869
- {filteredPresets.map((option) => (
1864
+ {maxItems != null && filteredPresets.length > 0 ? (
1865
+ <p className="m-0 text-sm text-semantic-text-muted">
1866
+ Max selections allowed: {maxItems}
1867
+ </p>
1868
+ ) : null}
1869
+
1870
+ {filteredPresets.length > 0 ? (
1871
+ <div
1872
+ id={listboxId}
1873
+ role="listbox"
1874
+ className="flex flex-wrap gap-1.5"
1875
+ >
1876
+ {filteredPresets.map((option) => (
1877
+ <button
1878
+ key={option.value}
1879
+ type="button"
1880
+ role="option"
1881
+ aria-selected={false}
1882
+ onMouseDown={(e) => {
1883
+ e.preventDefault()
1884
+ addValue(option.value)
1885
+ }}
1886
+ className="inline-flex items-center gap-2.5 whitespace-nowrap rounded border-0 bg-semantic-bg-ui px-2 py-1 text-left text-sm text-semantic-text-primary transition-colors hover:bg-semantic-bg-hover"
1887
+ >
1888
+ <Plus
1889
+ className="size-2.5 shrink-0 text-semantic-text-muted"
1890
+ strokeWidth={2}
1891
+ aria-hidden
1892
+ />
1893
+ {option.label}
1894
+ </button>
1895
+ ))}
1896
+ </div>
1897
+ ) : null}
1898
+
1899
+ {isCustomDraft ? (
1870
1900
  <button
1871
- key={option.value}
1872
1901
  type="button"
1873
1902
  role="option"
1874
1903
  aria-selected={false}
1875
1904
  onMouseDown={(e) => {
1876
1905
  e.preventDefault()
1877
- addValue(option.value)
1906
+ addValue(inputValue)
1878
1907
  }}
1879
- className="inline-flex items-center gap-2.5 whitespace-nowrap rounded border-0 bg-semantic-bg-ui px-2 py-1 text-left text-sm text-semantic-text-primary transition-colors hover:bg-semantic-bg-hover"
1908
+ className="-mx-4 flex w-[calc(100%+2rem)] items-center gap-2 rounded-none px-4 py-2 text-left text-base text-semantic-text-link outline-none transition-colors cursor-pointer select-none hover:bg-semantic-bg-ui"
1880
1909
  >
1881
- <Plus
1882
- className="size-2.5 shrink-0 text-semantic-text-muted"
1883
- strokeWidth={2}
1884
- aria-hidden
1885
- />
1886
- {option.label}
1910
+ Create &ldquo;{trimmedInput}&rdquo;
1887
1911
  </button>
1888
- ))}
1912
+ ) : null}
1889
1913
  </div>
1890
- </div>
1914
+ )}
1891
1915
  </div>
1892
1916
  )}
1893
1917
  </div>
1894
1918
 
1895
- {maxLengthPerItem != null &&
1896
- showPerItemCharacterCounter &&
1897
- isOpen ? (
1898
- <div className="mt-1.5 flex items-center justify-end gap-2">
1899
- <span className="shrink-0 text-sm text-semantic-text-muted">
1900
- {inputValue.length}/{maxLengthPerItem}
1901
- </span>
1919
+ {helperText && !isOpen ? (
1920
+ <div className="mt-1.5 flex items-center gap-1.5">
1921
+ <Info className="size-[18px] shrink-0 text-semantic-text-muted" />
1922
+ <p className="m-0 text-sm text-semantic-text-muted">
1923
+ {helperText}
1924
+ </p>
1902
1925
  </div>
1903
- ) : (
1904
- helperText &&
1905
- !isOpen && (
1906
- <div className="mt-1.5 flex items-center gap-1.5">
1907
- <Info className="size-[18px] shrink-0 text-semantic-text-muted" />
1908
- <p className="m-0 text-sm text-semantic-text-muted">
1909
- {helperText}
1910
- </p>
1911
- </div>
1912
- )
1913
- )}
1926
+ ) : null}
1914
1927
  </div>
1915
1928
  )
1916
1929
  }
@@ -1988,7 +2001,7 @@ export interface CreatableSelectProps
1988
2001
  creatableHint?: string
1989
2002
  /** Whether the select is disabled */
1990
2003
  disabled?: boolean
1991
- /** Max character length for the value (enforced when open and when creating) */
2004
+ /** Max character length for the value (enforced when open and when creating). When set, an in-field \`current/max\` counter renders before the chevron while the dropdown is open. */
1992
2005
  maxLength?: number
1993
2006
  /**
1994
2007
  * When set, combobox input is transformed (e.g. strip invalid characters).
@@ -2202,6 +2215,11 @@ const CreatableSelect = React.forwardRef(
2202
2215
  role="combobox"
2203
2216
  aria-autocomplete="list"
2204
2217
  />
2218
+ {maxLength != null ? (
2219
+ <span className="mr-2 shrink-0 text-sm text-semantic-text-muted">
2220
+ {search.length}/{maxLength}
2221
+ </span>
2222
+ ) : null}
2205
2223
  <ChevronDown className="size-4 text-semantic-text-muted opacity-70 shrink-0 rotate-180 transition-transform" />
2206
2224
  </div>
2207
2225
  ) : (
@@ -3155,6 +3173,7 @@ import { Check } from "lucide-react";
3155
3173
  import { cn } from "@/lib/utils";
3156
3174
 
3157
3175
  const blockedNumberKeys = new Set(["e", "E"]);
3176
+ const decimalSeparatorKeys = new Set([".", ","]);
3158
3177
 
3159
3178
  /**
3160
3179
  * Input variants for different visual states
@@ -3203,6 +3222,15 @@ export interface InputProps
3203
3222
  * Default \`true\` so amount-like fields cannot accept exponent values such as \`2e22\`.
3204
3223
  */
3205
3224
  preventNumberExponent?: boolean;
3225
+ /**
3226
+ * When \`type="number"\`, whether decimal separators (\`.\` / \`,\`) are allowed.
3227
+ * Default \`true\`. Set \`false\` for whole-number-only fields; uses \`inputMode="numeric"\` on supported agents.
3228
+ */
3229
+ decimal?: boolean;
3230
+ /**
3231
+ * Same as \`decimal\` for whole-number-only fields. If both are set, this wins.
3232
+ */
3233
+ allowDecimal?: boolean;
3206
3234
  }
3207
3235
 
3208
3236
  const Input = React.forwardRef(
@@ -3214,19 +3242,25 @@ const Input = React.forwardRef(
3214
3242
  showCheckIcon,
3215
3243
  hideNumberSpinners = true,
3216
3244
  preventNumberExponent = true,
3245
+ decimal = true,
3246
+ allowDecimal,
3217
3247
  onFocus,
3218
3248
  onBlur,
3219
3249
  onWheel,
3220
3250
  onKeyDown,
3221
3251
  onPaste,
3222
3252
  onChange,
3253
+ step,
3223
3254
  ...props
3224
3255
  }: InputProps,
3225
3256
  ref: React.Ref<HTMLInputElement>
3226
3257
  ) => {
3227
3258
  const [isFocused, setIsFocused] = React.useState(false);
3259
+ const decimalAllowed = allowDecimal ?? decimal;
3228
3260
  const shouldPreventNumberExponent =
3229
3261
  type === "number" && preventNumberExponent;
3262
+ const shouldBlockDecimals =
3263
+ type === "number" && !decimalAllowed;
3230
3264
 
3231
3265
  const inputEl = (
3232
3266
  <input
@@ -3239,6 +3273,16 @@ const Input = React.forwardRef(
3239
3273
  "[appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none"
3240
3274
  )}
3241
3275
  ref={ref}
3276
+ inputMode={
3277
+ type === "number"
3278
+ ? decimalAllowed
3279
+ ? "decimal"
3280
+ : "numeric"
3281
+ : undefined
3282
+ }
3283
+ step={
3284
+ type === "number" && !decimalAllowed ? (step ?? 1) : step
3285
+ }
3242
3286
  onFocus={(e) => {
3243
3287
  setIsFocused(true);
3244
3288
  onFocus?.(e);
@@ -3259,13 +3303,20 @@ const Input = React.forwardRef(
3259
3303
  if (shouldPreventNumberExponent && blockedNumberKeys.has(e.key)) {
3260
3304
  e.preventDefault();
3261
3305
  }
3306
+ if (
3307
+ shouldBlockDecimals &&
3308
+ decimalSeparatorKeys.has(e.key)
3309
+ ) {
3310
+ e.preventDefault();
3311
+ }
3262
3312
  onKeyDown?.(e);
3263
3313
  }}
3264
3314
  onPaste={(e) => {
3265
- if (
3266
- shouldPreventNumberExponent &&
3267
- /[eE]/.test(e.clipboardData.getData("text"))
3268
- ) {
3315
+ const text = e.clipboardData.getData("text");
3316
+ if (shouldPreventNumberExponent && /[eE]/.test(text)) {
3317
+ e.preventDefault();
3318
+ }
3319
+ if (shouldBlockDecimals && /[.,]/.test(text)) {
3269
3320
  e.preventDefault();
3270
3321
  }
3271
3322
  onPaste?.(e);
@@ -3274,6 +3325,9 @@ const Input = React.forwardRef(
3274
3325
  if (shouldPreventNumberExponent && /[eE]/.test(e.target.value)) {
3275
3326
  return;
3276
3327
  }
3328
+ if (shouldBlockDecimals && /[.,]/.test(e.target.value)) {
3329
+ return;
3330
+ }
3277
3331
  onChange?.(e);
3278
3332
  }}
3279
3333
  {...props}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "myoperator-mcp",
3
- "version": "0.2.267",
3
+ "version": "0.2.269",
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",