myoperator-mcp 0.2.321 → 0.2.323

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 +458 -50
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -2520,6 +2520,13 @@ import { cva, type VariantProps } from "class-variance-authority";
2520
2520
  import { ChevronLeft, ChevronRight, Clock2, X } from "lucide-react";
2521
2521
 
2522
2522
  import { cn } from "@/lib/utils";
2523
+ import {
2524
+ Select,
2525
+ SelectContent,
2526
+ SelectItem,
2527
+ SelectTrigger,
2528
+ SelectValue,
2529
+ } from "./select";
2523
2530
 
2524
2531
  const DEFAULT_START_TIME = "10:30:00";
2525
2532
  const DEFAULT_END_TIME = "12:30:00";
@@ -2533,6 +2540,19 @@ const POPOVER_WIDTH_VAR = "--date-time-picker-popover-width";
2533
2540
  const CALENDAR_PLACEMENT: Placement = "bottom-start";
2534
2541
  const YEAR_RANGE_BEFORE = 100;
2535
2542
  const YEAR_RANGE_AFTER = 10;
2543
+ const CALENDAR_SELECT_CONTENT_SELECTOR =
2544
+ "[data-date-time-picker-calendar-select]";
2545
+ const CALENDAR_SELECT_TRIGGER_CLASS = "!w-[90px] !gap-[6px]";
2546
+ const CALENDAR_SELECT_CONTENT_CLASS =
2547
+ "z-[10060] w-[var(--radix-select-trigger-width)] min-w-[var(--radix-select-trigger-width)] max-h-[min(16rem,var(--radix-select-content-available-height))]";
2548
+ const DATE_TIME_INPUT_SEGMENT_RANGES = {
2549
+ day: [0, 2],
2550
+ month: [3, 5],
2551
+ year: [6, 10],
2552
+ hour: [11, 13],
2553
+ minute: [14, 16],
2554
+ meridiem: [17, 19],
2555
+ } as const;
2536
2556
 
2537
2557
  const weekDays = ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"];
2538
2558
  const monthNames = Array.from({ length: 12 }, (_, monthIndex) =>
@@ -2719,6 +2739,12 @@ function isPointerInsideElement(
2719
2739
  return false;
2720
2740
  }
2721
2741
 
2742
+ function isPointerInsideSelector(event: MouseEvent, selector: string) {
2743
+ const target = event.target;
2744
+
2745
+ return target instanceof Element && target.closest(selector) !== null;
2746
+ }
2747
+
2722
2748
  function formatTimeForDisplay(time: string) {
2723
2749
  const [hour = "0", minute = "0"] = time.split(":");
2724
2750
  const hourNumber = Number(hour);
@@ -2796,6 +2822,307 @@ function parseTimePart(timePart?: string) {
2796
2822
  return \`\${hour.toString().padStart(2, "0")}:\${minuteValue}:\${secondValue}\`;
2797
2823
  }
2798
2824
 
2825
+ function getDaysInMonth(year: number, month: number) {
2826
+ return new Date(year, month, 0).getDate();
2827
+ }
2828
+
2829
+ function getPotentialMaxDay(month: number, year?: number) {
2830
+ if (month === 2 && year === undefined) return 29;
2831
+ if (month === 2) return getDaysInMonth(year ?? 2000, month);
2832
+ if ([4, 6, 9, 11].includes(month)) return 30;
2833
+
2834
+ return 31;
2835
+ }
2836
+
2837
+ function isPotentiallyValidDateParts(
2838
+ dayValue: string,
2839
+ monthValue: string,
2840
+ yearValue: string
2841
+ ) {
2842
+ const day = Number(dayValue);
2843
+ const month = Number(monthValue);
2844
+ const year = Number(yearValue);
2845
+
2846
+ if (dayValue.length === 1 && day > 3) return false;
2847
+ if (dayValue.length === 2 && (day < 1 || day > 31)) return false;
2848
+ if (monthValue.length === 1 && month > 1) return false;
2849
+ if (monthValue.length === 2 && (month < 1 || month > 12)) return false;
2850
+ if (yearValue.length === 4 && year < 1000) return false;
2851
+
2852
+ if (dayValue.length === 2 && monthValue.length === 2) {
2853
+ const maxDay = getPotentialMaxDay(
2854
+ month,
2855
+ yearValue.length === 4 ? year : undefined
2856
+ );
2857
+ if (day > maxDay) return false;
2858
+ }
2859
+
2860
+ return true;
2861
+ }
2862
+
2863
+ function isPotentiallyValidDateDigits(dateDigits: string) {
2864
+ return isPotentiallyValidDateParts(
2865
+ dateDigits.slice(0, 2),
2866
+ dateDigits.slice(2, 4),
2867
+ dateDigits.slice(4, 8)
2868
+ );
2869
+ }
2870
+
2871
+ function formatDateDigits(dateDigits: string) {
2872
+ const day = dateDigits.slice(0, 2);
2873
+ const month = dateDigits.slice(2, 4);
2874
+ const year = dateDigits.slice(4, 8);
2875
+
2876
+ if (dateDigits.length <= 2) {
2877
+ return dateDigits.length === 2 ? \`\${day}/\` : day;
2878
+ }
2879
+
2880
+ if (dateDigits.length <= 4) {
2881
+ return \`\${day}/\${dateDigits.length === 4 ? \`\${month}/\` : month}\`;
2882
+ }
2883
+
2884
+ return \`\${day}/\${month}/\${year}\`;
2885
+ }
2886
+
2887
+ function formatSegmentedDateInput(
2888
+ dateSource: string,
2889
+ day: string,
2890
+ month: string,
2891
+ year: string
2892
+ ) {
2893
+ const separatorCount = dateSource.match(/[/-]/g)?.length ?? 0;
2894
+ const shouldShowMonth = separatorCount >= 1 || !!month || !!year;
2895
+ const shouldShowYear = separatorCount >= 2 || !!year;
2896
+
2897
+ return [
2898
+ day,
2899
+ shouldShowMonth ? month : undefined,
2900
+ shouldShowYear ? year : undefined,
2901
+ ]
2902
+ .filter((part): part is string => part !== undefined)
2903
+ .join("/");
2904
+ }
2905
+
2906
+ function consumeDigits(source: string, maxLength: number) {
2907
+ let digits = "";
2908
+ let endIndex = source.length;
2909
+
2910
+ for (let index = 0; index < source.length; index += 1) {
2911
+ if (!/\\d/.test(source[index])) continue;
2912
+
2913
+ digits += source[index];
2914
+ if (digits.length === maxLength) {
2915
+ endIndex = index + 1;
2916
+ break;
2917
+ }
2918
+ }
2919
+
2920
+ return {
2921
+ digits,
2922
+ rest: source.slice(endIndex),
2923
+ };
2924
+ }
2925
+
2926
+ function splitTypedDateInput(value: string) {
2927
+ const firstSpaceIndex = value.search(/\\s/);
2928
+ const dateSource =
2929
+ firstSpaceIndex === -1 ? value : value.slice(0, firstSpaceIndex);
2930
+ const restValue =
2931
+ firstSpaceIndex === -1 ? "" : value.slice(firstSpaceIndex + 1);
2932
+
2933
+ if (/[/-]/.test(dateSource)) {
2934
+ const [dayPart = "", monthPart = "", yearPart = ""] =
2935
+ dateSource.split(/[/-]/);
2936
+ const dayResult = consumeDigits(dayPart, 2);
2937
+ const monthResult = consumeDigits(\`\${dayResult.rest}\${monthPart}\`, 2);
2938
+ const yearResult = consumeDigits(
2939
+ \`\${monthResult.rest}\${yearPart}\`,
2940
+ 4
2941
+ );
2942
+ const day = dayResult.digits;
2943
+ const month = monthResult.digits;
2944
+ const year = yearResult.digits;
2945
+
2946
+ return {
2947
+ dateDigits: \`\${day}\${month}\${year}\`,
2948
+ dateValue: formatSegmentedDateInput(dateSource, day, month, year),
2949
+ isComplete: !!day && !!month && year.length === 4,
2950
+ isValid: isPotentiallyValidDateParts(day, month, year),
2951
+ restValue: [yearResult.rest, restValue].filter(Boolean).join(" "),
2952
+ };
2953
+ }
2954
+
2955
+ let dateDigits = "";
2956
+ let dateEndIndex = value.length;
2957
+
2958
+ for (let index = 0; index < value.length; index += 1) {
2959
+ const character = value[index];
2960
+
2961
+ if (/\\d/.test(character)) {
2962
+ dateDigits += character;
2963
+ if (dateDigits.length === 8) {
2964
+ dateEndIndex = index + 1;
2965
+ break;
2966
+ }
2967
+ }
2968
+ }
2969
+
2970
+ return {
2971
+ dateDigits,
2972
+ dateValue: formatDateDigits(dateDigits),
2973
+ isComplete: dateDigits.length === 8,
2974
+ isValid: isPotentiallyValidDateDigits(dateDigits),
2975
+ restValue: value.slice(dateEndIndex),
2976
+ };
2977
+ }
2978
+
2979
+ function isPotentiallyValidTimeDigits(timeDigits: string) {
2980
+ const hourValue = timeDigits.slice(0, 2);
2981
+ const minuteValue = timeDigits.slice(2, 4);
2982
+ const hour = Number(hourValue);
2983
+ const minute = Number(minuteValue);
2984
+
2985
+ if (hourValue.length === 1 && hour > 1) return false;
2986
+ if (hourValue.length === 2 && (hour < 1 || hour > 12)) return false;
2987
+ if (minuteValue.length === 1 && minute > 5) return false;
2988
+ if (minuteValue.length === 2 && minute > 59) return false;
2989
+
2990
+ return true;
2991
+ }
2992
+
2993
+ function formatMeridiemInput(letters: string) {
2994
+ if (!letters) return "";
2995
+ if ("AM".startsWith(letters)) return letters;
2996
+ if ("PM".startsWith(letters)) return letters;
2997
+
2998
+ return null;
2999
+ }
3000
+
3001
+ function formatTimeInput(restValue: string) {
3002
+ const normalizedValue = restValue.toUpperCase();
3003
+ const timeDigits = normalizedValue.replace(/\\D/g, "").slice(0, 4);
3004
+ const meridiemLetters = normalizedValue.replace(/[^A-Z]/g, "");
3005
+ const meridiem = formatMeridiemInput(meridiemLetters.slice(0, 2));
3006
+
3007
+ if (!timeDigits && !meridiem) return "";
3008
+ if (!isPotentiallyValidTimeDigits(timeDigits) || meridiem === null) {
3009
+ return null;
3010
+ }
3011
+
3012
+ const timeValue =
3013
+ timeDigits.length <= 2
3014
+ ? timeDigits
3015
+ : \`\${timeDigits.slice(0, 2)}:\${timeDigits.slice(2, 4)}\`;
3016
+
3017
+ return [timeValue, meridiem].filter(Boolean).join(" ");
3018
+ }
3019
+
3020
+ function sanitizeTypedDateTimeInput(value: string, previousValue: string) {
3021
+ const trimmedValue = value.trimStart();
3022
+ if (/^[A-Za-z]/.test(trimmedValue)) return previousValue;
3023
+
3024
+ const normalizedValue = value
3025
+ .toUpperCase()
3026
+ .replace(/[^0-9\\s/:APM-]/g, "");
3027
+ const { dateDigits, dateValue, isComplete, isValid, restValue } =
3028
+ splitTypedDateInput(normalizedValue);
3029
+ const limitedDateDigits = dateDigits.slice(0, 8);
3030
+
3031
+ if (!limitedDateDigits) return "";
3032
+ if (!isValid) return previousValue;
3033
+
3034
+ const formattedTime = isComplete ? formatTimeInput(restValue) : "";
3035
+ if (formattedTime === null) return previousValue;
3036
+
3037
+ return [dateValue, formattedTime].filter(Boolean).join(" ");
3038
+ }
3039
+
3040
+ type DateTimeInputSegment = keyof typeof DATE_TIME_INPUT_SEGMENT_RANGES;
3041
+
3042
+ function getDateTimeInputSegment(cursorPosition: number): DateTimeInputSegment {
3043
+ if (cursorPosition <= DATE_TIME_INPUT_SEGMENT_RANGES.day[1]) return "day";
3044
+ if (cursorPosition <= DATE_TIME_INPUT_SEGMENT_RANGES.month[1]) return "month";
3045
+ if (cursorPosition <= DATE_TIME_INPUT_SEGMENT_RANGES.year[1]) return "year";
3046
+ if (cursorPosition <= DATE_TIME_INPUT_SEGMENT_RANGES.hour[1]) return "hour";
3047
+ if (cursorPosition <= DATE_TIME_INPUT_SEGMENT_RANGES.minute[1]) {
3048
+ return "minute";
3049
+ }
3050
+
3051
+ return "meridiem";
3052
+ }
3053
+
3054
+ function clampDateToMonth(year: number, month: number, day: number) {
3055
+ return new Date(year, month, Math.min(day, getDaysInMonth(year, month + 1)));
3056
+ }
3057
+
3058
+ function stepDateTimeInputValue(
3059
+ value: string,
3060
+ cursorPosition: number,
3061
+ direction: 1 | -1,
3062
+ fallbackTime: string
3063
+ ) {
3064
+ const typedDateTime = parseTypedDateTime(value);
3065
+ if (!typedDateTime) return null;
3066
+
3067
+ const segment = getDateTimeInputSegment(cursorPosition);
3068
+ const nextDate = new Date(typedDateTime.date);
3069
+ const [hourValue = "0", minuteValue = "0", secondValue = "00"] = (
3070
+ typedDateTime.startTime ?? fallbackTime
3071
+ ).split(":");
3072
+ nextDate.setHours(Number(hourValue), Number(minuteValue), Number(secondValue));
3073
+
3074
+ if (segment === "day") {
3075
+ nextDate.setDate(nextDate.getDate() + direction);
3076
+ } else if (segment === "month") {
3077
+ const day = nextDate.getDate();
3078
+ const steppedMonth = clampDateToMonth(
3079
+ nextDate.getFullYear(),
3080
+ nextDate.getMonth() + direction,
3081
+ day
3082
+ );
3083
+ nextDate.setFullYear(
3084
+ steppedMonth.getFullYear(),
3085
+ steppedMonth.getMonth(),
3086
+ steppedMonth.getDate()
3087
+ );
3088
+ } else if (segment === "year") {
3089
+ const day = nextDate.getDate();
3090
+ const steppedYear = clampDateToMonth(
3091
+ nextDate.getFullYear() + direction,
3092
+ nextDate.getMonth(),
3093
+ day
3094
+ );
3095
+ nextDate.setFullYear(
3096
+ steppedYear.getFullYear(),
3097
+ steppedYear.getMonth(),
3098
+ steppedYear.getDate()
3099
+ );
3100
+ } else if (segment === "hour") {
3101
+ nextDate.setHours(nextDate.getHours() + direction);
3102
+ } else if (segment === "minute") {
3103
+ nextDate.setMinutes(nextDate.getMinutes() + direction);
3104
+ } else {
3105
+ nextDate.setHours(nextDate.getHours() + 12 * direction);
3106
+ }
3107
+
3108
+ const startTime = \`\${nextDate
3109
+ .getHours()
3110
+ .toString()
3111
+ .padStart(2, "0")}:\${nextDate
3112
+ .getMinutes()
3113
+ .toString()
3114
+ .padStart(2, "0")}:\${nextDate
3115
+ .getSeconds()
3116
+ .toString()
3117
+ .padStart(2, "0")}\`;
3118
+
3119
+ return {
3120
+ date: startOfDay(nextDate),
3121
+ startTime,
3122
+ segment,
3123
+ };
3124
+ }
3125
+
2799
3126
  function parseTypedDateTime(value: string) {
2800
3127
  const typedValue = value.trim();
2801
3128
  if (!typedValue) return undefined;
@@ -3002,7 +3329,8 @@ const DateTimePicker = React.forwardRef<HTMLDivElement, DateTimePickerProps>(
3002
3329
  const handlePointerDown = (event: MouseEvent) => {
3003
3330
  if (
3004
3331
  !isPointerInsideElement(event, rootRef.current) &&
3005
- !isPointerInsideElement(event, popoverRef.current)
3332
+ !isPointerInsideElement(event, popoverRef.current) &&
3333
+ !isPointerInsideSelector(event, CALENDAR_SELECT_CONTENT_SELECTOR)
3006
3334
  ) {
3007
3335
  setOpen(false);
3008
3336
  }
@@ -3061,9 +3389,20 @@ const DateTimePicker = React.forwardRef<HTMLDivElement, DateTimePickerProps>(
3061
3389
  const handleTypedDateChange = (
3062
3390
  event: React.ChangeEvent<HTMLInputElement>
3063
3391
  ) => {
3064
- const nextInputValue = event.target.value;
3392
+ const nextInputValue = sanitizeTypedDateTimeInput(
3393
+ event.target.value,
3394
+ dateInputValue
3395
+ );
3065
3396
  setDateInputValue(nextInputValue);
3066
3397
 
3398
+ if (
3399
+ nextInputValue === dateInputValue &&
3400
+ event.target.value !== dateInputValue
3401
+ ) {
3402
+ event.currentTarget.value = nextInputValue;
3403
+ return;
3404
+ }
3405
+
3067
3406
  const typedDateTime = parseTypedDateTime(nextInputValue);
3068
3407
  if (typedDateTime === undefined) {
3069
3408
  updateValue({ ...currentValue, date: undefined });
@@ -3086,6 +3425,53 @@ const DateTimePicker = React.forwardRef<HTMLDivElement, DateTimePickerProps>(
3086
3425
  }
3087
3426
  };
3088
3427
 
3428
+ const handleTypedDateKeyDown = (
3429
+ event: React.KeyboardEvent<HTMLInputElement>
3430
+ ) => {
3431
+ if (event.key !== "ArrowUp" && event.key !== "ArrowDown") return;
3432
+
3433
+ const direction = event.key === "ArrowUp" ? 1 : -1;
3434
+ const cursorPosition =
3435
+ event.currentTarget.selectionStart ?? dateInputValue.length;
3436
+ const inputElement = event.currentTarget;
3437
+ const steppedDateTime = stepDateTimeInputValue(
3438
+ dateInputValue,
3439
+ cursorPosition,
3440
+ direction,
3441
+ currentValue.startTime
3442
+ );
3443
+
3444
+ if (!steppedDateTime) return;
3445
+ if (
3446
+ (effectiveMinDate &&
3447
+ isBeforeDay(steppedDateTime.date, effectiveMinDate)) ||
3448
+ (maxDate && isAfterDay(steppedDateTime.date, maxDate))
3449
+ ) {
3450
+ return;
3451
+ }
3452
+
3453
+ event.preventDefault();
3454
+
3455
+ const nextInputValue = formatDateForDisplay(
3456
+ steppedDateTime.date,
3457
+ steppedDateTime.startTime
3458
+ );
3459
+ const [selectionStart, selectionEnd] =
3460
+ DATE_TIME_INPUT_SEGMENT_RANGES[steppedDateTime.segment];
3461
+
3462
+ setDateInputValue(nextInputValue);
3463
+ updateValue({
3464
+ ...currentValue,
3465
+ date: steppedDateTime.date,
3466
+ startTime: steppedDateTime.startTime,
3467
+ });
3468
+ updateVisibleMonth(steppedDateTime.date);
3469
+
3470
+ window.requestAnimationFrame(() => {
3471
+ inputElement.setSelectionRange(selectionStart, selectionEnd);
3472
+ });
3473
+ };
3474
+
3089
3475
  const handleTypedDateBlur = () => {
3090
3476
  setIsDateInputFocused(false);
3091
3477
  setDateInputValue(
@@ -3133,71 +3519,89 @@ const DateTimePicker = React.forwardRef<HTMLDivElement, DateTimePickerProps>(
3133
3519
  >
3134
3520
  <ChevronLeft className="size-4" aria-hidden="true" />
3135
3521
  </button>
3136
- <div className="flex min-w-0 items-center gap-2">
3522
+ <div className="flex min-w-0 items-center gap-1.5">
3137
3523
  <label className="sr-only" htmlFor={\`\${triggerId}-month\`}>
3138
3524
  Month
3139
3525
  </label>
3140
- <select
3141
- id={\`\${triggerId}-month\`}
3142
- aria-label="Month"
3143
- value={visibleMonth.getMonth()}
3144
- className="h-8 rounded-md border border-solid border-semantic-border-input bg-semantic-bg-primary px-2 text-sm font-medium text-semantic-text-primary outline-none transition-colors hover:border-semantic-border-input-focus/50 focus:border-semantic-border-input-focus/50"
3145
- onChange={(event) =>
3526
+ <Select
3527
+ value={visibleMonth.getMonth().toString()}
3528
+ onValueChange={(nextMonth) =>
3146
3529
  updateVisibleMonth(
3147
3530
  new Date(
3148
3531
  visibleMonth.getFullYear(),
3149
- Number(event.target.value),
3532
+ Number(nextMonth),
3150
3533
  1
3151
3534
  )
3152
3535
  )
3153
3536
  }
3154
3537
  >
3155
- {monthNames.map((monthName, monthIndex) => {
3156
- const optionMonth = new Date(
3157
- visibleMonth.getFullYear(),
3158
- monthIndex,
3159
- 1
3160
- );
3161
- const isDisabled =
3162
- (effectiveMinDate &&
3163
- isMonthBefore(optionMonth, effectiveMinDate)) ||
3164
- (maxDate && isMonthAfter(optionMonth, maxDate));
3165
-
3166
- return (
3167
- <option
3168
- key={monthName}
3169
- value={monthIndex}
3170
- disabled={!!isDisabled}
3171
- >
3172
- {monthName}
3173
- </option>
3174
- );
3175
- })}
3176
- </select>
3538
+ <SelectTrigger
3539
+ id={\`\${triggerId}-month\`}
3540
+ aria-label="Month"
3541
+ className={CALENDAR_SELECT_TRIGGER_CLASS}
3542
+ >
3543
+ <SelectValue />
3544
+ </SelectTrigger>
3545
+ <SelectContent
3546
+ data-date-time-picker-calendar-select=""
3547
+ className={CALENDAR_SELECT_CONTENT_CLASS}
3548
+ >
3549
+ {monthNames.map((monthName, monthIndex) => {
3550
+ const optionMonth = new Date(
3551
+ visibleMonth.getFullYear(),
3552
+ monthIndex,
3553
+ 1
3554
+ );
3555
+ const isDisabled =
3556
+ (effectiveMinDate &&
3557
+ isMonthBefore(optionMonth, effectiveMinDate)) ||
3558
+ (maxDate && isMonthAfter(optionMonth, maxDate));
3559
+
3560
+ return (
3561
+ <SelectItem
3562
+ key={monthName}
3563
+ value={monthIndex.toString()}
3564
+ disabled={!!isDisabled}
3565
+ >
3566
+ {monthName}
3567
+ </SelectItem>
3568
+ );
3569
+ })}
3570
+ </SelectContent>
3571
+ </Select>
3177
3572
  <label className="sr-only" htmlFor={\`\${triggerId}-year\`}>
3178
3573
  Year
3179
3574
  </label>
3180
- <select
3181
- id={\`\${triggerId}-year\`}
3182
- aria-label="Year"
3183
- value={visibleMonth.getFullYear()}
3184
- className="h-8 rounded-md border border-solid border-semantic-border-input bg-semantic-bg-primary px-2 text-sm font-medium text-semantic-text-primary outline-none transition-colors hover:border-semantic-border-input-focus/50 focus:border-semantic-border-input-focus/50"
3185
- onChange={(event) =>
3575
+ <Select
3576
+ value={visibleMonth.getFullYear().toString()}
3577
+ onValueChange={(nextYear) =>
3186
3578
  updateVisibleMonth(
3187
3579
  new Date(
3188
- Number(event.target.value),
3580
+ Number(nextYear),
3189
3581
  visibleMonth.getMonth(),
3190
3582
  1
3191
3583
  )
3192
3584
  )
3193
3585
  }
3194
3586
  >
3195
- {yearOptions.map((year) => (
3196
- <option key={year} value={year}>
3197
- {year}
3198
- </option>
3199
- ))}
3200
- </select>
3587
+ <SelectTrigger
3588
+ id={\`\${triggerId}-year\`}
3589
+ aria-label="Year"
3590
+ className={CALENDAR_SELECT_TRIGGER_CLASS}
3591
+ >
3592
+ <SelectValue />
3593
+ </SelectTrigger>
3594
+ <SelectContent
3595
+ data-date-time-picker-calendar-select=""
3596
+ className={CALENDAR_SELECT_CONTENT_CLASS}
3597
+ >
3598
+ {yearOptions.map((year) => (
3599
+ <SelectItem key={year} value={year.toString()}>
3600
+ {year}
3601
+ </SelectItem>
3602
+ ))}
3603
+ </SelectContent>
3604
+ </Select>
3201
3605
  <div id={\`\${triggerId}-calendar-heading\`} className="sr-only">
3202
3606
  {monthFormatter.format(visibleMonth)}
3203
3607
  </div>
@@ -3384,6 +3788,7 @@ const DateTimePicker = React.forwardRef<HTMLDivElement, DateTimePickerProps>(
3384
3788
  }}
3385
3789
  onClick={() => setOpen(true)}
3386
3790
  onChange={handleTypedDateChange}
3791
+ onKeyDown={handleTypedDateKeyDown}
3387
3792
  onBlur={handleTypedDateBlur}
3388
3793
  />
3389
3794
  {showClear && displayValue && !disabled && !readOnly && (
@@ -6607,7 +7012,7 @@ const ReplyQuote = React.forwardRef(
6607
7012
  <div
6608
7013
  ref={ref}
6609
7014
  className={cn(
6610
- "tw-w-full tw-min-w-0 tw-bg-[var(--semantic-bg-ui,#F5F5F5)] tw-border-l-[3px] tw-border-solid tw-border-[var(--semantic-border-accent,#27ABB8)] tw-rounded-sm tw-px-4 tw-py-1.5 tw-mb-2 tw-h-[56px] tw-flex tw-flex-col tw-justify-center tw-gap-0 tw-overflow-hidden tw-cursor-pointer hover:tw-bg-[var(--semantic-bg-hover,#D5D7DA)] tw-transition-colors",
7015
+ "tw-max-w-full tw-min-w-0 tw-bg-[var(--semantic-bg-ui,#F5F5F5)] tw-border-l-[3px] tw-border-solid tw-border-[var(--semantic-border-accent,#27ABB8)] tw-rounded-sm tw-px-4 tw-py-1.5 tw-mb-2 tw-h-[56px] tw-flex tw-flex-col tw-justify-center tw-gap-0 tw-overflow-hidden tw-cursor-pointer hover:tw-bg-[var(--semantic-bg-hover,#D5D7DA)] tw-transition-colors",
6611
7016
  isInteractive && "focus-visible:tw-ring-2 focus-visible:tw-ring-[var(--semantic-border-focus,#2BBCCA)] focus-visible:tw-ring-offset-1 focus-visible:tw-outline-none",
6612
7017
  className
6613
7018
  )}
@@ -6621,9 +7026,9 @@ const ReplyQuote = React.forwardRef(
6621
7026
  <p className="tw-m-0 tw-min-w-0 tw-shrink-0 tw-truncate tw-text-[14px] tw-font-semibold tw-leading-5 tw-tracking-[0.014px] tw-text-[var(--semantic-text-primary,#181D27)]">
6622
7027
  {sender}
6623
7028
  </p>
6624
- <p className="tw-m-0 tw-min-h-0 tw-min-w-0 tw-flex-1 tw-truncate tw-text-[14px] tw-leading-5 tw-text-[var(--semantic-text-muted,#717680)]">
7029
+ <div className="tw-m-0 tw-min-h-0 tw-min-w-0 tw-flex-1 tw-overflow-hidden tw-text-ellipsis tw-whitespace-nowrap tw-text-[14px] tw-leading-5 tw-text-[var(--semantic-text-muted,#717680)] [&_*]:tw-inline">
6625
7030
  {message}
6626
- </p>
7031
+ </div>
6627
7032
  </div>
6628
7033
  );
6629
7034
  }
@@ -6977,6 +7382,7 @@ const SelectField = React.forwardRef(
6977
7382
  </SelectTrigger>
6978
7383
  <SelectContent
6979
7384
  onViewportScrollEnd={hasMore !== false ? onScrollEnd : undefined}
7385
+ hideScrollButtons={totalRendered === 0}
6980
7386
  >
6981
7387
  {/* Search input */}
6982
7388
  {searchable && (
@@ -7233,6 +7639,7 @@ export type SelectContentProps = React.ComponentPropsWithoutRef<
7233
7639
  * can still read scrollTop/scrollHeight/clientHeight from it.
7234
7640
  */
7235
7641
  onViewportScrollEnd?: (event: React.UIEvent<HTMLDivElement>) => void;
7642
+ hideScrollButtons?: boolean;
7236
7643
  };
7237
7644
 
7238
7645
  const BOTTOM_THRESHOLD_PX = 24;
@@ -7245,6 +7652,7 @@ const SelectContent = React.forwardRef(
7245
7652
  children,
7246
7653
  position = "popper",
7247
7654
  onViewportScrollEnd,
7655
+ hideScrollButtons,
7248
7656
  ...props
7249
7657
  }: SelectContentProps,
7250
7658
  ref: React.Ref<React.ElementRef<typeof SelectPrimitive.Content>>
@@ -7318,7 +7726,7 @@ const SelectContent = React.forwardRef(
7318
7726
  position={position}
7319
7727
  {...props}
7320
7728
  >
7321
- <SelectScrollUpButton />
7729
+ {!hideScrollButtons && <SelectScrollUpButton />}
7322
7730
  <SelectPrimitive.Viewport
7323
7731
  ref={setViewport}
7324
7732
  data-select-viewport=""
@@ -7330,7 +7738,7 @@ const SelectContent = React.forwardRef(
7330
7738
  >
7331
7739
  {children}
7332
7740
  </SelectPrimitive.Viewport>
7333
- <SelectScrollDownButton />
7741
+ {!hideScrollButtons && <SelectScrollDownButton />}
7334
7742
  </SelectPrimitive.Content>
7335
7743
  </SelectPrimitive.Portal>
7336
7744
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "myoperator-mcp",
3
- "version": "0.2.321",
3
+ "version": "0.2.323",
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",