@miso.ai/client-sdk-ui 1.13.2-beta.1 → 1.14.0-beta.3

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 (226) hide show
  1. package/dist/cjs/asset/svgs.js +17 -4
  2. package/dist/cjs/combo/ask/index.js +2 -2
  3. package/dist/cjs/constants.js +120 -98
  4. package/dist/cjs/defaults/ask/controls.js +1 -1
  5. package/dist/cjs/defaults/hybrid-search/answer-box.js +4 -4
  6. package/dist/cjs/defaults.js +259 -0
  7. package/dist/cjs/element/container/index.js +28 -0
  8. package/dist/cjs/element/container/miso-ask.js +3 -3
  9. package/dist/cjs/element/container/miso-conversation.js +21 -0
  10. package/dist/cjs/element/container/miso-explore.js +1 -1
  11. package/dist/cjs/element/container/miso-history.js +21 -0
  12. package/dist/cjs/element/container/miso-hybrid-search.js +1 -1
  13. package/dist/cjs/element/container/miso-message-item.js +91 -0
  14. package/dist/cjs/element/container/miso-recommendation.js +1 -1
  15. package/dist/cjs/element/container/miso-search.js +1 -1
  16. package/dist/cjs/element/container/miso-thread-item.js +91 -0
  17. package/dist/cjs/element/context/miso-follow-ups.js +1 -1
  18. package/dist/cjs/element/role/index.js +49 -0
  19. package/dist/cjs/element/role/miso-delete.js +20 -0
  20. package/dist/cjs/element/role/miso-messages.js +20 -0
  21. package/dist/cjs/element/role/miso-new-thread.js +20 -0
  22. package/dist/cjs/element/role/miso-rename.js +20 -0
  23. package/dist/cjs/element/role/miso-subscription.js +20 -0
  24. package/dist/cjs/element/role/miso-threads.js +20 -0
  25. package/dist/cjs/element/role/miso-title.js +20 -0
  26. package/dist/cjs/layout/affiliation/affiliation.js +1 -1
  27. package/dist/cjs/layout/banner/banner.js +2 -1
  28. package/dist/cjs/layout/button/button.js +170 -0
  29. package/dist/cjs/layout/button/checkbox.js +116 -0
  30. package/dist/cjs/layout/button/more-button.js +3 -3
  31. package/dist/cjs/layout/container/container.js +1 -1
  32. package/dist/cjs/layout/container/item-container.js +99 -0
  33. package/dist/cjs/layout/error/error.js +2 -1
  34. package/dist/cjs/layout/index.js +42 -0
  35. package/dist/cjs/layout/input/facets.js +4 -3
  36. package/dist/cjs/layout/input/option-list.js +3 -2
  37. package/dist/cjs/layout/input/search-box.js +50 -12
  38. package/dist/cjs/layout/input/select.js +2 -1
  39. package/dist/cjs/layout/list/cards.js +2 -1
  40. package/dist/cjs/layout/list/carousel.js +2 -1
  41. package/dist/cjs/layout/list/collection.js +13 -1
  42. package/dist/cjs/layout/list/gallery.js +2 -1
  43. package/dist/cjs/layout/list/horizontal.js +2 -1
  44. package/dist/cjs/layout/list/list.js +2 -1
  45. package/dist/cjs/layout/list/messages.js +359 -0
  46. package/dist/cjs/layout/list/threads.js +199 -0
  47. package/dist/cjs/layout/mixin/trackable.js +4 -5
  48. package/dist/cjs/layout/mixin/triggerable.js +2 -2
  49. package/dist/cjs/layout/radio/feedback.js +4 -3
  50. package/dist/cjs/layout/templates.js +97 -0
  51. package/dist/cjs/layout/text/question.js +71 -0
  52. package/dist/cjs/layout/text/text.js +2 -1
  53. package/dist/cjs/layout/text/typewriter/index.js +18 -23
  54. package/dist/cjs/plugin.js +43 -104
  55. package/dist/cjs/util/confirm.js +33 -0
  56. package/dist/cjs/util/dom.js +21 -0
  57. package/dist/cjs/util/modal.js +48 -0
  58. package/dist/cjs/util/prompt.js +47 -0
  59. package/dist/cjs/version.js +1 -1
  60. package/package.json +5 -4
  61. package/src/CLAUDE.md +51 -104
  62. package/src/asset/svgs.js +16 -3
  63. package/src/combo/ask/index.js +2 -2
  64. package/src/constants.js +39 -147
  65. package/src/defaults/ask/controls.js +1 -1
  66. package/src/defaults/hybrid-search/answer-box.js +2 -2
  67. package/src/defaults.js +174 -0
  68. package/src/element/container/index.js +4 -0
  69. package/src/element/container/miso-ask.js +3 -3
  70. package/src/element/container/miso-conversation.js +19 -0
  71. package/src/element/container/miso-explore.js +1 -1
  72. package/src/element/container/miso-history.js +19 -0
  73. package/src/element/container/miso-hybrid-search.js +1 -1
  74. package/src/element/container/miso-message-item.js +94 -0
  75. package/src/element/container/miso-recommendation.js +1 -1
  76. package/src/element/container/miso-search.js +1 -1
  77. package/src/element/container/miso-thread-item.js +94 -0
  78. package/src/element/context/miso-follow-ups.js +1 -1
  79. package/src/element/role/index.js +7 -0
  80. package/src/element/role/miso-delete.js +16 -0
  81. package/src/element/role/miso-messages.js +16 -0
  82. package/src/element/role/miso-new-thread.js +16 -0
  83. package/src/element/role/miso-rename.js +16 -0
  84. package/src/element/role/miso-subscription.js +16 -0
  85. package/src/element/role/miso-threads.js +16 -0
  86. package/src/element/role/miso-title.js +16 -0
  87. package/src/layout/affiliation/affiliation.js +2 -2
  88. package/src/layout/banner/banner.js +2 -1
  89. package/src/layout/button/button.js +152 -0
  90. package/src/layout/button/checkbox.js +92 -0
  91. package/src/layout/button/more-button.js +3 -3
  92. package/src/layout/container/container.js +2 -2
  93. package/src/layout/container/item-container.js +99 -0
  94. package/src/layout/error/error.js +2 -1
  95. package/src/layout/index.js +6 -0
  96. package/src/layout/input/facets.js +3 -2
  97. package/src/layout/input/option-list.js +3 -2
  98. package/src/layout/input/search-box.js +45 -8
  99. package/src/layout/input/select.js +2 -1
  100. package/src/layout/list/cards.js +2 -1
  101. package/src/layout/list/carousel.js +2 -1
  102. package/src/layout/list/collection.js +15 -2
  103. package/src/layout/list/gallery.js +2 -1
  104. package/src/layout/list/horizontal.js +2 -1
  105. package/src/layout/list/list.js +2 -1
  106. package/src/layout/list/messages.js +316 -0
  107. package/src/layout/list/threads.js +166 -0
  108. package/src/layout/mixin/trackable.js +2 -3
  109. package/src/layout/mixin/triggerable.js +1 -1
  110. package/src/layout/radio/feedback.js +3 -2
  111. package/src/layout/templates.js +97 -0
  112. package/src/layout/text/question.js +58 -0
  113. package/src/layout/text/text.js +2 -1
  114. package/src/layout/text/typewriter/index.js +15 -18
  115. package/src/plugin.js +31 -117
  116. package/src/util/confirm.js +28 -0
  117. package/src/util/dom.js +16 -0
  118. package/src/util/modal.js +49 -0
  119. package/src/util/prompt.js +42 -0
  120. package/src/version.js +1 -1
  121. package/dist/cjs/actor/data.js +0 -132
  122. package/dist/cjs/actor/feedback.js +0 -29
  123. package/dist/cjs/actor/fields.js +0 -57
  124. package/dist/cjs/actor/filters.js +0 -161
  125. package/dist/cjs/actor/hub.js +0 -57
  126. package/dist/cjs/actor/index.js +0 -73
  127. package/dist/cjs/actor/interactions.js +0 -36
  128. package/dist/cjs/actor/sessions.js +0 -25
  129. package/dist/cjs/actor/tracker-states.js +0 -61
  130. package/dist/cjs/actor/tracker.js +0 -109
  131. package/dist/cjs/actor/trackers.js +0 -52
  132. package/dist/cjs/actor/utils.js +0 -15
  133. package/dist/cjs/actor/view.js +0 -294
  134. package/dist/cjs/actor/views.js +0 -209
  135. package/dist/cjs/source.js +0 -27
  136. package/dist/cjs/util/proxy.js +0 -88
  137. package/dist/cjs/util/trackers.js +0 -88
  138. package/dist/cjs/workflow/affiliations.js +0 -140
  139. package/dist/cjs/workflow/answer-based.js +0 -337
  140. package/dist/cjs/workflow/ask.js +0 -319
  141. package/dist/cjs/workflow/asks.js +0 -150
  142. package/dist/cjs/workflow/autocompletable.js +0 -63
  143. package/dist/cjs/workflow/autocomplete.js +0 -120
  144. package/dist/cjs/workflow/base.js +0 -418
  145. package/dist/cjs/workflow/context.js +0 -19
  146. package/dist/cjs/workflow/explore.js +0 -210
  147. package/dist/cjs/workflow/explores.js +0 -21
  148. package/dist/cjs/workflow/hybrid-search-answer.js +0 -88
  149. package/dist/cjs/workflow/hybrid-search-views.js +0 -109
  150. package/dist/cjs/workflow/hybrid-search.js +0 -213
  151. package/dist/cjs/workflow/index.js +0 -41
  152. package/dist/cjs/workflow/options/api.js +0 -41
  153. package/dist/cjs/workflow/options/auto-query.js +0 -97
  154. package/dist/cjs/workflow/options/configurable.js +0 -45
  155. package/dist/cjs/workflow/options/data-processor.js +0 -20
  156. package/dist/cjs/workflow/options/filters.js +0 -15
  157. package/dist/cjs/workflow/options/index.js +0 -126
  158. package/dist/cjs/workflow/options/interactions.js +0 -41
  159. package/dist/cjs/workflow/options/layouts.js +0 -79
  160. package/dist/cjs/workflow/options/options.js +0 -156
  161. package/dist/cjs/workflow/options/pagination.js +0 -25
  162. package/dist/cjs/workflow/options/roles.js +0 -15
  163. package/dist/cjs/workflow/options/templates.js +0 -30
  164. package/dist/cjs/workflow/options/trackers.js +0 -83
  165. package/dist/cjs/workflow/options/utils.js +0 -33
  166. package/dist/cjs/workflow/processors.js +0 -607
  167. package/dist/cjs/workflow/recommendation.js +0 -57
  168. package/dist/cjs/workflow/recommendations.js +0 -18
  169. package/dist/cjs/workflow/search-based.js +0 -337
  170. package/dist/cjs/workflow/search.js +0 -64
  171. package/dist/cjs/workflow/unit.js +0 -57
  172. package/dist/cjs/workflow/units.js +0 -42
  173. package/dist/cjs/workflow/use-link.js +0 -49
  174. package/src/actor/data.js +0 -106
  175. package/src/actor/feedback.js +0 -28
  176. package/src/actor/fields.js +0 -51
  177. package/src/actor/filters.js +0 -149
  178. package/src/actor/hub.js +0 -47
  179. package/src/actor/index.js +0 -10
  180. package/src/actor/interactions.js +0 -32
  181. package/src/actor/sessions.js +0 -21
  182. package/src/actor/tracker-states.js +0 -57
  183. package/src/actor/tracker.js +0 -104
  184. package/src/actor/trackers.js +0 -45
  185. package/src/actor/utils.js +0 -7
  186. package/src/actor/view.js +0 -272
  187. package/src/actor/views.js +0 -224
  188. package/src/source.js +0 -17
  189. package/src/util/proxy.js +0 -81
  190. package/src/util/trackers.js +0 -84
  191. package/src/workflow/affiliations.js +0 -121
  192. package/src/workflow/answer-based.js +0 -312
  193. package/src/workflow/ask.js +0 -284
  194. package/src/workflow/asks.js +0 -143
  195. package/src/workflow/autocompletable.js +0 -67
  196. package/src/workflow/autocomplete.js +0 -114
  197. package/src/workflow/base.js +0 -396
  198. package/src/workflow/context.js +0 -16
  199. package/src/workflow/explore.js +0 -194
  200. package/src/workflow/explores.js +0 -18
  201. package/src/workflow/hybrid-search-answer.js +0 -73
  202. package/src/workflow/hybrid-search-views.js +0 -116
  203. package/src/workflow/hybrid-search.js +0 -230
  204. package/src/workflow/index.js +0 -5
  205. package/src/workflow/options/api.js +0 -31
  206. package/src/workflow/options/auto-query.js +0 -70
  207. package/src/workflow/options/configurable.js +0 -48
  208. package/src/workflow/options/data-processor.js +0 -15
  209. package/src/workflow/options/filters.js +0 -10
  210. package/src/workflow/options/index.js +0 -11
  211. package/src/workflow/options/interactions.js +0 -36
  212. package/src/workflow/options/layouts.js +0 -73
  213. package/src/workflow/options/options.js +0 -175
  214. package/src/workflow/options/pagination.js +0 -18
  215. package/src/workflow/options/roles.js +0 -12
  216. package/src/workflow/options/templates.js +0 -27
  217. package/src/workflow/options/trackers.js +0 -80
  218. package/src/workflow/options/utils.js +0 -26
  219. package/src/workflow/processors.js +0 -490
  220. package/src/workflow/recommendation.js +0 -63
  221. package/src/workflow/recommendations.js +0 -14
  222. package/src/workflow/search-based.js +0 -329
  223. package/src/workflow/search.js +0 -73
  224. package/src/workflow/unit.js +0 -51
  225. package/src/workflow/units.js +0 -43
  226. package/src/workflow/use-link.js +0 -41
@@ -6,7 +6,7 @@ Object.defineProperty(exports, "__esModule", {
6
6
  exports.default = void 0;
7
7
  var _commons = require("@miso.ai/commons");
8
8
  var _constants = require("../../constants.js");
9
- var _index = require("../../actor/index.js");
9
+ var _clientSdkWorkflow = require("@miso.ai/client-sdk-workflow");
10
10
  var _template = _interopRequireDefault(require("../template.js"));
11
11
  var _templates = require("../templates.js");
12
12
  var _svgs = require("../../asset/svgs.js");
@@ -14,7 +14,7 @@ var _dom = require("../../util/dom.js");
14
14
  const _excluded = ["className", "templates", "autocomplete"];
15
15
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
16
16
  function _objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t = {}; for (var n in r) if ({}.hasOwnProperty.call(r, n)) { if (-1 !== e.indexOf(n)) continue; t[n] = r[n]; } return t; }
17
- const TYPE = 'search-box';
17
+ const TYPE = _constants.LAYOUT_TYPE.SEARCH_BOX;
18
18
  const DEFAULT_CLASSNAME = 'miso-search-box';
19
19
  const DEFAULT_AUTOCOMPLETE_CLASSNAME = 'miso-autocomplete';
20
20
  function root(layout) {
@@ -170,9 +170,31 @@ class SearchBoxLayout extends _template.default {
170
170
  proxyElement,
171
171
  hub
172
172
  } = view;
173
- this._unsubscribes = [...this._unsubscribes, proxyElement.on('input', e => this._syncInput(e)), proxyElement.on('keydown', e => this._handleKeyDown(e)), proxyElement.on('click', e => this._handleClick(e)), proxyElement.on('focusin', e => this._handleFocusIn(e)), proxyElement.on('focusout', e => this._handleFocusOut(e)), hub.on(_index.fields.completions(), () => view.refresh({
173
+ this._unsubscribes = [...this._unsubscribes, proxyElement.on('input', e => this._syncInput(e)), proxyElement.on('keydown', e => this._handleKeyDown(e)), proxyElement.on('click', e => this._handleClick(e)), proxyElement.on('focusin', e => this._handleFocusIn(e)), proxyElement.on('focusout', e => this._handleFocusOut(e)), hub.on(_clientSdkWorkflow.fields.completions(), () => view.refresh({
174
174
  force: true
175
175
  }))];
176
+ if (this.options.disableWhenOngoing) {
177
+ // disable submission while the main view is still displaying an answer
178
+ const mainRole = view.workflow._roles && view.workflow._roles.main;
179
+ if (mainRole) {
180
+ this._unsubscribes.push(hub.on(_clientSdkWorkflow.fields.view(mainRole), state => this._syncDisabled(!!state.ongoing)));
181
+ }
182
+ }
183
+ }
184
+ _syncDisabled(disabled) {
185
+ if (this._disabled === disabled) {
186
+ return;
187
+ }
188
+ this._disabled = disabled;
189
+ this._syncDisabledAttributes();
190
+ }
191
+ _syncDisabledAttributes() {
192
+ // the user can keep typing; only submission is disabled
193
+ const element = this._element;
194
+ const button = element && element.querySelector(`[data-role="submit"]`);
195
+ if (button) {
196
+ button.disabled = !!this._disabled;
197
+ }
176
198
  }
177
199
  focus() {
178
200
  // TODO: support blur as well
@@ -203,7 +225,7 @@ class SearchBoxLayout extends _template.default {
203
225
  state
204
226
  });
205
227
  return Object.assign({}, state, {
206
- completions: this._view.hub.states[_index.fields.completions()]
228
+ completions: this._view.hub.states[_clientSdkWorkflow.fields.completions()]
207
229
  });
208
230
  }
209
231
  _render(element, data, controls) {
@@ -216,6 +238,7 @@ class SearchBoxLayout extends _template.default {
216
238
  _afterRender(element, state) {
217
239
  this._fullfillFocusRequest(element);
218
240
  this._fullfillValueRequest(element);
241
+ this._syncDisabledAttributes(); // a re-render replaces the input element
219
242
  }
220
243
  _updateCompletions(element, {
221
244
  state,
@@ -293,7 +316,7 @@ class SearchBoxLayout extends _template.default {
293
316
  }
294
317
  this._prevInput = value;
295
318
  const self = this;
296
- this._debounce(() => self._view.hub.update(_index.fields.input(), {
319
+ this._debounce(() => self._view.hub.update(_clientSdkWorkflow.fields.input(), {
297
320
  value
298
321
  }));
299
322
  }
@@ -325,7 +348,9 @@ class SearchBoxLayout extends _template.default {
325
348
  }) {
326
349
  if (!isComposing && key === 'Enter' && target.matches('[data-role="input"]')) {
327
350
  this._submit(target.value);
328
- target.blur();
351
+ if (this.options.blurOnSubmit !== false) {
352
+ target.blur();
353
+ }
329
354
  }
330
355
  // TODO: select completion items with arrow keys
331
356
  }
@@ -339,7 +364,9 @@ class SearchBoxLayout extends _template.default {
339
364
  for (let element = target; element && element !== this._element; element = element.parentElement) {
340
365
  if (element.matches('[data-role="submit"]') || element.matches('[data-role="button"]') || element.matches('[type="submit"]')) {
341
366
  if (inputElement) {
342
- inputElement.blur();
367
+ if (this.options.blurOnSubmit !== false) {
368
+ inputElement.blur();
369
+ }
343
370
  this._submit(inputElement.value);
344
371
  }
345
372
  return;
@@ -375,14 +402,25 @@ class SearchBoxLayout extends _template.default {
375
402
  }
376
403
  }
377
404
  async _submit(value) {
378
- if (!value || !value.trim()) {
405
+ if (this._disabled || !value || !value.trim()) {
379
406
  return;
380
407
  }
381
408
  this.close();
382
- // TODO: q -> value
383
- this._view.hub.update(_index.fields.query(), {
384
- q: value
385
- });
409
+ // a view event, like the buttons: the workflow routes it into its query
410
+ // flow (or its own submission semantics), the layout stays hands-off
411
+ const {
412
+ session
413
+ } = this._view._state || {};
414
+ this._view._emit('submit', Object.freeze({
415
+ session,
416
+ value
417
+ }));
418
+ if (this.options.clearOnSubmit) {
419
+ const inputElement = this._element && this._element.querySelector(`[data-role="input"]`);
420
+ if (inputElement) {
421
+ inputElement.value = '';
422
+ }
423
+ }
386
424
  this._trackSubmit(value);
387
425
  }
388
426
  _trackSubmit(value) {
@@ -4,13 +4,14 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.default = void 0;
7
+ var _constants = require("../../constants.js");
7
8
  var _commons = require("@miso.ai/commons");
8
9
  var _template = _interopRequireDefault(require("../template.js"));
9
10
  var _trackable = require("../mixin/trackable.js");
10
11
  const _excluded = ["className", "templates"];
11
12
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
12
13
  function _objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t = {}; for (var n in r) if ({}.hasOwnProperty.call(r, n)) { if (-1 !== e.indexOf(n)) continue; t[n] = r[n]; } return t; }
13
- const TYPE = 'select';
14
+ const TYPE = _constants.LAYOUT_TYPE.SELECT;
14
15
  const DEFAULT_CLASSNAME = 'miso-select';
15
16
  function root(layout, state) {
16
17
  const {
@@ -4,11 +4,12 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.default = void 0;
7
+ var _constants = require("../../constants.js");
7
8
  var _collection = _interopRequireDefault(require("./collection.js"));
8
9
  const _excluded = ["className"];
9
10
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
10
11
  function _objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t = {}; for (var n in r) if ({}.hasOwnProperty.call(r, n)) { if (-1 !== e.indexOf(n)) continue; t[n] = r[n]; } return t; }
11
- const TYPE = 'cards';
12
+ const TYPE = _constants.LAYOUT_TYPE.CARDS;
12
13
  const DEFAULT_CLASSNAME = 'miso-cards';
13
14
  class CardsLayout extends _collection.default {
14
15
  static get type() {
@@ -4,13 +4,14 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.default = void 0;
7
+ var _constants = require("../../constants.js");
7
8
  var _collection = _interopRequireDefault(require("./collection.js"));
8
9
  var _swipeable = require("../mixin/swipeable.js");
9
10
  var _svgs = require("../../asset/svgs.js");
10
11
  const _excluded = ["className", "templates"];
11
12
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
12
13
  function _objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t = {}; for (var n in r) if ({}.hasOwnProperty.call(r, n)) { if (-1 !== e.indexOf(n)) continue; t[n] = r[n]; } return t; }
13
- const TYPE = 'carousel';
14
+ const TYPE = _constants.LAYOUT_TYPE.CAROUSEL;
14
15
  const DEFAULT_CLASSNAME = 'miso-carousel';
15
16
  const DEFAULT_TEMPLATES = Object.freeze({
16
17
  body,
@@ -95,6 +95,11 @@ const DEFAULT_TEMPLATES = Object.freeze({
95
95
  compactArticle: _templates.compactArticle,
96
96
  image: _templates.image,
97
97
  question: _templates.question,
98
+ thread: _templates.thread,
99
+ threadBody: _templates.threadBody,
100
+ threadMenuBlock: _templates.threadMenuBlock,
101
+ message: _templates.message,
102
+ messageBody: _templates.messageBody,
98
103
  root,
99
104
  [_constants.STATUS.READY]: ready,
100
105
  empty: () => ``,
@@ -203,7 +208,14 @@ class CollectionLayout extends _template.default {
203
208
  return element.querySelector('[data-role="list"]');
204
209
  }
205
210
  _getItemElements(element) {
206
- return element ? Array.from(element.querySelectorAll(`[data-role="item"]`)) : [];
211
+ if (!element) {
212
+ return [];
213
+ }
214
+ // an item may host a nested collection with items of its own (a
215
+ // conversation message and its source cards): ours are only those whose
216
+ // closest list is this layout's own list
217
+ const listElement = this._getListElement(element);
218
+ return Array.from((listElement || element).querySelectorAll(`[data-role="item"]`)).filter(item => item.closest(`[data-role="list"]`) === listElement);
207
219
  }
208
220
  _onClick(event) {
209
221
  const element = event.target.closest(`[data-role="item"]`);
@@ -4,6 +4,7 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.default = void 0;
7
+ var _constants = require("../../constants.js");
7
8
  var _commons = require("@miso.ai/commons");
8
9
  var _collection = _interopRequireDefault(require("./collection.js"));
9
10
  var _dom = require("../../util/dom.js");
@@ -43,7 +44,7 @@ const DEFAULT_HEIGHT = '16rem';
43
44
  const DEFAULT_TEMPLATES = Object.freeze(Object.assign({}, _collection.default.defaultTemplates, {
44
45
  item
45
46
  }));
46
- const TYPE = 'gallery';
47
+ const TYPE = _constants.LAYOUT_TYPE.GALLERY;
47
48
  const DEFAULT_CLASSNAME = 'miso-gallery';
48
49
  class GalleryLayout extends _collection.default {
49
50
  static get type() {
@@ -4,11 +4,12 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.default = void 0;
7
+ var _constants = require("../../constants.js");
7
8
  var _collection = _interopRequireDefault(require("./collection.js"));
8
9
  const _excluded = ["className"];
9
10
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
10
11
  function _objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t = {}; for (var n in r) if ({}.hasOwnProperty.call(r, n)) { if (-1 !== e.indexOf(n)) continue; t[n] = r[n]; } return t; }
11
- const TYPE = 'horizontal';
12
+ const TYPE = _constants.LAYOUT_TYPE.HORIZONTAL;
12
13
  const DEFAULT_CLASSNAME = 'miso-horizontal';
13
14
  class HorizontalLayout extends _collection.default {
14
15
  static get type() {
@@ -4,11 +4,12 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.default = void 0;
7
+ var _constants = require("../../constants.js");
7
8
  var _collection = _interopRequireDefault(require("./collection.js"));
8
9
  const _excluded = ["className"];
9
10
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
10
11
  function _objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t = {}; for (var n in r) if ({}.hasOwnProperty.call(r, n)) { if (-1 !== e.indexOf(n)) continue; t[n] = r[n]; } return t; }
11
- const TYPE = 'list';
12
+ const TYPE = _constants.LAYOUT_TYPE.LIST;
12
13
  const DEFAULT_CLASSNAME = 'miso-list';
13
14
  class ListLayout extends _collection.default {
14
15
  static get type() {
@@ -0,0 +1,359 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = void 0;
7
+ var _constants = require("../../constants.js");
8
+ var _collection = _interopRequireDefault(require("./collection.js"));
9
+ const _excluded = ["className", "templates"];
10
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
11
+ function _objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t = {}; for (var n in r) if ({}.hasOwnProperty.call(r, n)) { if (-1 !== e.indexOf(n)) continue; t[n] = r[n]; } return t; }
12
+ const TYPE = _constants.LAYOUT_TYPE.MESSAGES;
13
+ const DEFAULT_CLASSNAME = 'miso-messages';
14
+
15
+ // the infinite scroll trigger sits ABOVE the list — older messages load as
16
+ // the user scrolls up, unlike the stock collection root's bottom trigger
17
+ function root(layout, state) {
18
+ const {
19
+ className,
20
+ role,
21
+ templates,
22
+ options
23
+ } = layout;
24
+ const {
25
+ status
26
+ } = state;
27
+ const roleAttr = role ? ` data-role="${role}"` : '';
28
+ const itemTypeAttr = options.itemType ? ` data-item-type="${options.itemType}"` : '';
29
+ return `<div class="${className} ${status}"${roleAttr}${itemTypeAttr}>${templates.trigger(layout, state)}${status === _constants.STATUS.READY ? templates[status](layout, state) : ''}${templates.loading(layout, state)}</div>`;
30
+ }
31
+
32
+ /**
33
+ * The conversation panel of the chat history interface: a shell that renders
34
+ * one <miso-message-item> container element per message item, incrementally —
35
+ * fresh items land at either end of the list: appended messages (a posted
36
+ * follow-up question) render as new items at the bottom, while an older
37
+ * page fetched by the more flow (infinite scroll: the trigger, placed above
38
+ * the list, fires the `more` hub field as the user scrolls up) renders as
39
+ * prepended items — told apart by which boundary the already-rendered
40
+ * records line up with.
41
+ * The content of a message is not rendered here: each <miso-message-item> is
42
+ * assigned its item subworkflow (workflow._getMessageWorkflow, off the item
43
+ * binding) in the post-render sync pass, and the role elements inside
44
+ * (question, answer, ...) render through that workflow's own layouts.
45
+ *
46
+ * All message-level presentation is the message workflow's own: a record
47
+ * without its answer body presents as status `loading`, stamped on the
48
+ * <miso-message-item> element by its container layout — there is deliberately no
49
+ * per-message loading icon; the answer typewriter's blinking caret is the
50
+ * loading indication — and the question bubble (text, authorship
51
+ * attributes) renders through the `question` layout.
52
+ *
53
+ * The shell keeps the panel-level behaviors: scroll pinning (the view sticks
54
+ * to the bottom while the user has not scrolled up, following content growth
55
+ * as answers type out), scroll anchoring while scrolled up (prepended items
56
+ * — and their contents, rendering in later through their own workflows —
57
+ * must not shift the reading position), and the `ongoing` view state read
58
+ * off the data — the last message's answer absent or unfinished — which
59
+ * e.g. disables the search box while an answer is on its way.
60
+ */
61
+ class MessagesLayout extends _collection.default {
62
+ static get type() {
63
+ return TYPE;
64
+ }
65
+ static get defaultClassName() {
66
+ return DEFAULT_CLASSNAME;
67
+ }
68
+ constructor(_ref = {}) {
69
+ let {
70
+ className = DEFAULT_CLASSNAME,
71
+ templates
72
+ } = _ref,
73
+ options = _objectWithoutPropertiesLoose(_ref, _excluded);
74
+ super(Object.assign({
75
+ className,
76
+ templates: Object.assign({
77
+ root
78
+ }, templates)
79
+ }, options));
80
+ this._pinned = true; // whether the view sticks to the bottom on updates
81
+ this._anchor = undefined; // the scroll anchor while prepended content settles
82
+ this._ongoing = undefined;
83
+ }
84
+
85
+ // item identity: the question id, or the local placeholder id standing in
86
+ // for it while a posted question awaits its response — the placeholder id
87
+ // survives the settle, so the element and its workflow do too
88
+ _getItemKey(message) {
89
+ return message.placeholder_id || message.question_id || message;
90
+ }
91
+
92
+ // render //
93
+ async render(element, state, controls = {}) {
94
+ // capture the view state callback for the ongoing report
95
+ this._notifyUpdate = controls.notifyUpdate;
96
+ await super.render(element, state, controls);
97
+ }
98
+
99
+ // incremental renders cover a pure append (a posted follow-up at the
100
+ // bottom) or a pure prepend (an older page above) — anything else
101
+ // re-renders in full
102
+ _shallRenderIncrementally(state, rendered) {
103
+ if (!super._shallRenderIncrementally(state, rendered)) {
104
+ return false;
105
+ }
106
+ const values = this._getItems(state) || [];
107
+ const renderedValues = rendered.value;
108
+ const count = values.length - renderedValues.length;
109
+ return count === 0 ||
110
+ // in-place changes only
111
+ this._getItemKey(values[0]) === this._getItemKey(renderedValues[0]) ||
112
+ // appended
113
+ this._getItemKey(values[count]) === this._getItemKey(renderedValues[0]); // prepended
114
+ }
115
+
116
+ // the fresh items land at the tail (a posted follow-up) or at the head
117
+ // (an older page fetched by the more flow): told apart by which boundary
118
+ // the already-rendered records line up with
119
+ _html(state, rendered, incremental) {
120
+ if (!incremental) {
121
+ return this.templates.root(this, state);
122
+ }
123
+ const values = this._getItems(state) || [];
124
+ const renderedValues = rendered.value;
125
+ const count = values.length - renderedValues.length;
126
+ if (count === 0) {
127
+ return '';
128
+ }
129
+ if (this._getItemKey(values[0]) === this._getItemKey(renderedValues[0])) {
130
+ const fresh = values.slice(renderedValues.length);
131
+ return {
132
+ position: 'beforeend',
133
+ items: this.templates.items(this, state, fresh, {
134
+ offset: renderedValues.length
135
+ })
136
+ };
137
+ }
138
+ const fresh = values.slice(0, count);
139
+ return {
140
+ position: 'afterbegin',
141
+ items: this.templates.items(this, state, fresh, {
142
+ offset: 0
143
+ })
144
+ };
145
+ }
146
+ _render(element, {
147
+ state
148
+ }, {
149
+ notifyUpdate
150
+ }) {
151
+ const {
152
+ incremental,
153
+ html
154
+ } = state;
155
+ if (incremental) {
156
+ if (html) {
157
+ const listElement = this._getListElement(element);
158
+ if (html.position === 'afterbegin') {
159
+ this._prependAnchored(listElement, html.items);
160
+ } else {
161
+ listElement.insertAdjacentHTML('beforeend', html.items);
162
+ }
163
+ } else {
164
+ notifyUpdate(false);
165
+ }
166
+ } else {
167
+ element.innerHTML = html;
168
+ }
169
+ }
170
+ _afterRender(element, state) {
171
+ super._afterRender(element, state); // syncs bindings and (re)arms the trigger
172
+ if (!state.incremental) {
173
+ // a fresh render (thread load): jump to the bottom
174
+ this._dropAnchor();
175
+ this._scrollToBottom({
176
+ force: true
177
+ });
178
+ } else if (state.html && state.html.position === 'beforeend') {
179
+ // appended items (posted follow-up): jump to the bottom
180
+ this._scrollToBottom({
181
+ force: true
182
+ });
183
+ }
184
+ this._watchGrowth(this._getListElement(element) || element);
185
+ this._syncWorkflows(element);
186
+ this._syncOngoing(state);
187
+ }
188
+ _unrender() {
189
+ super._unrender();
190
+ this._dropAnchor();
191
+ }
192
+
193
+ // assign each <miso-message-item> its item subworkflow, off the item binding
194
+ _syncWorkflows(element) {
195
+ const workflow = this._view && this._view.workflow;
196
+ if (!workflow || typeof workflow._getMessageWorkflow !== 'function') {
197
+ return;
198
+ }
199
+ for (const item of this._getItemElements(element)) {
200
+ const binding = this._bindings.get(item);
201
+ if (binding && item.isContainer) {
202
+ item.workflow = workflow._getMessageWorkflow(binding.value);
203
+ }
204
+ }
205
+ }
206
+
207
+ // whether an answer is on its way — pending, or still being generated —
208
+ // reported as the `ongoing` view state, e.g. for the search box to disable
209
+ // submission
210
+ _syncOngoing(state) {
211
+ const messages = state.value || [];
212
+ const last = messages[messages.length - 1];
213
+ // a message settled by a failed content fetch is not on its way anymore
214
+ const ongoing = !!last && !last.error && (last.answer === undefined || last.finished === false);
215
+ if (this._ongoing === ongoing) {
216
+ return;
217
+ }
218
+ this._ongoing = ongoing;
219
+ this._notifyUpdate && this._notifyUpdate({
220
+ ongoing
221
+ });
222
+ }
223
+
224
+ // scrolling //
225
+ /**
226
+ * Scroll the containing scrollable to the bottom. Unless forced, only
227
+ * applies while the user is pinned to the bottom — scrolling up to read
228
+ * releases the pin, scrolling back down restores it.
229
+ */
230
+ _scrollToBottom({
231
+ force = false
232
+ } = {}) {
233
+ const scrollable = this._getScrollable();
234
+ if (!scrollable || !(force || this._pinned)) {
235
+ return;
236
+ }
237
+ scrollable.scrollTop = scrollable.scrollHeight;
238
+ this._pinned = true;
239
+ }
240
+ _getScrollable() {
241
+ const element = this._element;
242
+ if (!element || typeof getComputedStyle === 'undefined') {
243
+ return undefined;
244
+ }
245
+ for (let el = element; el && el.nodeType === 1 && el !== document.body; el = el.parentElement) {
246
+ const {
247
+ overflowY
248
+ } = getComputedStyle(el);
249
+ if (overflowY === 'auto' || overflowY === 'scroll') {
250
+ this._watchScroll(el);
251
+ return el;
252
+ }
253
+ }
254
+ return undefined;
255
+ }
256
+ _watchScroll(scrollable) {
257
+ if (this._watchedScrollable === scrollable) {
258
+ return;
259
+ }
260
+ this._unwatchScroll && this._unwatchScroll();
261
+ const onScroll = () => {
262
+ this._pinned = scrollable.scrollHeight - scrollable.scrollTop - scrollable.clientHeight < 40;
263
+ if (this._pinned) {
264
+ // back at the bottom: the reading position needs no anchor
265
+ this._anchor = undefined;
266
+ } else if (this._anchor && this._anchor.element.isConnected) {
267
+ // follow the user: the anchor holds the position they scrolled to,
268
+ // not the one where the prepend happened
269
+ this._anchor.top = this._relativeTop(this._anchor.element, scrollable);
270
+ }
271
+ };
272
+ scrollable.addEventListener('scroll', onScroll, {
273
+ passive: true
274
+ });
275
+ this._watchedScrollable = scrollable;
276
+ const unwatch = this._unwatchScroll = () => {
277
+ scrollable.removeEventListener('scroll', onScroll);
278
+ this._watchedScrollable = undefined;
279
+ this._unwatchScroll = undefined;
280
+ };
281
+ this._unsubscribes.push(() => this._unwatchScroll === unwatch && unwatch());
282
+ }
283
+
284
+ // the message contents render (and type out) through their own workflows,
285
+ // outside this layout's render cycle: follow the content growth — pinned,
286
+ // the view sticks to the bottom; scrolled up with an anchor active (a
287
+ // prepended page still rendering its contents in), the anchor is held
288
+ // stationary instead
289
+ _watchGrowth(element) {
290
+ if (!element || this._watchedGrowth === element || typeof ResizeObserver === 'undefined') {
291
+ return;
292
+ }
293
+ this._unwatchGrowth && this._unwatchGrowth();
294
+ const observer = new ResizeObserver(() => this._onContentGrowth());
295
+ observer.observe(element);
296
+ this._watchedGrowth = element;
297
+ const unwatch = this._unwatchGrowth = () => {
298
+ observer.disconnect();
299
+ this._watchedGrowth = undefined;
300
+ this._unwatchGrowth = undefined;
301
+ };
302
+ this._unsubscribes.push(() => this._unwatchGrowth === unwatch && unwatch());
303
+ }
304
+ _onContentGrowth() {
305
+ if (this._pinned) {
306
+ this._scrollToBottom();
307
+ } else {
308
+ this._restoreAnchor();
309
+ }
310
+ }
311
+
312
+ // scroll anchoring for prepended content //
313
+ // The anchor is the previously-first item: everything prepended — and
314
+ // its content, rendering in later through the item workflows — sits
315
+ // above it, so holding the anchor stationary holds the reading position,
316
+ // while growth below it (a streaming answer at the bottom) moves nothing
317
+ // above and needs no compensation. Not the browser's own scroll
318
+ // anchoring, which not every engine provides.
319
+ _prependAnchored(listElement, items) {
320
+ this._setAnchor();
321
+ listElement.insertAdjacentHTML('afterbegin', items);
322
+ this._restoreAnchor();
323
+ }
324
+ _setAnchor() {
325
+ const scrollable = this._getScrollable();
326
+ const element = this._getItemElements(this._element)[0];
327
+ this._anchor = scrollable && element ? {
328
+ element,
329
+ scrollable,
330
+ top: this._relativeTop(element, scrollable)
331
+ } : undefined;
332
+ }
333
+ _restoreAnchor() {
334
+ const anchor = this._anchor;
335
+ if (!anchor) {
336
+ return;
337
+ }
338
+ const {
339
+ element,
340
+ scrollable,
341
+ top
342
+ } = anchor;
343
+ if (!element.isConnected) {
344
+ this._anchor = undefined;
345
+ return;
346
+ }
347
+ const delta = this._relativeTop(element, scrollable) - top;
348
+ if (delta) {
349
+ scrollable.scrollTop += delta;
350
+ }
351
+ }
352
+ _relativeTop(element, scrollable) {
353
+ return element.getBoundingClientRect().top - scrollable.getBoundingClientRect().top;
354
+ }
355
+ _dropAnchor() {
356
+ this._anchor = undefined;
357
+ }
358
+ }
359
+ exports.default = MessagesLayout;