@miso.ai/client-sdk-ui 1.12.2 → 1.12.3-beta.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 (41) hide show
  1. package/dist/cjs/actor/data.js +4 -16
  2. package/dist/cjs/constants.js +8 -3
  3. package/dist/cjs/defaults/ask/phrases.js +7 -3
  4. package/dist/cjs/defaults/ask/templates.js +19 -19
  5. package/dist/cjs/defaults/common/templates.js +6 -4
  6. package/dist/cjs/element/context/miso-follow-ups.js +45 -13
  7. package/dist/cjs/element/role/index.js +7 -0
  8. package/dist/cjs/element/role/miso-follow-up-questions.js +20 -0
  9. package/dist/cjs/layout/input/option-list.js +4 -5
  10. package/dist/cjs/layout/text/typewriter/index.js +19 -1
  11. package/dist/cjs/version.js +1 -1
  12. package/dist/cjs/workflow/answer-based.js +12 -7
  13. package/dist/cjs/workflow/ask.js +66 -5
  14. package/dist/cjs/workflow/asks.js +2 -1
  15. package/dist/cjs/workflow/base.js +19 -8
  16. package/dist/cjs/workflow/explore.js +2 -2
  17. package/dist/cjs/workflow/hybrid-search-answer.js +3 -3
  18. package/dist/cjs/workflow/hybrid-search-results.js +2 -2
  19. package/dist/cjs/workflow/processors.js +45 -1
  20. package/dist/cjs/workflow/search-based.js +26 -3
  21. package/package.json +2 -2
  22. package/src/actor/data.js +2 -10
  23. package/src/constants.js +8 -1
  24. package/src/defaults/ask/phrases.js +5 -2
  25. package/src/defaults/ask/templates.js +12 -12
  26. package/src/defaults/common/templates.js +6 -6
  27. package/src/element/context/miso-follow-ups.js +36 -11
  28. package/src/element/role/index.js +1 -0
  29. package/src/element/role/miso-follow-up-questions.js +16 -0
  30. package/src/layout/input/option-list.js +3 -4
  31. package/src/layout/text/typewriter/index.js +14 -1
  32. package/src/version.js +1 -1
  33. package/src/workflow/answer-based.js +13 -8
  34. package/src/workflow/ask.js +48 -5
  35. package/src/workflow/asks.js +2 -1
  36. package/src/workflow/base.js +16 -8
  37. package/src/workflow/explore.js +2 -2
  38. package/src/workflow/hybrid-search-answer.js +4 -4
  39. package/src/workflow/hybrid-search-results.js +2 -2
  40. package/src/workflow/processors.js +40 -2
  41. package/src/workflow/search-based.js +26 -4
@@ -90,7 +90,7 @@ class Workflow extends _commons.Component {
90
90
  _initSubscriptions({
91
91
  roles
92
92
  }) {
93
- this._unsubscribes = [this._hub.on(_index.fields.response(), data => this.updateData(data)), this._hub.on(_index.fields.tracker(), args => this._onTracker(args))];
93
+ this._unsubscribes = [this._hub.on(_index.fields.session(), session => this._onSession(session)), this._hub.on(_index.fields.response(), data => this.updateData(data)), this._hub.on(_index.fields.tracker(), args => this._onTracker(args))];
94
94
  if (roles.main) {
95
95
  this._unsubscribes.push(this._hub.on(_index.fields.view(roles.main), state => this._onMainViewUpdate(state)));
96
96
  }
@@ -166,6 +166,11 @@ class Workflow extends _commons.Component {
166
166
  this._emitLifecycleEvent('finally', state);
167
167
  }
168
168
  }
169
+ _onSession(session) {
170
+ this.updateData({
171
+ session
172
+ });
173
+ }
169
174
  _onMainViewUpdate(state) {
170
175
  const {
171
176
  status
@@ -202,10 +207,14 @@ class Workflow extends _commons.Component {
202
207
  const {
203
208
  session
204
209
  } = this;
205
- const event = (0, _index3.mergeApiOptions)(this._options.resolved.api, Object.assign({}, options, {
210
+ const request = (0, _index3.mergeApiOptions)(this._options.resolved.api, options);
211
+ this.updateData({
212
+ session,
213
+ request
214
+ });
215
+ this._hub.update(_index.fields.request(), Object.assign({}, request, {
206
216
  session
207
217
  }));
208
- this._hub.update(_index.fields.request(), event);
209
218
  }
210
219
 
211
220
  // data //
@@ -238,13 +247,15 @@ class Workflow extends _commons.Component {
238
247
  return this;
239
248
  }
240
249
  _updateData(data) {
241
- data = this._defaultProcessData(data);
250
+ const oldData = this._hub.states[_index.fields.data()];
251
+ data = this._defaultProcessData(data, oldData);
242
252
  for (const process of this._options.resolved.dataProcessor) {
243
- data = process(data);
253
+ data = process(data, oldData);
244
254
  }
245
- this._updateDataInHub(data);
255
+ this._updateDataInHub(data, oldData);
246
256
  }
247
- _defaultProcessData(data) {
257
+ _defaultProcessData(data, oldData) {
258
+ data = (0, _processors.writeRequestFromPreviousData)(data, oldData);
248
259
  data = (0, _processors.writeDataStatus)(data);
249
260
  data = (0, _processors.writeMisoIdToMeta)(data);
250
261
  return data;
@@ -324,7 +335,7 @@ class Workflow extends _commons.Component {
324
335
 
325
336
  // destroy //
326
337
  destroy(options) {
327
- this._events.emit('destroy');
338
+ this._emit('destroy');
328
339
  this._destroy(options);
329
340
  }
330
341
  _destroy({
@@ -129,8 +129,8 @@ class Explore extends _unit.default {
129
129
  super.notifyViewUpdate(role, ...args);
130
130
  return this;
131
131
  }
132
- _defaultProcessData(data) {
133
- data = super._defaultProcessData(data);
132
+ _defaultProcessData(data, oldData) {
133
+ data = super._defaultProcessData(data, oldData);
134
134
  data = this._addUrlToRelatedQuestions(data);
135
135
  return data;
136
136
  }
@@ -61,14 +61,14 @@ class HybridSearchAnswer extends _answerBased.default {
61
61
  payload = _objectWithoutPropertiesLoose(_ref, _excluded);
62
62
  return Object.assign({}, payload, {
63
63
  _meta: Object.assign({}, payload._meta, {
64
- question_source: qs || _constants.ORGANIC_QUESTION_SOURCE // might be null, not undefined
64
+ question_source: qs || _constants.QUESTION_SOURCE.ORGANIC // might be null, not undefined
65
65
  })
66
66
  });
67
67
  }
68
68
 
69
69
  // data //
70
- _defaultProcessData(data) {
71
- data = super._defaultProcessData(data);
70
+ _defaultProcessData(data, oldData) {
71
+ data = super._defaultProcessData(data, oldData);
72
72
  data = (0, _processors.writeKeywordsToData)(data);
73
73
  data = (0, _processors.writeMisoIdFromSession)(data);
74
74
  data = (0, _processors.writeUnanswerableToMeta)(data);
@@ -69,8 +69,8 @@ class HybridSearchResults extends _searchBased.default {
69
69
  }
70
70
 
71
71
  // data //
72
- _defaultProcessData(data) {
73
- data = super._defaultProcessData(data);
72
+ _defaultProcessData(data, oldData) {
73
+ data = super._defaultProcessData(data, oldData);
74
74
  (0, _processors.writeMisoIdToSession)(data);
75
75
  return data;
76
76
  }
@@ -7,6 +7,7 @@ exports.buildBaseInteraction = buildBaseInteraction;
7
7
  exports.concatItemsFromMoreResponse = concatItemsFromMoreResponse;
8
8
  exports.getRevision = getRevision;
9
9
  exports.mappingAnswerData = mappingAnswerData;
10
+ exports.mappingFollowUpQuestionsData = mappingFollowUpQuestionsData;
10
11
  exports.mappingReasoningData = mappingReasoningData;
11
12
  exports.mappingSortData = mappingSortData;
12
13
  exports.mappingSuggestionsData = mappingSuggestionsData;
@@ -18,9 +19,11 @@ exports.writeDataStatus = writeDataStatus;
18
19
  exports.writeEventTargetToInteraction = writeEventTargetToInteraction;
19
20
  exports.writeExhaustionToData = writeExhaustionToData;
20
21
  exports.writeKeywordsToData = writeKeywordsToData;
22
+ exports.writeMisoIdAsRootMisoId = writeMisoIdAsRootMisoId;
21
23
  exports.writeMisoIdFromSession = writeMisoIdFromSession;
22
24
  exports.writeMisoIdToMeta = writeMisoIdToMeta;
23
25
  exports.writeMisoIdToSession = writeMisoIdToSession;
26
+ exports.writeRequestFromPreviousData = writeRequestFromPreviousData;
24
27
  exports.writeUnanswerableToMeta = writeUnanswerableToMeta;
25
28
  var _commons = require("@miso.ai/commons");
26
29
  var _constants = require("../constants.js");
@@ -99,11 +102,31 @@ function mappingSuggestionsData(_, {
99
102
  text
100
103
  }));
101
104
  }
105
+ function mappingFollowUpQuestionsData(data) {
106
+ if (!data || !data.value) {
107
+ return undefined;
108
+ }
109
+ return (data.value.followup_questions || []).map(text => ({
110
+ text
111
+ }));
112
+ }
102
113
 
103
114
  // data //
104
115
  function getRevision(data) {
105
116
  return data && data.value && data.value.revision;
106
117
  }
118
+ function writeRequestFromPreviousData(data, oldData) {
119
+ // patch request in case user call updateData() without request object in custom data scenario
120
+ if (!data || !data.value || data.request || !oldData || !oldData.request) {
121
+ return data;
122
+ }
123
+ const {
124
+ request
125
+ } = oldData;
126
+ return Object.assign({}, data, {
127
+ request
128
+ });
129
+ }
107
130
  function writeDataStatus(data) {
108
131
  const status = getStatus(data);
109
132
  const ongoing = status === _constants.STATUS.READY ? data.ongoing : undefined;
@@ -250,6 +273,25 @@ function retainFacetCountsInData(data, currentFacetCounts) {
250
273
  })
251
274
  });
252
275
  }
276
+ function writeMisoIdAsRootMisoId(data) {
277
+ const {
278
+ value
279
+ } = data;
280
+ if (!value) {
281
+ return data;
282
+ }
283
+ const {
284
+ miso_id
285
+ } = value;
286
+ if (!miso_id) {
287
+ return data;
288
+ }
289
+ return Object.assign({}, data, {
290
+ value: Object.assign({}, value, {
291
+ root_miso_id: miso_id
292
+ })
293
+ });
294
+ }
253
295
  function concatItemsFromMoreResponse(oldData, newData, {
254
296
  role = _constants.ROLE.PRODUCTS
255
297
  } = {}) {
@@ -259,7 +301,8 @@ function concatItemsFromMoreResponse(oldData, newData, {
259
301
  const {
260
302
  [role]: oldProducts = [],
261
303
  facet_counts,
262
- total
304
+ total,
305
+ root_miso_id
263
306
  } = oldData.value;
264
307
  const {
265
308
  [role]: newProducts = []
@@ -272,6 +315,7 @@ function concatItemsFromMoreResponse(oldData, newData, {
272
315
  value: Object.assign({}, newData.value, {
273
316
  facet_counts,
274
317
  total,
318
+ root_miso_id,
275
319
  [role]: [...oldProducts, ...newProducts]
276
320
  })
277
321
  });
@@ -215,8 +215,8 @@ class SearchBasedWorkflow extends _base.default {
215
215
  }
216
216
 
217
217
  // data //
218
- _defaultProcessData(data) {
219
- data = super._defaultProcessData(data);
218
+ _defaultProcessData(data, oldData) {
219
+ data = super._defaultProcessData(data, oldData);
220
220
  data = (0, _processors.writeKeywordsToData)(data);
221
221
  data = (0, _processors.retainFacetCountsInData)(data, this._currentFacetCounts);
222
222
  data = (0, _processors.writeExhaustionToData)(data, {
@@ -239,7 +239,7 @@ class SearchBasedWorkflow extends _base.default {
239
239
  _meta
240
240
  } = request.payload;
241
241
  if (!_meta || !_meta.page) {
242
- return data; // not from "more" request
242
+ return (0, _processors.writeMisoIdAsRootMisoId)(data); // not from "more" request
243
243
  }
244
244
  // concat records if it's from "more" request
245
245
  const currentData = this._hub.states[_index.fields.data()];
@@ -260,6 +260,29 @@ class SearchBasedWorkflow extends _base.default {
260
260
  });
261
261
  this._views.filters.apply();
262
262
  }
263
+
264
+ // interactions //
265
+ _defaultProcessInteraction(payload, args) {
266
+ payload = super._defaultProcessInteraction(payload, args);
267
+ payload = this._writeRootMisoIdToInteraction(payload);
268
+ return payload;
269
+ }
270
+ _writeRootMisoIdToInteraction(payload) {
271
+ const data = this._hub.states[_index.fields.data()];
272
+ const {
273
+ root_miso_id
274
+ } = data && data.value || {};
275
+ if (!root_miso_id) {
276
+ return payload;
277
+ }
278
+ return (0, _processors.mergeInteraction)(payload, {
279
+ context: {
280
+ custom_context: {
281
+ root_miso_id
282
+ }
283
+ }
284
+ });
285
+ }
263
286
  }
264
287
  exports.default = SearchBasedWorkflow;
265
288
  (0, _index3.makeConfigurable)(SearchBasedWorkflow.prototype, [_constants.WORKFLOW_CONFIGURABLE.PAGINATION, _constants.WORKFLOW_CONFIGURABLE.FILTERS]);
package/package.json CHANGED
@@ -26,7 +26,7 @@
26
26
  "url": "https://github.com/MisoAI/miso-client-js-sdk/issues"
27
27
  },
28
28
  "dependencies": {
29
- "@miso.ai/commons": "1.12.2"
29
+ "@miso.ai/commons": "1.12.3-beta.1"
30
30
  },
31
- "version": "1.12.2"
31
+ "version": "1.12.3-beta.1"
32
32
  }
package/src/actor/data.js CHANGED
@@ -38,10 +38,6 @@ export default class DataActor {
38
38
  this._ac = new AbortController();
39
39
  }
40
40
  this._session = session;
41
-
42
- // TODO: move this to workflow
43
- // reflect session update
44
- this._emitResponse({ session });
45
41
  }
46
42
 
47
43
  async _handleRequest(event) {
@@ -49,16 +45,12 @@ export default class DataActor {
49
45
  if (!this.active) {
50
46
  return;
51
47
  }
52
- const { session } = this._hub.states;
53
- const { session: _, ...request } = event;
54
-
55
- // emit response to update request
56
- this._emitResponse({ session, request });
48
+ const { session, ...request } = event;
57
49
 
58
50
  try {
59
51
  const { signal } = this._ac || {};
60
52
  const options = { ...event.options, signal };
61
- const response = await this._source({ session, ...event, options });
53
+ const response = await this._source({ session, ...request, options });
62
54
  // takes an iterable, either sync or async
63
55
  if (response && response[Symbol.asyncIterator]) {
64
56
  // also emit reponse of the head request, if available
package/src/constants.js CHANGED
@@ -1,7 +1,6 @@
1
1
  export const ATTR_DATA_MISO_UNIT_ID = `data-miso-unit-id`;
2
2
  export const ATTR_DATA_MISO_PRODUCT_ID = `data-miso-product-id`;
3
3
  export const DEFAULT_UNIT_ID = 'default';
4
- export const ORGANIC_QUESTION_SOURCE = '_organic';
5
4
 
6
5
  export const ROLE = Object.freeze({
7
6
  CONTAINER: 'container',
@@ -19,6 +18,7 @@ export const ROLE = Object.freeze({
19
18
  IMAGES: 'images',
20
19
  SOURCES: 'sources',
21
20
  RELATED_RESOURCES: 'related_resources',
21
+ FOLLOW_UP_QUESTIONS: 'follow_up_questions',
22
22
  QUERY_SUGGESTIONS: 'query_suggestions',
23
23
  RELATED_QUESTIONS: 'related_questions',
24
24
  AFFILIATION: 'affiliation',
@@ -42,6 +42,7 @@ const DATA_ROLE_SET = new Set([
42
42
  ROLE.IMAGES,
43
43
  ROLE.SOURCES,
44
44
  ROLE.RELATED_RESOURCES,
45
+ ROLE.FOLLOW_UP_QUESTIONS,
45
46
  ROLE.QUERY_SUGGESTIONS,
46
47
  ROLE.RELATED_QUESTIONS,
47
48
  ROLE.AFFILIATION,
@@ -110,6 +111,12 @@ export const STATUS = Object.freeze({
110
111
  UNANSWERABLE: 'unanswerable',
111
112
  });
112
113
 
114
+ export const QUESTION_SOURCE = Object.freeze({
115
+ ORGANIC: '_organic',
116
+ SUGGESTED_QUESTIONS: '_suggested_questions',
117
+ INLINE_QUESTION_LINK: '_inline_question_link',
118
+ });
119
+
113
120
  export function validateEventType(value) {
114
121
  if (TRACKING_EVENT_TYPES.indexOf(value) < 0) {
115
122
  throw new Error(`Unrecognized event type: ${value}`);
@@ -9,13 +9,16 @@ export const question = 'You asked...';
9
9
  export const sources = 'This reply is based on the following articles';
10
10
 
11
11
  /**
12
- * The phrase displayed right before <miso-query-suggestions>
12
+ * The phrase displayed right before <miso-follow-up-questions>
13
13
  */
14
- export const querySuggestions = 'Related questions you can explore';
14
+ export const followUpQuestions = 'Related questions you can explore';
15
15
 
16
16
  /**
17
17
  * The phrase displayed right before <miso-related-resources>
18
18
  */
19
19
  export const relatedResources = 'Go beyond, and learn more about this topic';
20
20
 
21
+ /**
22
+ * The phrase displayed at the header of <miso-reasoning> box
23
+ */
21
24
  export const thoughts = 'Thoughts';
@@ -18,6 +18,7 @@ class Sections extends _Sections {
18
18
  this.answer(options),
19
19
  this.affiliation(options),
20
20
  this.sources(options),
21
+ this.followUpQuestions(options),
21
22
  container(options, { name: 'bottom-spacing', visibleWhen: 'ongoing' }),
22
23
  ];
23
24
  }
@@ -76,6 +77,16 @@ class Sections extends _Sections {
76
77
  ]);
77
78
  }
78
79
 
80
+ followUpQuestions(options) {
81
+ const { container, phrase } = this._helpers;
82
+ const name = 'follow-up-questions';
83
+ const extraNames = ['query-suggestions']; // backward compatibility (styling)
84
+ return container(options, { name, extraNames, visibleWhen: 'nonempty' }, [
85
+ phrase(options, { name, extraNames, tag: 'h3' }),
86
+ '<miso-follow-up-questions></miso-follow-up-questions>',
87
+ ]);
88
+ }
89
+
79
90
  followUps(options) {
80
91
  const { features = {} } = options;
81
92
  return features.followUpQuestions === false ? '' : `<miso-follow-ups id="miso-ask-combo__follow-ups" class="miso-ask-combo__follow-ups"></miso-follow-ups>`;
@@ -91,16 +102,6 @@ class Sections extends _Sections {
91
102
  return section(options, { name: 'related-resources' }, body);
92
103
  }
93
104
 
94
- querySuggestions(options) {
95
- const { container, phrase } = this._helpers;
96
- const { features = {} } = options;
97
- return features.querySuggestions === false ? '' :
98
- container(options, { name: 'query-suggestions', visibleWhen: 'initial+nonempty' }, [
99
- phrase(options, { name: 'query-suggestions', tag: 'h3' }),
100
- '<miso-query-suggestions></miso-query-suggestions>',
101
- ]);
102
- }
103
-
104
105
  }
105
106
 
106
107
  export const helpers = externalize(new Helpers({ ...CONSTANTS, DEFAULT_PHRASES }));
@@ -122,10 +123,9 @@ export function root(options = {}) {
122
123
 
123
124
  export function followUp(options = {}) {
124
125
  const { normalizeOptions, section, container } = helpers;
125
- const { querySuggestions, answerGroup } = sections;
126
+ const { answerGroup } = sections;
126
127
  options = normalizeOptions(options);
127
128
  return section(options, { name: 'follow-up' }, [
128
- querySuggestions(options),
129
129
  container(options, { name: 'query', visibleWhen: 'initial loading' }, '<miso-query></miso-query>'),
130
130
  ...answerGroup(options),
131
131
  ]);
@@ -94,17 +94,17 @@ export class Helpers {
94
94
 
95
95
  normalizePhraseOptionKey(key) {
96
96
  key = kebabOrSnakeToLowerCamel(key);
97
- if (key === 'relatedQuestions') {
98
- return 'querySuggestions'; // legacy key
97
+ if (key === 'relatedQuestions' || key === 'querySuggestions') {
98
+ return 'followUpQuestions'; // legacy key
99
99
  }
100
100
  return key;
101
101
  }
102
102
 
103
103
  // html //
104
- phrase(options, { name, tag = 'div', inline = false }) {
104
+ phrase(options, { name, extraNames = [], tag = 'div', inline = false }) {
105
105
  const { classPrefix } = options;
106
106
  const mainClass = inline ? `${classPrefix}__phrase-inline` : `${classPrefix}__phrase`;
107
- return `<${tag} class="${mainClass} ${classPrefix}__${name}-phrase">${this.phraseText(options, kebabToLowerCamel(name))}</${tag}>`;
107
+ return `<${tag} class="${mainClass} ${[name, ...extraNames].map(name => `${classPrefix}__${name}-phrase`).join(' ')}">${this.phraseText(options, kebabToLowerCamel(name))}</${tag}>`;
108
108
  }
109
109
 
110
110
  phraseText(options, key) {
@@ -126,8 +126,8 @@ export class Helpers {
126
126
  }, body);
127
127
  }
128
128
 
129
- container({ classPrefix, circledCitationIndex, parentQuestionId }, { name, visibleWhen, trait, logo = false }, body) {
130
- const classes = [`${classPrefix}__${name}-container`];
129
+ container({ classPrefix, circledCitationIndex, parentQuestionId }, { name, extraNames = [], visibleWhen, trait, logo = false }, body) {
130
+ const classes = [name, ...extraNames].map(name => `${classPrefix}__${name}-container`);
131
131
  circledCitationIndex && classes.push('miso-circled-citation-index');
132
132
  return this.element(this._context.CONAINER_TAG, { classes, visibleWhen, trait, logo, parentQuestionId }, body);
133
133
  }
@@ -14,6 +14,7 @@ export default class MisoFollowUpsElement extends MisoContextElement {
14
14
 
15
15
  constructor() {
16
16
  super();
17
+ this._elementToWorkflow = new WeakMap();
17
18
  this._unsubscribes = [];
18
19
  }
19
20
 
@@ -41,21 +42,45 @@ export default class MisoFollowUpsElement extends MisoContextElement {
41
42
  return;
42
43
  }
43
44
 
44
- // 1. when an answer is fully populated, insert a new section for the follow-up question
45
- this._unsubscribes.push(context.on('done', (event) => {
46
- const template = event.workflow._options.resolved.templates.followUp;
47
- if (!template) {
45
+ // 1. call auto-next if unset
46
+ if (!context._autoNextFn) {
47
+ context.autoNext();
48
+ }
49
+
50
+ // 2. when a new workflow is created, insert a new section
51
+ this._unsubscribes.push(context.on('create', (workflow) => {
52
+ const { parentQuestionId } = workflow;
53
+ const template = workflow._options.resolved.templates.followUp;
54
+ if (!parentQuestionId || !template) {
48
55
  return;
49
56
  }
50
- this.insertAdjacentHTML('beforeend', template({ parentQuestionId: event.workflow.questionId }));
57
+ const oldChildCount = this.children.length;
58
+ this.insertAdjacentHTML('beforeend', template({ parentQuestionId }));
59
+ const newChildCount = this.children.length;
60
+ // keep track of the elements
61
+ for (const element of Array.prototype.slice.call(this.children, oldChildCount, newChildCount)) {
62
+ this._elementToWorkflow.set(element, workflow);
63
+ }
51
64
  }));
52
65
 
53
- // 2. if user starts over, clean up existing follow-up questions
54
- this._unsubscribes.push(context.root.on('loading', () => {
55
- // clean up the entire follow-ups section
56
- this.innerHTML = '';
57
- // destroy all follow-up workflows
58
- context.reset({ root: false });
66
+ // 3. when a workflow is loading, scroll into view
67
+ this._unsubscribes.push(context.on('loading', ({ workflow }) => {
68
+ for (const element of this.children) {
69
+ if (this._elementToWorkflow.get(element) === workflow) {
70
+ element.scrollIntoView({ behavior: 'smooth', block: 'start' });
71
+ break;
72
+ }
73
+ }
74
+ }));
75
+
76
+ // 4. when a workflow is destroyed, remove the section
77
+ this._unsubscribes.push(context.on('destroy', ({ workflow }) => {
78
+ for (const element of this.children) {
79
+ if (this._elementToWorkflow.get(element) === workflow) {
80
+ this._elementToWorkflow.delete(element);
81
+ element.remove();
82
+ }
83
+ }
59
84
  }));
60
85
  }
61
86
 
@@ -1,5 +1,6 @@
1
1
  export { default as MisoComponentElement } from './miso-component.js';
2
2
  export { default as MisoQueryElement } from './miso-query.js';
3
+ export { default as MisoFollowUpQuestionsElement } from './miso-follow-up-questions.js';
3
4
  export { default as MisoQuerySuggestionsElement } from './miso-query-suggestions.js';
4
5
  export { default as MisoFeedbackElement } from './miso-feedback.js';
5
6
  export { default as MisoResultsElement } from './miso-results.js';
@@ -0,0 +1,16 @@
1
+ import MisoComponentElement from './miso-component.js';
2
+ import { ROLE } from '../../constants.js';
3
+
4
+ export default class MisoFollowUpQuestionsElement extends MisoComponentElement {
5
+
6
+ constructor() {
7
+ super({
8
+ role: ROLE.FOLLOW_UP_QUESTIONS,
9
+ });
10
+ }
11
+
12
+ static get tagName() {
13
+ return 'miso-follow-up-questions';
14
+ }
15
+
16
+ }
@@ -116,15 +116,14 @@ export default class OptionListLayout extends TemplateBasedLayout {
116
116
  return;
117
117
  }
118
118
  this._trackClick(event, binding);
119
- this._submit(option.text);
119
+ this._select(option.text);
120
120
  }
121
121
 
122
- async _submit(value) {
122
+ async _select(value) {
123
123
  if (!value) {
124
124
  return;
125
125
  }
126
- // TODO: q -> value
127
- this._view.hub.update(fields.query(), { q: value });
126
+ this._view._events.emit('select', { value });
128
127
  }
129
128
 
130
129
  destroy() {
@@ -151,8 +151,9 @@ export default class TypewriterLayout extends ProgressiveLayout {
151
151
  // event //
152
152
  _onClick(event) {
153
153
  const { target } = event;
154
- const citationLinkElement = target.closest(`[data-role="citation-link"]`);
154
+
155
155
  // citation link click
156
+ const citationLinkElement = target.closest(`[data-role="citation-link"]`);
156
157
  if (citationLinkElement) {
157
158
  const index = parseInt(citationLinkElement.dataset.index);
158
159
  if (!Number.isNaN(index)) {
@@ -160,6 +161,18 @@ export default class TypewriterLayout extends ProgressiveLayout {
160
161
  }
161
162
  return;
162
163
  }
164
+
165
+ // follow-up link click
166
+ const followUpLinkElement = target.closest(`[data-role="follow-up-link"]`);
167
+ if (followUpLinkElement) {
168
+ let { q } = followUpLinkElement.dataset;
169
+ q = q ? q.trim() : undefined;
170
+ if (q) {
171
+ this._view._events.emit('follow-up-click', { event, q });
172
+ }
173
+ return;
174
+ }
175
+
163
176
  // generic link click
164
177
  const anchorElement = target.closest('a');
165
178
  if (anchorElement) {
package/src/version.js CHANGED
@@ -1 +1 @@
1
- export default '1.12.2';
1
+ export default '1.12.3-beta.1';
@@ -1,7 +1,7 @@
1
1
  import { API } from '@miso.ai/commons';
2
2
  import Workflow from './base.js';
3
3
  import { fields, FeedbackActor } from '../actor/index.js';
4
- import { ROLE, STATUS, ORGANIC_QUESTION_SOURCE } from '../constants.js';
4
+ import { ROLE, STATUS, QUESTION_SOURCE } from '../constants.js';
5
5
  import { SearchBoxLayout, TextLayout, ListLayout, GalleryLayout, TypewriterLayout, FeedbackLayout, AffiliationLayout } from '../layout/index.js';
6
6
  import { processData as processAffiliationData } from '../affiliation/index.js';
7
7
  import { mergeRolesOptions, autoQuery as autoQueryFn, DEFAULT_AUTO_QUERY_PARAM, DEFAULT_TRACKER_OPTIONS } from './options/index.js';
@@ -174,7 +174,7 @@ export default class AnswerBasedWorkflow extends Workflow {
174
174
  }
175
175
 
176
176
  _writeQuestionSourceToSession(args) {
177
- this.session.meta.question_source = args.qs || ORGANIC_QUESTION_SOURCE; // might be null, not undefined
177
+ this.session.meta.question_source = args.qs || QUESTION_SOURCE.ORGANIC; // might be null, not undefined
178
178
  }
179
179
 
180
180
  _buildPayload() {
@@ -209,8 +209,8 @@ export default class AnswerBasedWorkflow extends Workflow {
209
209
 
210
210
  _handleHeadResponse(data) {}
211
211
 
212
- _defaultProcessData(data) {
213
- data = super._defaultProcessData(data);
212
+ _defaultProcessData(data, oldData) {
213
+ data = super._defaultProcessData(data, oldData);
214
214
  data = processAffiliationData(data);
215
215
  data = writeAnswerStageToMeta(data);
216
216
  return data;
@@ -234,12 +234,12 @@ export default class AnswerBasedWorkflow extends Workflow {
234
234
 
235
235
  const url = new URL(window.location);
236
236
  const currentQuestion = url.searchParams.get(param);
237
- const currentQuestionSource = url.searchParams.get(sourceParam) || ORGANIC_QUESTION_SOURCE;
237
+ const currentQuestionSource = url.searchParams.get(sourceParam) || QUESTION_SOURCE.ORGANIC;
238
238
  if (question === currentQuestion && questionSource === currentQuestionSource) {
239
239
  return;
240
240
  }
241
241
  url.searchParams.set(param, question);
242
- if (questionSource === ORGANIC_QUESTION_SOURCE) {
242
+ if (questionSource === QUESTION_SOURCE.ORGANIC) {
243
243
  url.searchParams.delete(sourceParam);
244
244
  } else {
245
245
  url.searchParams.set(sourceParam, questionSource);
@@ -294,10 +294,13 @@ export default class AnswerBasedWorkflow extends Workflow {
294
294
 
295
295
  // handlers //
296
296
  _onCitationClick({ index, event }) {
297
+ if (event.button !== 0) {
298
+ return; // only left click
299
+ }
297
300
  if (isTracked(event)) {
298
301
  return;
299
302
  }
300
- const { value } = this.states[fields.data()] || {};
303
+ const { value } = this._hub.states[fields.data()] || {};
301
304
  const { sources } = value || {};
302
305
  // index is 1-based
303
306
  const source = sources && sources[index - 1];
@@ -307,11 +310,13 @@ export default class AnswerBasedWorkflow extends Workflow {
307
310
  // TODO: should we track impression as well?
308
311
  markAsTracked(event);
309
312
  // distinguish from regular sources element click
310
- // TODO
311
313
  this._views.trackers.sources.click([source.product_id], { event_target: 'citation-link' });
312
314
  }
313
315
 
314
316
  _onAnswerLinkClick({ event, ...item }) {
317
+ if (event.button !== 0) {
318
+ return; // only left click
319
+ }
315
320
  if (isTracked(event)) {
316
321
  return;
317
322
  }