@king-design/vue 3.8.0-beta.1 → 3.8.0-beta.2

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.
@@ -4,6 +4,8 @@ export interface BubbleTyping {
4
4
  interval?: number;
5
5
  step?: number;
6
6
  suffix?: boolean;
7
+ keepPrefix?: boolean;
8
+ resumeFrom?: string | number | 'content';
7
9
  }
8
10
  export interface BubbleRenderSlots {
9
11
  avatar?: Function;
@@ -772,8 +772,8 @@ describe('Bubble', function () {
772
772
  }
773
773
  }, _callee19);
774
774
  })));
775
- it('should render typing suffix only when enabled', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee20() {
776
- var Demo, _mount20, element, i, enabledSuffix;
775
+ it('should resume typing from previous rendered content after remount', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee20() {
776
+ var Demo, _mount20, element, text, nextText, i;
777
777
  return _regeneratorRuntime.wrap(function _callee20$(_context40) {
778
778
  while (1) switch (_context40.prev = _context40.next) {
779
779
  case 0:
@@ -791,25 +791,198 @@ describe('Bubble', function () {
791
791
  }
792
792
  return Demo;
793
793
  }(Component);
794
- Demo.template = "\n const { Bubble } = this;\n <div>\n <Bubble\n className=\"suffix-enabled\"\n content=\"hello\"\n typing={{interval: 16, step: 2, suffix: true}}\n streaming={true}\n />\n <Bubble\n className=\"suffix-default\"\n content=\"hello\"\n typing={{interval: 16, step: 2}}\n streaming={true}\n />\n </div>\n ";
794
+ Demo.template = "\n const { Bubble } = this;\n <div>\n <Bubble\n content=\"hello world\"\n streaming={true}\n typing={{interval: 16, step: 2, resumeFrom: 'hello'}}\n />\n </div>\n ";
795
795
  _mount20 = mount(Demo), element = _mount20[1];
796
+ text = element.querySelector('.k-bubble-text').textContent || '';
797
+ expect(text).to.eql('hello');
798
+ nextText = text;
799
+ i = 0;
800
+ case 7:
801
+ if (!(i < 6)) {
802
+ _context40.next = 16;
803
+ break;
804
+ }
805
+ _context40.next = 10;
806
+ return wait(20);
807
+ case 10:
808
+ nextText = element.querySelector('.k-bubble-text').textContent || '';
809
+ if (!(nextText.length > text.length)) {
810
+ _context40.next = 13;
811
+ break;
812
+ }
813
+ return _context40.abrupt("break", 16);
814
+ case 13:
815
+ i++;
816
+ _context40.next = 7;
817
+ break;
818
+ case 16:
819
+ expect(_startsWithInstanceProperty(nextText).call(nextText, 'hello')).to.be.true;
820
+ expect(nextText.length).to.be.greaterThan(text.length);
821
+ case 18:
822
+ case "end":
823
+ return _context40.stop();
824
+ }
825
+ }, _callee20);
826
+ })));
827
+ it('should support content resume mode without replaying existing content', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee21() {
828
+ var Demo, _mount21, instance, element, text;
829
+ return _regeneratorRuntime.wrap(function _callee21$(_context42) {
830
+ while (1) switch (_context42.prev = _context42.next) {
831
+ case 0:
832
+ Demo = /*#__PURE__*/function (_Component21) {
833
+ _inheritsLoose(Demo, _Component21);
834
+ function Demo() {
835
+ var _context41;
836
+ var _this21;
837
+ for (var _len21 = arguments.length, args = new Array(_len21), _key21 = 0; _key21 < _len21; _key21++) {
838
+ args[_key21] = arguments[_key21];
839
+ }
840
+ _this21 = _Component21.call.apply(_Component21, _concatInstanceProperty(_context41 = [this]).call(_context41, args)) || this;
841
+ _this21.Bubble = Bubble;
842
+ return _this21;
843
+ }
844
+ Demo.defaults = function defaults() {
845
+ return {
846
+ content: 'already rendered'
847
+ };
848
+ };
849
+ return Demo;
850
+ }(Component);
851
+ Demo.template = "\n const { Bubble } = this;\n <div>\n <Bubble\n content={this.get('content')}\n streaming={true}\n typing={{interval: 16, step: 2, resumeFrom: 'content'}}\n />\n </div>\n ";
852
+ _mount21 = mount(Demo), instance = _mount21[0], element = _mount21[1];
853
+ expect(element.querySelector('.k-bubble-text').textContent).to.eql('already rendered');
854
+ instance.set('content', 'already rendered and more');
855
+ _context42.next = 7;
856
+ return wait(20);
857
+ case 7:
858
+ text = element.querySelector('.k-bubble-text').textContent || '';
859
+ expect(_startsWithInstanceProperty(text).call(text, 'already rendered')).to.be.true;
860
+ expect(text.length).to.be.lessThan('already rendered and more'.length);
861
+ case 10:
862
+ case "end":
863
+ return _context42.stop();
864
+ }
865
+ }, _callee21);
866
+ })));
867
+ it('should restart typing from empty when keepPrefix is false', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee22() {
868
+ var Demo, _mount22, instance, element, initialText, i, _element$querySelecto, text, _i2, _element$querySelecto2;
869
+ return _regeneratorRuntime.wrap(function _callee22$(_context44) {
870
+ while (1) switch (_context44.prev = _context44.next) {
871
+ case 0:
872
+ Demo = /*#__PURE__*/function (_Component22) {
873
+ _inheritsLoose(Demo, _Component22);
874
+ function Demo() {
875
+ var _context43;
876
+ var _this22;
877
+ for (var _len22 = arguments.length, args = new Array(_len22), _key22 = 0; _key22 < _len22; _key22++) {
878
+ args[_key22] = arguments[_key22];
879
+ }
880
+ _this22 = _Component22.call.apply(_Component22, _concatInstanceProperty(_context43 = [this]).call(_context43, args)) || this;
881
+ _this22.Bubble = Bubble;
882
+ return _this22;
883
+ }
884
+ Demo.defaults = function defaults() {
885
+ return {
886
+ content: 'hello world'
887
+ };
888
+ };
889
+ return Demo;
890
+ }(Component);
891
+ Demo.template = "\n const { Bubble } = this;\n <div>\n <Bubble\n content={this.get('content')}\n streaming={true}\n typing={{interval: 16, step: 2, keepPrefix: false}}\n />\n </div>\n ";
892
+ _mount22 = mount(Demo), instance = _mount22[0], element = _mount22[1];
893
+ initialText = '';
894
+ i = 0;
895
+ case 5:
896
+ if (!(i < 12)) {
897
+ _context44.next = 14;
898
+ break;
899
+ }
900
+ initialText = ((_element$querySelecto = element.querySelector('.k-bubble-text')) == null ? void 0 : _element$querySelecto.textContent) || '';
901
+ if (!_includesInstanceProperty(initialText).call(initialText, 'hello')) {
902
+ _context44.next = 9;
903
+ break;
904
+ }
905
+ return _context44.abrupt("break", 14);
906
+ case 9:
907
+ _context44.next = 11;
908
+ return wait(20);
909
+ case 11:
910
+ i++;
911
+ _context44.next = 5;
912
+ break;
913
+ case 14:
914
+ expect(initialText).to.contain('hello');
915
+ instance.set('content', 'hello there');
916
+ _context44.next = 18;
917
+ return wait();
918
+ case 18:
919
+ text = '';
920
+ _i2 = 0;
921
+ case 20:
922
+ if (!(_i2 < 6)) {
923
+ _context44.next = 29;
924
+ break;
925
+ }
926
+ _context44.next = 23;
927
+ return wait(20);
928
+ case 23:
929
+ text = ((_element$querySelecto2 = element.querySelector('.k-bubble-text')) == null ? void 0 : _element$querySelecto2.textContent) || '';
930
+ if (!text) {
931
+ _context44.next = 26;
932
+ break;
933
+ }
934
+ return _context44.abrupt("break", 29);
935
+ case 26:
936
+ _i2++;
937
+ _context44.next = 20;
938
+ break;
939
+ case 29:
940
+ expect(text).not.to.contain('hello');
941
+ expect(text.length).to.be.lessThan('hello'.length);
942
+ case 31:
943
+ case "end":
944
+ return _context44.stop();
945
+ }
946
+ }, _callee22);
947
+ })));
948
+ it('should render typing suffix only when enabled', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee23() {
949
+ var Demo, _mount23, element, i, enabledSuffix;
950
+ return _regeneratorRuntime.wrap(function _callee23$(_context46) {
951
+ while (1) switch (_context46.prev = _context46.next) {
952
+ case 0:
953
+ Demo = /*#__PURE__*/function (_Component23) {
954
+ _inheritsLoose(Demo, _Component23);
955
+ function Demo() {
956
+ var _context45;
957
+ var _this23;
958
+ for (var _len23 = arguments.length, args = new Array(_len23), _key23 = 0; _key23 < _len23; _key23++) {
959
+ args[_key23] = arguments[_key23];
960
+ }
961
+ _this23 = _Component23.call.apply(_Component23, _concatInstanceProperty(_context45 = [this]).call(_context45, args)) || this;
962
+ _this23.Bubble = Bubble;
963
+ return _this23;
964
+ }
965
+ return Demo;
966
+ }(Component);
967
+ Demo.template = "\n const { Bubble } = this;\n <div>\n <Bubble\n className=\"suffix-enabled\"\n content=\"hello\"\n typing={{interval: 16, step: 2, suffix: true}}\n streaming={true}\n />\n <Bubble\n className=\"suffix-default\"\n content=\"hello\"\n typing={{interval: 16, step: 2}}\n streaming={true}\n />\n </div>\n ";
968
+ _mount23 = mount(Demo), element = _mount23[1];
796
969
  i = 0;
797
970
  case 4:
798
971
  if (!(i < 10)) {
799
- _context40.next = 12;
972
+ _context46.next = 12;
800
973
  break;
801
974
  }
802
975
  if (!element.querySelector('.suffix-enabled .k-bubble-typing-suffix')) {
803
- _context40.next = 7;
976
+ _context46.next = 7;
804
977
  break;
805
978
  }
806
- return _context40.abrupt("break", 12);
979
+ return _context46.abrupt("break", 12);
807
980
  case 7:
808
- _context40.next = 9;
981
+ _context46.next = 9;
809
982
  return wait(20);
810
983
  case 9:
811
984
  i++;
812
- _context40.next = 4;
985
+ _context46.next = 4;
813
986
  break;
814
987
  case 12:
815
988
  enabledSuffix = element.querySelector('.suffix-enabled .k-bubble-typing-suffix');
@@ -817,8 +990,8 @@ describe('Bubble', function () {
817
990
  expect(element.querySelector('.suffix-default .k-bubble-typing-suffix')).to.eql(null);
818
991
  case 15:
819
992
  case "end":
820
- return _context40.stop();
993
+ return _context46.stop();
821
994
  }
822
- }, _callee20);
995
+ }, _callee23);
823
996
  })));
824
997
  });
@@ -10,6 +10,7 @@ export function useBubbleDisplay() {
10
10
  var previousLoading = !!instance.get('loading');
11
11
  var previousStreaming = !!instance.get('streaming');
12
12
  var hasPendingCompletion = previousLoading || previousStreaming;
13
+ var typingTargetKey = '';
13
14
  function setDisplayContent(value) {
14
15
  if (instance.get('$displayContent') === value) return false;
15
16
  instance.set('$displayContent', value);
@@ -24,12 +25,16 @@ export function useBubbleDisplay() {
24
25
  if (typing && typeof typing === 'object') {
25
26
  return {
26
27
  interval: Math.max(typing.interval || 24, 16),
27
- step: Math.max(typing.step || 2, 1)
28
+ step: Math.max(typing.step || 2, 1),
29
+ keepPrefix: typing.keepPrefix !== false,
30
+ resumeFrom: typing.resumeFrom
28
31
  };
29
32
  }
30
33
  return {
31
34
  interval: 24,
32
- step: 2
35
+ step: 2,
36
+ keepPrefix: true,
37
+ resumeFrom: undefined
33
38
  };
34
39
  }
35
40
  function stopTyping() {
@@ -71,18 +76,49 @@ export function useBubbleDisplay() {
71
76
  function getDisplayedContent() {
72
77
  return instance.get('$displayContent') || '';
73
78
  }
79
+ function getSafeContentPrefix(content, prefix) {
80
+ if (!prefix) return '';
81
+ if (_sliceInstanceProperty(content).call(content, 0, prefix.length) === prefix) return prefix;
82
+ var i = 0;
83
+ while (i < prefix.length && i < content.length && prefix[i] === content[i]) {
84
+ i++;
85
+ }
86
+ return _sliceInstanceProperty(prefix).call(prefix, 0, i);
87
+ }
88
+ function getResumePrefix(content) {
89
+ var _getTypingOptions = getTypingOptions(),
90
+ resumeFrom = _getTypingOptions.resumeFrom;
91
+ if (resumeFrom === undefined || resumeFrom === null) return '';
92
+ if (resumeFrom === 'content') return content;
93
+ if (typeof resumeFrom === 'number') return _sliceInstanceProperty(content).call(content, 0, Math.max(0, resumeFrom));
94
+ return getSafeContentPrefix(content, String(resumeFrom));
95
+ }
96
+ function getTypingTargetKey(content) {
97
+ var _getTypingOptions2 = getTypingOptions(),
98
+ keepPrefix = _getTypingOptions2.keepPrefix,
99
+ resumeFrom = _getTypingOptions2.resumeFrom;
100
+ return (keepPrefix ? '1' : '0') + "\0" + String(resumeFrom) + "\0" + content;
101
+ }
102
+ function syncTypingStart(content) {
103
+ var targetKey = getTypingTargetKey(content);
104
+ if (targetKey === typingTargetKey) return;
105
+ typingTargetKey = targetKey;
106
+ var sharedPrefix = getSharedPrefix(content);
107
+ if (sharedPrefix !== getDisplayedContent()) {
108
+ resetTypingComplete();
109
+ setDisplayContent(sharedPrefix);
110
+ }
111
+ }
74
112
  // 获取共享前缀
75
113
  function getSharedPrefix(content) {
76
114
  var displayedContent = getDisplayedContent();
115
+ if (!displayedContent) return getResumePrefix(content);
116
+ if (!getTypingOptions().keepPrefix) return '';
77
117
  // 避免 startsWith 依赖,兼容旧运行环境。
78
118
  if (!displayedContent || _sliceInstanceProperty(content).call(content, 0, displayedContent.length) === displayedContent) {
79
119
  return displayedContent;
80
120
  }
81
- var i = 0;
82
- while (i < displayedContent.length && i < content.length && displayedContent[i] === content[i]) {
83
- i++;
84
- }
85
- return _sliceInstanceProperty(displayedContent).call(displayedContent, 0, i);
121
+ return getSafeContentPrefix(content, displayedContent);
86
122
  }
87
123
  // 开始打字动画
88
124
  function startTyping(immediate) {
@@ -95,8 +131,8 @@ export function useBubbleDisplay() {
95
131
  runTyping();
96
132
  return;
97
133
  }
98
- var _getTypingOptions = getTypingOptions(),
99
- interval = _getTypingOptions.interval;
134
+ var _getTypingOptions3 = getTypingOptions(),
135
+ interval = _getTypingOptions3.interval;
100
136
  setTypingActive(true);
101
137
  typingTimer = window.setTimeout(function () {
102
138
  typingTimer = null;
@@ -111,26 +147,24 @@ export function useBubbleDisplay() {
111
147
  if (instance.get('loading')) {
112
148
  stopTyping();
113
149
  resetTypingComplete();
150
+ typingTargetKey = '';
114
151
  setDisplayContent('');
115
152
  return;
116
153
  }
117
154
  if (!instance.get('typing') || !nextValue) {
118
155
  stopTyping();
156
+ typingTargetKey = '';
119
157
  setDisplayContent(nextValue);
120
158
  if (!instance.get('streaming') && shouldEmitTypingComplete(nextValue)) {
121
159
  triggerTypingComplete(nextValue);
122
160
  }
123
161
  return;
124
162
  }
125
- var sharedPrefix = getSharedPrefix(nextValue);
126
- if (sharedPrefix !== getDisplayedContent()) {
127
- resetTypingComplete();
128
- setDisplayContent(sharedPrefix);
129
- }
163
+ syncTypingStart(nextValue);
130
164
  var currentValue = getDisplayedContent();
131
- var _getTypingOptions2 = getTypingOptions(),
132
- interval = _getTypingOptions2.interval,
133
- step = _getTypingOptions2.step;
165
+ var _getTypingOptions4 = getTypingOptions(),
166
+ interval = _getTypingOptions4.interval,
167
+ step = _getTypingOptions4.step;
134
168
  if (currentValue !== nextValue) {
135
169
  var nextLength = Math.min(currentValue.length + step, nextValue.length);
136
170
  var renderedContent = _sliceInstanceProperty(nextValue).call(nextValue, 0, nextLength);
@@ -157,6 +191,7 @@ export function useBubbleDisplay() {
157
191
  if (instance.get('loading')) {
158
192
  stopTyping();
159
193
  resetTypingComplete();
194
+ typingTargetKey = '';
160
195
  setDisplayContent('');
161
196
  return;
162
197
  }
@@ -166,6 +201,7 @@ export function useBubbleDisplay() {
166
201
  var streaming = instance.get('streaming');
167
202
  if (!nextValue) {
168
203
  stopTyping();
204
+ typingTargetKey = '';
169
205
  setDisplayContent(nextValue);
170
206
  if (!streaming && shouldEmitTypingComplete(nextValue)) {
171
207
  triggerTypingComplete(nextValue);
@@ -174,18 +210,18 @@ export function useBubbleDisplay() {
174
210
  }
175
211
  if (!typing) {
176
212
  stopTyping();
213
+ typingTargetKey = '';
177
214
  setDisplayContent(nextValue);
178
215
  if (!streaming && shouldEmitTypingComplete(nextValue)) {
179
216
  triggerTypingComplete(nextValue);
180
217
  }
181
218
  return;
182
219
  }
183
- var sharedPrefix = getSharedPrefix(nextValue);
184
- if (sharedPrefix !== getDisplayedContent()) {
220
+ var previousDisplayContent = getDisplayedContent();
221
+ syncTypingStart(nextValue);
222
+ if (getDisplayedContent() !== previousDisplayContent) {
185
223
  // 内容被修订时保留共享前缀,避免流式修正时整段闪烁。
186
224
  stopTyping();
187
- resetTypingComplete();
188
- setDisplayContent(sharedPrefix);
189
225
  }
190
226
  if (getDisplayedContent() === nextValue) {
191
227
  if (!streaming) {
@@ -200,8 +236,10 @@ export function useBubbleDisplay() {
200
236
  // 初始化状态和绑定监听器
201
237
  function bootstrap() {
202
238
  var content = instance.get('content');
239
+ var nextValue = content === undefined || content === null ? '' : String(content);
203
240
  setTypingActive(false);
204
- setDisplayContent(!instance.get('loading') && !instance.get('typing') && content !== undefined && content !== null ? String(content) : '');
241
+ var initialDisplayContent = !instance.get('loading') && nextValue ? instance.get('typing') ? getResumePrefix(nextValue) : nextValue : '';
242
+ setDisplayContent(initialDisplayContent);
205
243
  instance.watch('content', function () {
206
244
  return syncDisplayContent();
207
245
  }, {
@@ -7,6 +7,7 @@ import _mapInstanceProperty from "@babel/runtime-corejs3/core-js/instance/map";
7
7
  import _Array$from from "@babel/runtime-corejs3/core-js/array/from";
8
8
  import _sliceInstanceProperty from "@babel/runtime-corejs3/core-js/instance/slice";
9
9
  import _atInstanceProperty from "@babel/runtime-corejs3/core-js/instance/at";
10
+ import _startsWithInstanceProperty from "@babel/runtime-corejs3/core-js/instance/starts-with";
10
11
  import { Component } from 'intact-vue-next';
11
12
  import { dispatchEvent, mount, unmount, wait } from '../../test/utils';
12
13
  import { BubbleList } from '.';
@@ -1329,4 +1330,80 @@ describe('BubbleList', function () {
1329
1330
  }
1330
1331
  }, _callee23);
1331
1332
  })));
1333
+ it('should pass typing resume options to rendered bubbles', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee24() {
1334
+ var Demo, _mount23, element, text, nextText, i;
1335
+ return _regeneratorRuntime.wrap(function _callee24$(_context70) {
1336
+ while (1) switch (_context70.prev = _context70.next) {
1337
+ case 0:
1338
+ Demo = /*#__PURE__*/function (_Component24) {
1339
+ _inheritsLoose(Demo, _Component24);
1340
+ function Demo() {
1341
+ var _context69;
1342
+ var _this24;
1343
+ for (var _len24 = arguments.length, args = new Array(_len24), _key24 = 0; _key24 < _len24; _key24++) {
1344
+ args[_key24] = arguments[_key24];
1345
+ }
1346
+ _this24 = _Component24.call.apply(_Component24, _concatInstanceProperty(_context69 = [this]).call(_context69, args)) || this;
1347
+ _this24.BubbleList = BubbleList;
1348
+ return _this24;
1349
+ }
1350
+ Demo.defaults = function defaults() {
1351
+ return {
1352
+ roles: {
1353
+ ai: {
1354
+ placement: 'start',
1355
+ typing: {
1356
+ interval: 16,
1357
+ step: 1
1358
+ }
1359
+ }
1360
+ },
1361
+ items: [{
1362
+ key: 1,
1363
+ role: 'ai',
1364
+ content: 'hello world',
1365
+ streaming: true,
1366
+ typing: {
1367
+ interval: 16,
1368
+ step: 2,
1369
+ resumeFrom: 'hello'
1370
+ }
1371
+ }]
1372
+ };
1373
+ };
1374
+ return Demo;
1375
+ }(Component);
1376
+ Demo.template = "\n const { BubbleList } = this;\n <BubbleList\n items={this.get('items')}\n roles={this.get('roles')}\n />\n ";
1377
+ _mount23 = mount(Demo), element = _mount23[1];
1378
+ text = element.querySelector('.k-bubble-text').textContent || '';
1379
+ expect(text).to.eql('hello');
1380
+ nextText = text;
1381
+ i = 0;
1382
+ case 7:
1383
+ if (!(i < 6)) {
1384
+ _context70.next = 16;
1385
+ break;
1386
+ }
1387
+ _context70.next = 10;
1388
+ return wait(20);
1389
+ case 10:
1390
+ nextText = element.querySelector('.k-bubble-text').textContent || '';
1391
+ if (!(nextText.length > text.length)) {
1392
+ _context70.next = 13;
1393
+ break;
1394
+ }
1395
+ return _context70.abrupt("break", 16);
1396
+ case 13:
1397
+ i++;
1398
+ _context70.next = 7;
1399
+ break;
1400
+ case 16:
1401
+ expect(_startsWithInstanceProperty(nextText).call(nextText, 'hello')).to.be.true;
1402
+ expect(nextText.length).to.be.greaterThan(text.length);
1403
+ case 18:
1404
+ case "end":
1405
+ return _context70.stop();
1406
+ }
1407
+ }, _callee24);
1408
+ })));
1332
1409
  });