@douyinfe/semi-foundation 2.93.0 → 2.94.1

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 (77) hide show
  1. package/aiChatInput/foundation.ts +3 -1
  2. package/autoComplete/foundation.ts +3 -2
  3. package/cascader/foundation.ts +8 -3
  4. package/collapsible/foundation.ts +1 -0
  5. package/datePicker/datePicker.scss +2 -2
  6. package/descriptions/foundation.ts +3 -1
  7. package/form/foundation.ts +61 -29
  8. package/form/interface.ts +9 -2
  9. package/input/textareaFoundation.ts +34 -1
  10. package/lib/cjs/aiChatInput/foundation.js +3 -1
  11. package/lib/cjs/autoComplete/foundation.d.ts +1 -0
  12. package/lib/cjs/autoComplete/foundation.js +1 -1
  13. package/lib/cjs/cascader/foundation.d.ts +1 -0
  14. package/lib/cjs/cascader/foundation.js +8 -3
  15. package/lib/cjs/collapsible/foundation.d.ts +1 -0
  16. package/lib/cjs/datePicker/datePicker.css +2 -2
  17. package/lib/cjs/datePicker/datePicker.scss +2 -2
  18. package/lib/cjs/descriptions/foundation.js +3 -1
  19. package/lib/cjs/form/foundation.d.ts +5 -5
  20. package/lib/cjs/form/foundation.js +58 -21
  21. package/lib/cjs/form/interface.d.ts +8 -2
  22. package/lib/cjs/input/textareaFoundation.d.ts +12 -0
  23. package/lib/cjs/input/textareaFoundation.js +39 -0
  24. package/lib/cjs/modal/modalFoundation.d.ts +6 -0
  25. package/lib/cjs/modal/modalFoundation.js +36 -4
  26. package/lib/cjs/pagination/foundation.js +28 -7
  27. package/lib/cjs/pincode/foundation.d.ts +1 -1
  28. package/lib/cjs/resizable/resizable.css +3 -3
  29. package/lib/cjs/resizable/variables.scss +2 -2
  30. package/lib/cjs/select/foundation.d.ts +1 -1
  31. package/lib/cjs/select/foundation.js +19 -8
  32. package/lib/cjs/steps/bacisSteps.scss +8 -2
  33. package/lib/cjs/steps/steps.css +6 -0
  34. package/lib/cjs/upload/foundation.d.ts +8 -1
  35. package/lib/cjs/upload/foundation.js +70 -22
  36. package/lib/cjs/utils/escapeHtml.d.ts +9 -0
  37. package/lib/cjs/utils/escapeHtml.js +90 -0
  38. package/lib/cjs/utils/object.js +3 -1
  39. package/lib/es/aiChatInput/foundation.js +3 -1
  40. package/lib/es/autoComplete/foundation.d.ts +1 -0
  41. package/lib/es/autoComplete/foundation.js +1 -1
  42. package/lib/es/cascader/foundation.d.ts +1 -0
  43. package/lib/es/cascader/foundation.js +8 -3
  44. package/lib/es/collapsible/foundation.d.ts +1 -0
  45. package/lib/es/datePicker/datePicker.css +2 -2
  46. package/lib/es/datePicker/datePicker.scss +2 -2
  47. package/lib/es/descriptions/foundation.js +3 -1
  48. package/lib/es/form/foundation.d.ts +5 -5
  49. package/lib/es/form/foundation.js +58 -21
  50. package/lib/es/form/interface.d.ts +8 -2
  51. package/lib/es/input/textareaFoundation.d.ts +12 -0
  52. package/lib/es/input/textareaFoundation.js +39 -0
  53. package/lib/es/modal/modalFoundation.d.ts +6 -0
  54. package/lib/es/modal/modalFoundation.js +36 -4
  55. package/lib/es/pagination/foundation.js +28 -7
  56. package/lib/es/pincode/foundation.d.ts +1 -1
  57. package/lib/es/resizable/resizable.css +3 -3
  58. package/lib/es/resizable/variables.scss +2 -2
  59. package/lib/es/select/foundation.d.ts +1 -1
  60. package/lib/es/select/foundation.js +19 -8
  61. package/lib/es/steps/bacisSteps.scss +8 -2
  62. package/lib/es/steps/steps.css +6 -0
  63. package/lib/es/upload/foundation.d.ts +8 -1
  64. package/lib/es/upload/foundation.js +70 -22
  65. package/lib/es/utils/escapeHtml.d.ts +9 -0
  66. package/lib/es/utils/escapeHtml.js +84 -0
  67. package/lib/es/utils/object.js +3 -1
  68. package/modal/modalFoundation.ts +33 -32
  69. package/package.json +35 -5
  70. package/pagination/foundation.ts +25 -7
  71. package/pincode/foundation.ts +1 -1
  72. package/resizable/variables.scss +2 -2
  73. package/select/foundation.ts +19 -8
  74. package/steps/bacisSteps.scss +8 -2
  75. package/upload/foundation.ts +81 -24
  76. package/utils/escapeHtml.ts +94 -0
  77. package/utils/object.ts +3 -1
@@ -42,6 +42,12 @@ export type ScrollToErrorOptions<K> = {
42
42
  index?: number;
43
43
  scrollOpts?: ScrollIntoViewOptions;
44
44
  };
45
+ export interface ValidateOptions<K extends keyof any = keyof any> {
46
+ /** Fields to validate, if not specified, validate all fields */
47
+ fields?: Array<K>;
48
+ /** Whether to validate silently (without updating UI or setting touched state) */
49
+ silent?: boolean;
50
+ }
45
51
  export interface BaseFormApi<T extends object = any> {
46
52
  /** get value of field */
47
53
  getValue: <P extends FieldPath<T>>(field?: P) => FieldPathValue<T, P>;
@@ -65,8 +71,8 @@ export interface BaseFormApi<T extends object = any> {
65
71
  submitForm: () => void;
66
72
  /** reset form manual */
67
73
  reset: (fields?: Array<string>) => void;
68
- /** trigger validate manual */
69
- validate: <K extends keyof T, Params extends Array<K>, V extends Params[number]>(fields?: Params) => Promise<{
74
+ /** trigger validate manual */
75
+ validate: <K extends keyof T, Params extends Array<K>, V extends Params[number]>(fields?: Params | ValidateOptions<K>) => Promise<{
70
76
  [R in V]: T[R];
71
77
  }>;
72
78
  getInitValue: <K extends keyof T>(field: K) => any;
@@ -18,6 +18,8 @@ export interface TextAreaAdapter extends Partial<DefaultAdapter>, Partial<TextAr
18
18
  notifyPressEnter(e: any): void;
19
19
  getRef(): HTMLInputElement;
20
20
  notifyHeightUpdate(e: any): void;
21
+ focusInput(): void;
22
+ isEventTarget(e: any): boolean;
21
23
  }
22
24
  export default class TextAreaFoundation extends BaseFoundation<TextAreaAdapter> {
23
25
  static get textAreaDefaultAdapter(): {
@@ -67,4 +69,14 @@ export default class TextAreaFoundation extends BaseFoundation<TextAreaAdapter>
67
69
  handleMouseLeave(e: any): void;
68
70
  isAllowClear(): boolean;
69
71
  handleClear(e: any): void;
72
+ /**
73
+ * trigger when click textarea wrapper
74
+ * @param {Event} e
75
+ */
76
+ handleClick(e: any): void;
77
+ /**
78
+ * trigger when click textarea counter
79
+ * @param {Event} e
80
+ */
81
+ handleCounterClick(e: any): void;
70
82
  }
@@ -277,5 +277,44 @@ class TextAreaFoundation extends _foundation.default {
277
277
  this._adapter.notifyClear(e);
278
278
  this.stopPropagation(e);
279
279
  }
280
+ /**
281
+ * trigger when click textarea wrapper
282
+ * @param {Event} e
283
+ */
284
+ handleClick(e) {
285
+ const {
286
+ disabled,
287
+ readonly
288
+ } = this._adapter.getProps();
289
+ const {
290
+ isFocus
291
+ } = this._adapter.getStates();
292
+ if (disabled || readonly || isFocus) {
293
+ return;
294
+ }
295
+ // do not handle bubbling up events
296
+ if (this._adapter.isEventTarget(e)) {
297
+ this._adapter.focusInput();
298
+ this._adapter.toggleFocusing(true);
299
+ }
300
+ }
301
+ /**
302
+ * trigger when click textarea counter
303
+ * @param {Event} e
304
+ */
305
+ handleCounterClick(e) {
306
+ const {
307
+ disabled,
308
+ readonly
309
+ } = this._adapter.getProps();
310
+ const {
311
+ isFocus
312
+ } = this._adapter.getStates();
313
+ if (disabled || readonly || isFocus) {
314
+ return;
315
+ }
316
+ this._adapter.focusInput();
317
+ this._adapter.toggleFocusing(true);
318
+ }
280
319
  }
281
320
  exports.default = TextAreaFoundation;
@@ -61,10 +61,16 @@ export interface ModalState {
61
61
  onCancelReturnPromiseStatus?: "pending" | "fulfilled" | "rejected";
62
62
  }
63
63
  export default class ModalFoundation extends BaseFoundation<ModalAdapter> {
64
+ private _debouncedOk;
65
+ private _debouncedCancel;
66
+ private _lastCancelTarget;
67
+ private _lastOkTarget;
64
68
  constructor(adapter: ModalAdapter);
65
69
  destroy(): void;
66
70
  handleCancel(e: any): void;
67
71
  handleOk(e: any): void;
72
+ private _invokeCancel;
73
+ private _invokeOk;
68
74
  beforeShow(): void;
69
75
  afterHide(): void;
70
76
  enabledBodyScroll(): void;
@@ -4,23 +4,55 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.default = void 0;
7
+ var _debounce2 = _interopRequireDefault(require("lodash/debounce"));
7
8
  var _foundation = _interopRequireDefault(require("../base/foundation"));
8
9
  var _isPromise = _interopRequireDefault(require("../utils/isPromise"));
9
10
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
10
11
  class ModalFoundation extends _foundation.default {
11
12
  constructor(adapter) {
12
13
  super(Object.assign({}, adapter));
13
- // afterClose() {
14
- // this._adapter.notifyClose();
15
- // }
14
+ this._debouncedOk = (0, _debounce2.default)(e => {
15
+ this._invokeOk(e);
16
+ }, 100, {
17
+ leading: true,
18
+ trailing: false
19
+ });
20
+ this._debouncedCancel = (0, _debounce2.default)(e => {
21
+ this._invokeCancel(e);
22
+ }, 100, {
23
+ leading: true,
24
+ trailing: false
25
+ });
26
+ this._lastCancelTarget = null;
27
+ this._lastOkTarget = null;
16
28
  this.toggleDisplayNone = (displayNone, callback) => {
17
29
  this._adapter.toggleDisplayNone(displayNone, callback);
18
30
  };
19
31
  }
20
32
  destroy() {
33
+ this._debouncedOk.cancel();
34
+ this._debouncedCancel.cancel();
21
35
  this.afterHide();
22
36
  }
23
37
  handleCancel(e) {
38
+ var _a, _b;
39
+ const target = (_b = (_a = e === null || e === void 0 ? void 0 : e.currentTarget) !== null && _a !== void 0 ? _a : e === null || e === void 0 ? void 0 : e.target) !== null && _b !== void 0 ? _b : null;
40
+ if (target !== this._lastCancelTarget) {
41
+ this._debouncedCancel.cancel();
42
+ }
43
+ this._lastCancelTarget = target;
44
+ this._debouncedCancel(e);
45
+ }
46
+ handleOk(e) {
47
+ var _a, _b;
48
+ const target = (_b = (_a = e === null || e === void 0 ? void 0 : e.currentTarget) !== null && _a !== void 0 ? _a : e === null || e === void 0 ? void 0 : e.target) !== null && _b !== void 0 ? _b : null;
49
+ if (target !== this._lastOkTarget) {
50
+ this._debouncedOk.cancel();
51
+ }
52
+ this._lastOkTarget = target;
53
+ this._debouncedOk(e);
54
+ }
55
+ _invokeCancel(e) {
24
56
  var _a;
25
57
  const result = this._adapter.notifyCancel(e);
26
58
  if ((0, _isPromise.default)(result)) {
@@ -39,7 +71,7 @@ class ModalFoundation extends _foundation.default {
39
71
  });
40
72
  }
41
73
  }
42
- handleOk(e) {
74
+ _invokeOk(e) {
43
75
  var _a;
44
76
  const result = this._adapter.notifyOk(e);
45
77
  if ((0, _isPromise.default)(result)) {
@@ -55,7 +55,13 @@ class PaginationFoundation extends _foundation.default {
55
55
  prevIsDisabled = false;
56
56
  nextIsDisabled = true;
57
57
  }
58
- this._adapter.setDisabled(prevIsDisabled, nextIsDisabled);
58
+ const {
59
+ prevDisabled: currentPrevDisabled,
60
+ nextDisabled: currentNextDisabled
61
+ } = this.getStates();
62
+ if (prevIsDisabled !== currentPrevDisabled || nextIsDisabled !== currentNextDisabled) {
63
+ this._adapter.setDisabled(prevIsDisabled, nextIsDisabled);
64
+ }
59
65
  }
60
66
  goPage(targetPageIndex) {
61
67
  if (targetPageIndex === '...') {
@@ -99,9 +105,17 @@ class PaginationFoundation extends _foundation.default {
99
105
  total,
100
106
  pageSize
101
107
  });
102
- this._adapter.updateTotal(total);
103
- this._adapter.setCurrentPage(targetPageIndex);
104
- this._adapter.updatePageSize(pageSize);
108
+ // Only call setState when value actually changed to avoid unnecessary re-renders
109
+ // that can cause infinite loops in React 18's concurrent batching
110
+ if (total !== this.getState('total')) {
111
+ this._adapter.updateTotal(total);
112
+ }
113
+ if (targetPageIndex !== this.getState('currentPage')) {
114
+ this._adapter.setCurrentPage(targetPageIndex);
115
+ }
116
+ if (pageSize !== this.getState('pageSize')) {
117
+ this._adapter.updatePageSize(pageSize);
118
+ }
105
119
  }
106
120
  updateAllPageNumbers(total, pageSize) {
107
121
  // only need to update in small size
@@ -237,9 +251,16 @@ class PaginationFoundation extends _foundation.default {
237
251
  total,
238
252
  currentPage
239
253
  } = this.getStates();
240
- // After converting the switching page capacity, which page is the current page
241
- const currentPageFirstItemIndex = (currentPage - 1) * pageSize + 1;
242
- const newCurrentPage = Math.ceil(currentPageFirstItemIndex / newPageSize);
254
+ // Check if we should prevent page change when pageSize changes
255
+ const {
256
+ preventPageChangeOnPageSizeChange
257
+ } = this.getProps();
258
+ let newCurrentPage = currentPage;
259
+ if (!preventPageChangeOnPageSizeChange) {
260
+ // After converting the switching page capacity, which page is the current page
261
+ const currentPageFirstItemIndex = (currentPage - 1) * pageSize + 1;
262
+ newCurrentPage = Math.ceil(currentPageFirstItemIndex / newPageSize);
263
+ }
243
264
  this.updatePage(newCurrentPage, total, newPageSize);
244
265
  if (currentPage !== newCurrentPage) {
245
266
  this._adapter.notifyPageChange(newCurrentPage);
@@ -3,7 +3,7 @@ export interface PinCodeBaseProps {
3
3
  disabled?: boolean;
4
4
  value?: string;
5
5
  format?: "number" | "mixed" | RegExp | ((value: string) => boolean);
6
- onChange: (value: string) => void;
6
+ onChange?: (value: string) => void;
7
7
  defaultValue?: string;
8
8
  count?: number;
9
9
  autoFocus?: boolean;
@@ -9,7 +9,7 @@
9
9
  .semi-resizable-resizableHandler {
10
10
  position: absolute;
11
11
  user-select: none;
12
- z-index: 2000;
12
+ z-index: 10;
13
13
  }
14
14
  .semi-resizable-resizableHandler-top {
15
15
  width: 100%;
@@ -91,7 +91,7 @@
91
91
  }
92
92
  .semi-resizable-handler {
93
93
  user-select: none;
94
- z-index: 2000;
94
+ z-index: 10;
95
95
  display: flex;
96
96
  align-items: center;
97
97
  justify-content: center;
@@ -114,7 +114,7 @@
114
114
  height: 100%;
115
115
  width: 100%;
116
116
  inset: 0;
117
- z-index: 2010;
117
+ z-index: 20;
118
118
  opacity: 0;
119
119
  position: fixed;
120
120
  }
@@ -1,5 +1,5 @@
1
- $z-resizable_handler: 2000 !default; // 伸缩框组件中handler的z-index
2
- $z-resizable_background: 2010; // 伸缩框组件中背景的z-index
1
+ $z-resizable_handler: 10 !default; // 伸缩框组件中handler的z-index,保持较低层级避免覆盖弹出层组件
2
+ $z-resizable_background: 20; // 伸缩框组件中背景的z-index,保持略高于handler
3
3
 
4
4
  $height-row-handler: 10px; // 单个伸缩框中上下handler的高度
5
5
  $width-col-handler: 10px; // 单个伸缩框中左右handler的宽度
@@ -54,7 +54,7 @@ export default class SelectFoundation extends BaseFoundation<SelectAdapter> {
54
54
  constructor(adapter: SelectAdapter);
55
55
  _keydownHandler: (...arg: any[]) => void | null;
56
56
  init(): void;
57
- focus(): void;
57
+ focus(optionsForOpen?: BasicOptionProps[]): void;
58
58
  _focusTrigger(): void;
59
59
  destroy(): void;
60
60
  _setDropdownWidth(): void;
@@ -43,20 +43,31 @@ class SelectFoundation extends _foundation.default {
43
43
  }
44
44
  const autoFocus = this.getProp('autoFocus');
45
45
  if (autoFocus) {
46
- this.focus();
46
+ this.focus(originalOptions);
47
47
  }
48
48
  }
49
- focus() {
49
+ focus(optionsForOpen) {
50
50
  const isFilterable = this._isFilterable();
51
51
  const isMultiple = this._isMultiple();
52
+ const {
53
+ isOpen
54
+ } = this.getStates();
52
55
  this._adapter.updateFocusState(true);
53
56
  this._adapter.setIsFocusInContainer(false);
54
- if (isFilterable && isMultiple) {
55
- // when filter and multiple, only focus input
56
- this.focusInput();
57
- } else if (isFilterable && !isMultiple) {
58
- // when filter and not multiple, only show input and focus input
59
- this.toggle2SearchInput(true);
57
+ if (isFilterable) {
58
+ if (isMultiple) {
59
+ // when filter and multiple, focus input and open dropdown
60
+ this.focusInput();
61
+ if (!isOpen) {
62
+ this.open(undefined, optionsForOpen);
63
+ }
64
+ } else {
65
+ // when filter and not multiple, show input, focus it and open dropdown
66
+ this.toggle2SearchInput(true);
67
+ if (!isOpen) {
68
+ this.open(undefined, optionsForOpen);
69
+ }
70
+ }
60
71
  } else {
61
72
  this._focusTrigger();
62
73
  }
@@ -69,11 +69,17 @@ $basicType: #{$module}-basic;
69
69
 
70
70
  .#{$item}-title {
71
71
  max-width: $width-steps_basic_item_title-maxWidth;
72
+ min-height: $height-steps_basic_item_left-icon;
73
+ display: inline-flex;
74
+ align-items: center;
72
75
 
73
76
  .#{$item}-title-text {
74
77
  @include text-overflow-hidden;
75
- transition: color $transition_duration-steps_item_title-text $transition_function-steps_item_title-text $transition_delay-steps_item_title-text
76
-
78
+ transition: color $transition_duration-steps_item_title-text $transition_function-steps_item_title-text $transition_delay-steps_item_title-text;
79
+
80
+ &-empty {
81
+ width: 0;
82
+ }
77
83
  }
78
84
  }
79
85
 
@@ -193,6 +193,9 @@
193
193
  }
194
194
  .semi-steps-basic.semi-steps-horizontal .semi-steps-item .semi-steps-item-title {
195
195
  max-width: 80%;
196
+ min-height: 24px;
197
+ display: inline-flex;
198
+ align-items: center;
196
199
  }
197
200
  .semi-steps-basic.semi-steps-horizontal .semi-steps-item .semi-steps-item-title .semi-steps-item-title-text {
198
201
  overflow: hidden;
@@ -200,6 +203,9 @@
200
203
  white-space: nowrap;
201
204
  transition: color var(--semi-transition_duration-none) var(--semi-transition_function-easeIn) var(--semi-transition_delay-none);
202
205
  }
206
+ .semi-steps-basic.semi-steps-horizontal .semi-steps-item .semi-steps-item-title .semi-steps-item-title-text-empty {
207
+ width: 0;
208
+ }
203
209
  .semi-steps-basic.semi-steps-vertical {
204
210
  display: flex;
205
211
  flex-flow: column nowrap;
@@ -74,8 +74,10 @@ export interface UploadAdapter<P = Record<string, any>, S = Record<string, any>>
74
74
  notifyPreviewClick: (file: any) => void;
75
75
  notifyDrop: (e: any, files: Array<File>, fileList: Array<BaseFileItem>) => void;
76
76
  notifyAcceptInvalid: (invalidFiles: Array<File>) => void;
77
- registerPastingHandler: (cb?: (params?: any) => void) => void;
77
+ registerPastingHandler: (cb?: (params?: KeyboardEvent | ClipboardEvent) => void) => void;
78
78
  unRegisterPastingHandler: () => void;
79
+ registerPasteEventHandler: (cb?: (params?: ClipboardEvent) => void) => void;
80
+ unRegisterPasteEventHandler: () => void;
79
81
  isMac: () => boolean;
80
82
  notifyPastingError: (error: Error | PermissionStatus) => void;
81
83
  }
@@ -87,6 +89,11 @@ declare class UploadFoundation<P = Record<string, any>, S = Record<string, any>>
87
89
  * when _createURL is called multiple times in a sync loop.
88
90
  */
89
91
  _localUrls: Record<string, string>;
92
+ /**
93
+ * Flag to prevent duplicate handling of paste events.
94
+ * When paste event is successfully handled, we ignore the subsequent keydown event.
95
+ */
96
+ _pasteHandled: boolean;
90
97
  constructor(adapter: UploadAdapter<P, S>);
91
98
  init(): void;
92
99
  /**
@@ -57,6 +57,11 @@ class UploadFoundation extends _foundation.default {
57
57
  * when _createURL is called multiple times in a sync loop.
58
58
  */
59
59
  this._localUrls = {};
60
+ /**
61
+ * Flag to prevent duplicate handling of paste events.
62
+ * When paste event is successfully handled, we ignore the subsequent keydown event.
63
+ */
64
+ this._pasteHandled = false;
60
65
  }
61
66
  init() {
62
67
  // make sure state reset, otherwise may cause upload abort in React StrictMode, like https://github.com/DouyinFE/semi-design/pull/843
@@ -1081,40 +1086,83 @@ class UploadFoundation extends _foundation.default {
1081
1086
  }
1082
1087
  }
1083
1088
  handlePasting(e) {
1084
- const isMac = this._adapter.isMac();
1085
- const isCombineKeydown = isMac ? e.metaKey : e.ctrlKey;
1086
1089
  const {
1087
1090
  addOnPasting
1088
1091
  } = this.getProps();
1089
- if (addOnPasting) {
1090
- if (isCombineKeydown && e.code === 'KeyV') {
1091
- // https://github.com/microsoft/TypeScript/issues/33923
1092
- const permissionName = 'clipboard-read';
1093
- // The main thread should not be blocked by clipboard, so callback writing is required here. No await here
1094
- navigator.permissions.query({
1095
- name: permissionName
1096
- }).then(result => {
1097
- if (result.state === 'granted' || result.state === 'prompt') {
1098
- // user has authorized or will authorize
1099
- navigator.clipboard.read().then(clipboardItems => {
1100
- // Process the data read from the pasteboard
1101
- // Check the returned data type to determine if it is image data, and process accordingly
1102
- this.readFileFromClipboard(clipboardItems);
1103
- });
1104
- } else {
1105
- this._adapter.notifyPastingError(result);
1092
+ if (!addOnPasting) {
1093
+ return;
1094
+ }
1095
+ // Try to read from native paste event (clipboardData) first as fallback
1096
+ if (e.type === 'paste' && e.clipboardData && e.clipboardData.items) {
1097
+ const items = e.clipboardData.items;
1098
+ const files = [];
1099
+ for (let i = 0; i < items.length; i++) {
1100
+ const item = items[i];
1101
+ // Check if the item is a file (image, etc.)
1102
+ if (item.kind === 'file') {
1103
+ const file = item.getAsFile();
1104
+ if (file) {
1105
+ files.push(file);
1106
1106
  }
1107
- }).catch(error => {
1108
- this._adapter.notifyPastingError(error);
1109
- });
1107
+ }
1108
+ }
1109
+ if (files.length > 0) {
1110
+ e.preventDefault();
1111
+ this.handleChange(files);
1112
+ // Mark that paste event has been handled to prevent duplicate handling by keydown event
1113
+ this._pasteHandled = true;
1114
+ // Reset the flag after a short delay to allow future paste operations
1115
+ setTimeout(() => {
1116
+ this._pasteHandled = false;
1117
+ }, 100);
1118
+ return;
1110
1119
  }
1111
1120
  }
1121
+ // Fallback to navigator.clipboard for keyboard events (keydown with Ctrl/Cmd+V)
1122
+ // Skip if paste event has already been handled
1123
+ if (this._pasteHandled) {
1124
+ this._pasteHandled = false;
1125
+ return;
1126
+ }
1127
+ const isMac = this._adapter.isMac();
1128
+ const isCombineKeydown = isMac ? e.metaKey : e.ctrlKey;
1129
+ if (isCombineKeydown && e.code === 'KeyV') {
1130
+ // Check if navigator.clipboard is available
1131
+ if (!navigator.clipboard || typeof navigator.clipboard.read !== 'function') {
1132
+ return;
1133
+ }
1134
+ // https://github.com/microsoft/TypeScript/issues/33923
1135
+ const permissionName = 'clipboard-read';
1136
+ // The main thread should not be blocked by clipboard, so callback writing is required here. No await here
1137
+ navigator.permissions.query({
1138
+ name: permissionName
1139
+ }).then(result => {
1140
+ if (result.state === 'granted' || result.state === 'prompt') {
1141
+ // user has authorized or will authorize
1142
+ navigator.clipboard.read().then(clipboardItems => {
1143
+ // Process the data read from the pasteboard
1144
+ // Check the returned data type to determine if it is image data, and process accordingly
1145
+ this.readFileFromClipboard(clipboardItems);
1146
+ }).catch(error => {
1147
+ this._adapter.notifyPastingError(error);
1148
+ });
1149
+ } else {
1150
+ this._adapter.notifyPastingError(result);
1151
+ }
1152
+ }).catch(error => {
1153
+ this._adapter.notifyPastingError(error);
1154
+ });
1155
+ }
1112
1156
  }
1113
1157
  bindPastingHandler() {
1158
+ // Register keyboard event handler (keydown with Ctrl/Cmd+V)
1114
1159
  this._adapter.registerPastingHandler(event => this.handlePasting(event));
1160
+ // Register native paste event handler as fallback
1161
+ this._adapter.registerPasteEventHandler(event => this.handlePasting(event));
1115
1162
  }
1116
1163
  unbindPastingHandler() {
1117
1164
  this._adapter.unRegisterPastingHandler();
1165
+ this._adapter.unRegisterPasteEventHandler();
1118
1166
  }
1119
1167
  }
1120
1168
  var _default = exports.default = UploadFoundation;
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Escape HTML angle brackets in markdown text, preserving code blocks and inline code.
3
+ *
4
+ * In `format='md'` mode, @mdx-js/mdx uses `rehypeRemoveRaw` which strips all raw HTML nodes.
5
+ * This causes user-typed HTML-like content (e.g. `<AgentChat />`) to silently disappear.
6
+ * By escaping `<` to `&lt;` outside of code spans/blocks, the markdown parser treats them
7
+ * as literal text instead of HTML tags.
8
+ */
9
+ export declare function escapeHtmlInMarkdown(text: string): string;
@@ -0,0 +1,90 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.escapeHtmlInMarkdown = escapeHtmlInMarkdown;
7
+ /**
8
+ * Escape HTML angle brackets in markdown text, preserving code blocks and inline code.
9
+ *
10
+ * In `format='md'` mode, @mdx-js/mdx uses `rehypeRemoveRaw` which strips all raw HTML nodes.
11
+ * This causes user-typed HTML-like content (e.g. `<AgentChat />`) to silently disappear.
12
+ * By escaping `<` to `&lt;` outside of code spans/blocks, the markdown parser treats them
13
+ * as literal text instead of HTML tags.
14
+ */
15
+ function escapeHtmlInMarkdown(text) {
16
+ const lines = text.split('\n');
17
+ const result = [];
18
+ let fenceChar = null;
19
+ let fenceLen = 0;
20
+ for (let i = 0; i < lines.length; i++) {
21
+ const line = lines[i];
22
+ if (fenceChar !== null) {
23
+ // Inside a fenced code block — check for closing fence
24
+ result.push(line);
25
+ const trimmed = line.trimEnd();
26
+ if (trimmed.length >= fenceLen && trimmed[0] === fenceChar && trimmed === fenceChar.repeat(trimmed.length)) {
27
+ fenceChar = null;
28
+ }
29
+ } else {
30
+ // Check if this line opens a fenced code block
31
+ const fenceMatch = line.match(/^(`{3,}|~{3,})/);
32
+ if (fenceMatch) {
33
+ fenceChar = fenceMatch[1][0];
34
+ fenceLen = fenceMatch[1].length;
35
+ result.push(line);
36
+ } else {
37
+ result.push(escapeAngleBracketsOutsideInlineCode(line));
38
+ }
39
+ }
40
+ }
41
+ return result.join('\n');
42
+ }
43
+ /**
44
+ * Escape `<` to `&lt;` in a single line, but preserve content inside inline code spans.
45
+ */
46
+ function escapeAngleBracketsOutsideInlineCode(line) {
47
+ const parts = [];
48
+ let i = 0;
49
+ while (i < line.length) {
50
+ if (line[i] === '`') {
51
+ // Count opening backticks
52
+ let count = 0;
53
+ const start = i;
54
+ while (i < line.length && line[i] === '`') {
55
+ count++;
56
+ i++;
57
+ }
58
+ // Find matching closing backticks (exact same count)
59
+ const closer = '`'.repeat(count);
60
+ const closeIdx = line.indexOf(closer, i);
61
+ if (closeIdx !== -1) {
62
+ parts.push(line.slice(start, closeIdx + count));
63
+ i = closeIdx + count;
64
+ } else {
65
+ // No matching close — treat backticks as regular text, escape any `<`
66
+ parts.push(line.slice(start, i).replace(/</g, '&lt;'));
67
+ }
68
+ } else if (line[i] === '<') {
69
+ parts.push('&lt;');
70
+ i++;
71
+ } else {
72
+ // Collect a run of non-special characters at once
73
+ const next = line.indexOf('<', i);
74
+ const nextBt = line.indexOf('`', i);
75
+ let end;
76
+ if (next === -1 && nextBt === -1) {
77
+ end = line.length;
78
+ } else if (next === -1) {
79
+ end = nextBt;
80
+ } else if (nextBt === -1) {
81
+ end = next;
82
+ } else {
83
+ end = Math.min(next, nextBt);
84
+ }
85
+ parts.push(line.slice(i, end));
86
+ i = end;
87
+ }
88
+ }
89
+ return parts.join('');
90
+ }
@@ -64,7 +64,9 @@ function cleanup(obj, path) {
64
64
  // lodashRemove(target, (value, index, array) => index > lastIndex);
65
65
  // }
66
66
  // Delete object if its empty
67
- if (Array.isArray(target) && target.every(e => e == null)) {
67
+ // Only delete array if it's not empty AND all elements are null/undefined
68
+ // This prevents empty arrays from being deleted (issue #2834)
69
+ if (Array.isArray(target) && target.length > 0 && target.every(e => e == null)) {
68
70
  (0, _unset2.default)(obj, path);
69
71
  } else if (isEmptyObject(target)) {
70
72
  (0, _unset2.default)(obj, path);
@@ -353,7 +353,9 @@ export default class AIChatInputFoundation extends BaseFoundation {
353
353
  };
354
354
  this.handRichTextArealKeyDown = (view, event) => {
355
355
  var _a;
356
- // console.log('outer key down handle');
356
+ if (view.composing) {
357
+ return false;
358
+ }
357
359
  const {
358
360
  sendHotKey
359
361
  } = this.getProps();
@@ -13,6 +13,7 @@ export interface DataItem {
13
13
  export interface StateOptionItem extends DataItem {
14
14
  show?: boolean;
15
15
  key?: string | number;
16
+ _renderedLabel?: any;
16
17
  }
17
18
  export type AutoCompleteData = Array<DataItem | string>;
18
19
  export interface AutoCompleteAdapter<P = Record<string, any>, S = Record<string, any>> extends KeyboardAdapter<P, S> {
@@ -112,7 +112,7 @@ class AutoCompleteFoundation extends BaseFoundation {
112
112
  }, item);
113
113
  }
114
114
  if (renderItem && typeof renderItem === 'function') {
115
- option.label = renderItem(item);
115
+ option._renderedLabel = renderItem(item);
116
116
  }
117
117
  options.push(option);
118
118
  });