myoperator-mcp 0.2.347 → 0.2.349

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 +149 -1
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -5152,6 +5152,28 @@ import { cn } from "@/lib/utils";
5152
5152
 
5153
5153
  const blockedNumberKeys = new Set(["e", "E"]);
5154
5154
  const decimalSeparatorKeys = new Set([".", ","]);
5155
+ const consecutiveSpaceInputTypes = new Set([
5156
+ "email",
5157
+ "password",
5158
+ "search",
5159
+ "tel",
5160
+ "text",
5161
+ "url",
5162
+ ]);
5163
+
5164
+ function shouldPreventConsecutiveSpacesForType(
5165
+ type: React.HTMLInputTypeAttribute | undefined
5166
+ ): boolean {
5167
+ return type == null || consecutiveSpaceInputTypes.has(type);
5168
+ }
5169
+
5170
+ function collapseConsecutiveSpaces(value: string): string {
5171
+ return value.replace(/ {2,}/g, " ");
5172
+ }
5173
+
5174
+ function getCollapsedCursorPosition(value: string, cursorPosition: number) {
5175
+ return collapseConsecutiveSpaces(value.slice(0, cursorPosition)).length;
5176
+ }
5155
5177
 
5156
5178
  /**
5157
5179
  * Input variants for different visual states
@@ -5209,6 +5231,11 @@ export interface InputProps
5209
5231
  * Same as \`decimal\` for whole-number-only fields. If both are set, this wins.
5210
5232
  */
5211
5233
  allowDecimal?: boolean;
5234
+ /**
5235
+ * Prevents inserting a second consecutive space in text-like inputs while
5236
+ * preserving the user's current cursor position. Defaults to \`true\`.
5237
+ */
5238
+ preventConsecutiveSpaces?: boolean;
5212
5239
  }
5213
5240
 
5214
5241
  const Input = React.forwardRef(
@@ -5222,11 +5249,13 @@ const Input = React.forwardRef(
5222
5249
  preventNumberExponent = true,
5223
5250
  decimal = true,
5224
5251
  allowDecimal,
5252
+ preventConsecutiveSpaces = true,
5225
5253
  onFocus,
5226
5254
  onBlur,
5227
5255
  onWheel,
5228
5256
  onKeyDown,
5229
5257
  onPaste,
5258
+ onBeforeInput,
5230
5259
  onChange,
5231
5260
  step,
5232
5261
  ...props
@@ -5239,6 +5268,8 @@ const Input = React.forwardRef(
5239
5268
  type === "number" && preventNumberExponent;
5240
5269
  const shouldBlockDecimals =
5241
5270
  type === "number" && !decimalAllowed;
5271
+ const shouldPreventConsecutiveSpaces =
5272
+ preventConsecutiveSpaces && shouldPreventConsecutiveSpacesForType(type);
5242
5273
 
5243
5274
  const inputEl = (
5244
5275
  <input
@@ -5302,6 +5333,33 @@ const Input = React.forwardRef(
5302
5333
  }
5303
5334
  onPaste?.(e);
5304
5335
  }}
5336
+ onBeforeInput={(e) => {
5337
+ onBeforeInput?.(e);
5338
+ if (!shouldPreventConsecutiveSpaces || e.defaultPrevented) {
5339
+ return;
5340
+ }
5341
+
5342
+ const nativeEvent = e.nativeEvent as InputEvent;
5343
+ if (nativeEvent.inputType !== "insertText" || nativeEvent.data !== " ") {
5344
+ return;
5345
+ }
5346
+
5347
+ const input = e.currentTarget;
5348
+ const selectionStart = input.selectionStart ?? input.value.length;
5349
+ const selectionEnd = input.selectionEnd ?? selectionStart;
5350
+ const nextValue =
5351
+ input.value.slice(0, selectionStart) +
5352
+ nativeEvent.data +
5353
+ input.value.slice(selectionEnd);
5354
+
5355
+ if (nextValue.includes(" ")) {
5356
+ e.preventDefault();
5357
+ input.setSelectionRange(selectionStart, selectionStart);
5358
+ window.requestAnimationFrame(() => {
5359
+ input.setSelectionRange(selectionStart, selectionStart);
5360
+ });
5361
+ }
5362
+ }}
5305
5363
  onChange={(e) => {
5306
5364
  if (shouldPreventNumberExponent && /[eE]/.test(e.target.value)) {
5307
5365
  return;
@@ -5309,6 +5367,21 @@ const Input = React.forwardRef(
5309
5367
  if (shouldBlockDecimals && /[.,]/.test(e.target.value)) {
5310
5368
  return;
5311
5369
  }
5370
+ if (shouldPreventConsecutiveSpaces && e.target.value.includes(" ")) {
5371
+ const input = e.currentTarget;
5372
+ const rawValue = input.value;
5373
+ const rawCursor = input.selectionStart ?? rawValue.length;
5374
+ const collapsedValue = collapseConsecutiveSpaces(rawValue);
5375
+ const nextCursor = Math.min(
5376
+ getCollapsedCursorPosition(rawValue, rawCursor),
5377
+ collapsedValue.length
5378
+ );
5379
+
5380
+ input.value = collapsedValue;
5381
+ window.requestAnimationFrame(() => {
5382
+ input.setSelectionRange(nextCursor, nextCursor);
5383
+ });
5384
+ }
5312
5385
  onChange?.(e);
5313
5386
  }}
5314
5387
  {...props}
@@ -9351,6 +9424,29 @@ import { Loader2, X } from "lucide-react";
9351
9424
 
9352
9425
  import { cn } from "@/lib/utils";
9353
9426
 
9427
+ const consecutiveSpaceInputTypes = new Set([
9428
+ "email",
9429
+ "password",
9430
+ "search",
9431
+ "tel",
9432
+ "text",
9433
+ "url",
9434
+ ]);
9435
+
9436
+ function shouldPreventConsecutiveSpacesForType(
9437
+ type: React.HTMLInputTypeAttribute | undefined
9438
+ ): boolean {
9439
+ return type == null || consecutiveSpaceInputTypes.has(type);
9440
+ }
9441
+
9442
+ function collapseConsecutiveSpaces(value: string): string {
9443
+ return value.replace(/ {2,}/g, " ");
9444
+ }
9445
+
9446
+ function getCollapsedCursorPosition(value: string, cursorPosition: number) {
9447
+ return collapseConsecutiveSpaces(value.slice(0, cursorPosition)).length;
9448
+ }
9449
+
9354
9450
  /**
9355
9451
  * TextField container variants for when icons/prefix/suffix are present
9356
9452
  */
@@ -9453,6 +9549,11 @@ export interface TextFieldProps
9453
9549
  labelClassName?: string;
9454
9550
  /** Additional class for the input container (includes prefix/suffix/icons) */
9455
9551
  inputContainerClassName?: string;
9552
+ /**
9553
+ * Prevents inserting a second consecutive space in text-like inputs while
9554
+ * preserving the user's current cursor position. Defaults to \`true\`.
9555
+ */
9556
+ preventConsecutiveSpaces?: boolean;
9456
9557
  }
9457
9558
 
9458
9559
  const TextField = React.forwardRef(
@@ -9480,10 +9581,12 @@ const TextField = React.forwardRef(
9480
9581
  value,
9481
9582
  defaultValue,
9482
9583
  onChange,
9584
+ onBeforeInput,
9483
9585
  onWheel,
9484
9586
  disabled,
9485
9587
  id,
9486
9588
  type,
9589
+ preventConsecutiveSpaces = true,
9487
9590
  ...props
9488
9591
  }: TextFieldProps,
9489
9592
  ref: React.ForwardedRef<HTMLInputElement>
@@ -9510,9 +9613,27 @@ const TextField = React.forwardRef(
9510
9613
 
9511
9614
  // Derive state from props
9512
9615
  const derivedState = error ? "error" : (state ?? "default");
9616
+ const shouldPreventConsecutiveSpaces =
9617
+ preventConsecutiveSpaces && shouldPreventConsecutiveSpacesForType(type);
9513
9618
 
9514
9619
  // Handle change for both controlled and uncontrolled
9515
9620
  const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
9621
+ if (shouldPreventConsecutiveSpaces && e.target.value.includes(" ")) {
9622
+ const input = e.currentTarget;
9623
+ const rawValue = input.value;
9624
+ const rawCursor = input.selectionStart ?? rawValue.length;
9625
+ const collapsedValue = collapseConsecutiveSpaces(rawValue);
9626
+ const nextCursor = Math.min(
9627
+ getCollapsedCursorPosition(rawValue, rawCursor),
9628
+ collapsedValue.length
9629
+ );
9630
+
9631
+ input.value = collapsedValue;
9632
+ window.requestAnimationFrame(() => {
9633
+ input.setSelectionRange(nextCursor, nextCursor);
9634
+ });
9635
+ }
9636
+
9516
9637
  if (!isControlled) {
9517
9638
  setInternalValue(e.target.value);
9518
9639
  }
@@ -9572,6 +9693,33 @@ const TextField = React.forwardRef(
9572
9693
  maxLength={maxLength}
9573
9694
  value={isControlled ? value : undefined}
9574
9695
  defaultValue={!isControlled ? defaultValue : undefined}
9696
+ onBeforeInput={(e) => {
9697
+ onBeforeInput?.(e);
9698
+ if (!shouldPreventConsecutiveSpaces || e.defaultPrevented) {
9699
+ return;
9700
+ }
9701
+
9702
+ const nativeEvent = e.nativeEvent as InputEvent;
9703
+ if (nativeEvent.inputType !== "insertText" || nativeEvent.data !== " ") {
9704
+ return;
9705
+ }
9706
+
9707
+ const input = e.currentTarget;
9708
+ const selectionStart = input.selectionStart ?? input.value.length;
9709
+ const selectionEnd = input.selectionEnd ?? selectionStart;
9710
+ const nextValue =
9711
+ input.value.slice(0, selectionStart) +
9712
+ nativeEvent.data +
9713
+ input.value.slice(selectionEnd);
9714
+
9715
+ if (nextValue.includes(" ")) {
9716
+ e.preventDefault();
9717
+ input.setSelectionRange(selectionStart, selectionStart);
9718
+ window.requestAnimationFrame(() => {
9719
+ input.setSelectionRange(selectionStart, selectionStart);
9720
+ });
9721
+ }
9722
+ }}
9575
9723
  onChange={handleChange}
9576
9724
  onWheel={
9577
9725
  type === "number"
@@ -9825,7 +9973,7 @@ const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
9825
9973
  }: TextareaProps,
9826
9974
  ref
9827
9975
  ) => {
9828
- const textareaRef = React.useRef<HTMLTextAreaElement>(null);
9976
+ const textareaRef = React.useRef<HTMLTextAreaElement | null>(null);
9829
9977
  const pendingSelectionRef = React.useRef<[number, number] | null>(null);
9830
9978
 
9831
9979
  const setTextareaRef = React.useCallback(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "myoperator-mcp",
3
- "version": "0.2.347",
3
+ "version": "0.2.349",
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",