myoperator-mcp 0.2.260 → 0.2.262

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 +145 -70
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1492,6 +1492,11 @@ export interface CreatableMultiSelectProps
1492
1492
  * Set to false to match Figma (counter lives only in the field flow / not under the control).
1493
1493
  */
1494
1494
  showPerItemCharacterCounter?: boolean
1495
+ /**
1496
+ * Closed trigger: show removable chips (default) or a single comma-separated summary line (Figma Tone).
1497
+ * While open, the trigger always shows the summary line; selected chips with remove controls appear in the panel.
1498
+ */
1499
+ triggerDisplay?: "chips" | "summary"
1495
1500
  /**
1496
1501
  * When set, the text input is transformed (e.g. strip invalid characters).
1497
1502
  * If the raw value differs from the sanitized value, \`onInvalidCharacters\` is called.
@@ -1537,6 +1542,7 @@ const CreatableMultiSelect = React.forwardRef(
1537
1542
  maxItems,
1538
1543
  maxLengthPerItem,
1539
1544
  showPerItemCharacterCounter = true,
1545
+ triggerDisplay = "chips",
1540
1546
  sanitizeInput,
1541
1547
  onInvalidCharacters,
1542
1548
  onValidInput,
@@ -1637,13 +1643,10 @@ const CreatableMultiSelect = React.forwardRef(
1637
1643
  !value.includes(draftForCreate) &&
1638
1644
  (maxItems == null || value.length < maxItems)
1639
1645
 
1640
- const hasHintCopy = Boolean(createHintText) || maxItems != null
1646
+ const panelInputPlaceholder = createHintText ?? placeholder
1641
1647
 
1642
- const showHintsSection = hasHintCopy || canShowEnterAffordance
1643
-
1644
- const inputPlaceholder = isOpen
1645
- ? selectedSummary || placeholder
1646
- : ""
1648
+ const summaryTriggerLabel =
1649
+ value.length === 0 ? placeholder : selectedSummary
1647
1650
 
1648
1651
  return (
1649
1652
  <div
@@ -1652,48 +1655,33 @@ const CreatableMultiSelect = React.forwardRef(
1652
1655
  {...props}
1653
1656
  >
1654
1657
  <div className="relative w-full">
1655
- {isOpen ? (
1658
+ {isOpen && (
1656
1659
  <div
1657
1660
  className={cn(
1658
1661
  creatableSelectTriggerVariants({ state: derivedState }),
1659
- "cursor-text"
1662
+ "flex h-auto min-h-[42px] cursor-text items-start gap-2 py-2 text-left"
1660
1663
  )}
1661
- onClick={() => inputRef.current?.focus()}
1664
+ onClick={() => !disabled && inputRef.current?.focus()}
1665
+ aria-hidden="true"
1662
1666
  >
1663
- <input
1664
- ref={inputRef}
1665
- type="text"
1666
- value={inputValue}
1667
- onChange={(e) => {
1668
- const raw = e.target.value
1669
- const sanitized = sanitizeInput ? sanitizeInput(raw) : raw
1670
- if (sanitizeInput) {
1671
- if (raw !== sanitized) onInvalidCharacters?.()
1672
- else onValidInput?.()
1673
- }
1674
- setInputValue(
1675
- maxLengthPerItem != null
1676
- ? sanitized.slice(0, maxLengthPerItem)
1677
- : sanitized
1678
- )
1679
- }}
1680
- maxLength={maxLengthPerItem}
1681
- onKeyDown={handleKeyDown}
1682
- disabled={disabled}
1683
- placeholder={inputPlaceholder}
1684
- className="flex-1 min-w-0 bg-transparent outline-none text-base text-semantic-text-primary placeholder:text-semantic-text-muted"
1685
- role="combobox"
1686
- aria-expanded={isOpen}
1687
- aria-controls={listboxId}
1688
- aria-haspopup="listbox"
1689
- aria-autocomplete="list"
1690
- />
1667
+ <span
1668
+ className={cn(
1669
+ "line-clamp-2 min-w-0 flex-1 text-base",
1670
+ value.length === 0
1671
+ ? "text-semantic-text-muted"
1672
+ : "text-semantic-text-primary"
1673
+ )}
1674
+ >
1675
+ {summaryTriggerLabel}
1676
+ </span>
1691
1677
  <ChevronRight
1692
- className="size-5 text-semantic-text-muted shrink-0 opacity-70"
1678
+ className="mt-1 size-5 shrink-0 self-start text-semantic-text-muted opacity-70"
1693
1679
  aria-hidden
1694
1680
  />
1695
1681
  </div>
1696
- ) : (
1682
+ )}
1683
+
1684
+ {!isOpen && (
1697
1685
  <div
1698
1686
  role="combobox"
1699
1687
  tabIndex={disabled ? -1 : 0}
@@ -1717,12 +1705,23 @@ const CreatableMultiSelect = React.forwardRef(
1717
1705
  }}
1718
1706
  className={cn(
1719
1707
  creatableSelectTriggerVariants({ state: derivedState }),
1720
- "flex min-h-[42px] cursor-pointer items-center gap-2 py-1 text-left outline-none focus-visible:ring-2 focus-visible:ring-semantic-border-focus focus-visible:ring-offset-2 focus-visible:ring-offset-semantic-bg-primary",
1708
+ "flex h-auto min-h-[42px] cursor-pointer items-start gap-2 py-2 text-left outline-none focus-visible:ring-2 focus-visible:ring-semantic-border-focus focus-visible:ring-offset-2 focus-visible:ring-offset-semantic-bg-primary",
1721
1709
  disabled && "pointer-events-none cursor-not-allowed"
1722
1710
  )}
1723
1711
  >
1724
- <div className="flex min-h-0 min-w-0 flex-1 flex-wrap items-center gap-1.5">
1725
- {value.length === 0 ? (
1712
+ <div className="flex min-h-0 min-w-0 flex-1 flex-wrap content-start items-center gap-1.5">
1713
+ {triggerDisplay === "summary" ? (
1714
+ <span
1715
+ className={cn(
1716
+ "line-clamp-2 flex-1 text-base",
1717
+ value.length === 0
1718
+ ? "text-semantic-text-muted"
1719
+ : "text-semantic-text-primary"
1720
+ )}
1721
+ >
1722
+ {summaryTriggerLabel}
1723
+ </span>
1724
+ ) : value.length === 0 ? (
1726
1725
  <span
1727
1726
  className={cn(
1728
1727
  "line-clamp-2 flex-1 text-base",
@@ -1766,52 +1765,128 @@ const CreatableMultiSelect = React.forwardRef(
1766
1765
  )}
1767
1766
  </div>
1768
1767
  <ChevronRight
1769
- className="size-5 shrink-0 text-semantic-text-muted opacity-70"
1768
+ className="mt-1 size-5 shrink-0 self-start text-semantic-text-muted opacity-70"
1770
1769
  aria-hidden
1771
1770
  />
1772
1771
  </div>
1773
1772
  )}
1774
1773
 
1775
- {/* Dropdown panel */}
1774
+ {/* Dropdown panel: input + limits + presets (Figma: summary row stays above; type-to-create lives here) */}
1776
1775
  {isOpen && (
1777
1776
  <div
1778
- id={listboxId}
1779
- role="listbox"
1780
- className="absolute left-0 top-full z-[9999] mt-1 flex w-full flex-col gap-2.5 overflow-hidden rounded border border-solid border-semantic-border-layout bg-semantic-bg-primary px-4 pb-4 pt-0 shadow-sm animate-in fade-in-0 zoom-in-95 slide-in-from-top-2 duration-200"
1777
+ 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"
1781
1778
  >
1782
- {showHintsSection && (
1783
- <div className="-mx-4 flex shrink-0 flex-col border-b border-solid border-semantic-border-layout">
1784
- <div className={creatableToneHintRowClassName}>
1785
- {createHintText ? (
1786
- <span className="text-sm text-semantic-text-muted">
1787
- {createHintText}
1788
- </span>
1789
- ) : (
1790
- <span className="min-w-0 flex-1" />
1779
+ <div className="-mx-0 flex shrink-0 flex-col border-b border-solid border-semantic-border-layout">
1780
+ <div className={creatableToneHintRowClassName}>
1781
+ <input
1782
+ ref={inputRef}
1783
+ type="text"
1784
+ value={inputValue}
1785
+ onChange={(e) => {
1786
+ const raw = e.target.value
1787
+ const sanitized = sanitizeInput ? sanitizeInput(raw) : raw
1788
+ if (sanitizeInput) {
1789
+ if (raw !== sanitized) onInvalidCharacters?.()
1790
+ else onValidInput?.()
1791
+ }
1792
+ setInputValue(
1793
+ maxLengthPerItem != null
1794
+ ? sanitized.slice(0, maxLengthPerItem)
1795
+ : sanitized
1796
+ )
1797
+ }}
1798
+ maxLength={maxLengthPerItem}
1799
+ onKeyDown={handleKeyDown}
1800
+ disabled={disabled}
1801
+ placeholder={panelInputPlaceholder}
1802
+ className="min-w-0 flex-1 bg-transparent text-base text-semantic-text-primary outline-none placeholder:text-semantic-text-muted"
1803
+ role="combobox"
1804
+ aria-expanded={isOpen}
1805
+ aria-controls={listboxId}
1806
+ aria-haspopup="listbox"
1807
+ aria-autocomplete="list"
1808
+ />
1809
+ <button
1810
+ type="button"
1811
+ disabled={disabled || !canShowEnterAffordance}
1812
+ onMouseDown={(e) => {
1813
+ e.preventDefault()
1814
+ }}
1815
+ onClick={() => {
1816
+ if (draftForCreate) addValue(inputValue)
1817
+ }}
1818
+ className={cn(
1819
+ creatableEnterHintKbdClassName,
1820
+ "shrink-0 enabled:cursor-pointer enabled:hover:bg-semantic-bg-hover disabled:opacity-50"
1791
1821
  )}
1792
- <kbd className={creatableEnterHintKbdClassName}>
1793
- Enter \u21B5
1794
- </kbd>
1795
- </div>
1796
- {maxItems != null ? (
1797
- <div className="border-t border-solid border-semantic-border-layout bg-semantic-bg-ui px-4 py-2 text-left text-sm text-semantic-text-muted">
1798
- Max selections allowed: {maxItems}
1799
- </div>
1800
- ) : null}
1822
+ >
1823
+ Enter \u21B5
1824
+ </button>
1801
1825
  </div>
1802
- )}
1826
+ </div>
1803
1827
 
1804
- {filteredPresets.length > 0 && (
1805
- <div className="flex flex-wrap gap-1.5">
1828
+ <div className="flex flex-col gap-2.5 px-4 pb-4 pt-0">
1829
+ {maxItems != null ? (
1830
+ <p className="m-0 py-1 text-sm text-semantic-text-muted">
1831
+ Max selections allowed: {maxItems}
1832
+ </p>
1833
+ ) : null}
1834
+
1835
+ {value.length > 0 ? (
1836
+ <div
1837
+ className="flex flex-wrap gap-1.5 border-b border-solid border-semantic-border-layout pb-2.5"
1838
+ aria-label="Selected values"
1839
+ >
1840
+ {value.map((val) => (
1841
+ <span
1842
+ key={val}
1843
+ className="inline-flex max-w-full items-center gap-0.5 rounded bg-semantic-bg-ui py-1 pl-2 pr-0.5 text-sm text-semantic-text-primary"
1844
+ >
1845
+ <span className="min-w-0 truncate">
1846
+ {labelForValue(val, options)}
1847
+ </span>
1848
+ <button
1849
+ type="button"
1850
+ data-chip-remove
1851
+ disabled={disabled}
1852
+ aria-label={\`Remove \${labelForValue(val, options)}\`}
1853
+ className={cn(
1854
+ "inline-flex size-6 shrink-0 items-center justify-center rounded text-semantic-text-muted transition-colors",
1855
+ !disabled &&
1856
+ "hover:bg-semantic-bg-hover hover:text-semantic-text-primary"
1857
+ )}
1858
+ onMouseDown={(e) => {
1859
+ e.preventDefault()
1860
+ e.stopPropagation()
1861
+ }}
1862
+ onClick={(e) => {
1863
+ e.stopPropagation()
1864
+ if (!disabled) removeValue(val)
1865
+ }}
1866
+ >
1867
+ <X className="size-3.5" strokeWidth={2} aria-hidden />
1868
+ </button>
1869
+ </span>
1870
+ ))}
1871
+ </div>
1872
+ ) : null}
1873
+
1874
+ <div
1875
+ id={listboxId}
1876
+ role="listbox"
1877
+ className="flex flex-wrap gap-1.5"
1878
+ >
1806
1879
  {filteredPresets.map((option) => (
1807
1880
  <button
1808
1881
  key={option.value}
1809
1882
  type="button"
1883
+ role="option"
1884
+ aria-selected={false}
1810
1885
  onMouseDown={(e) => {
1811
1886
  e.preventDefault()
1812
1887
  addValue(option.value)
1813
1888
  }}
1814
- className="inline-flex items-center gap-2.5 whitespace-nowrap rounded border-0 bg-semantic-bg-ui px-2 py-1 text-sm text-semantic-text-primary transition-colors hover:bg-semantic-bg-hover"
1889
+ 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"
1815
1890
  >
1816
1891
  <Plus
1817
1892
  className="size-2.5 shrink-0 text-semantic-text-muted"
@@ -1822,7 +1897,7 @@ const CreatableMultiSelect = React.forwardRef(
1822
1897
  </button>
1823
1898
  ))}
1824
1899
  </div>
1825
- )}
1900
+ </div>
1826
1901
  </div>
1827
1902
  )}
1828
1903
  </div>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "myoperator-mcp",
3
- "version": "0.2.260",
3
+ "version": "0.2.262",
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",