@coveo/quantic 3.28.2-pre.e4e7bfb159 → 3.28.2-pre.eb011b8318

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.
@@ -1001,7 +1001,7 @@
1001
1001
  "access": "@api",
1002
1002
  "description": "<p>A list of fields to fetch with the citations used to generate the answer.</p>",
1003
1003
  "required": false,
1004
- "defaultValue": "`'sfid,sfkbid,sfkavid'`",
1004
+ "defaultValue": "`'sfid,sfkbid,sfkavid,filetype'`",
1005
1005
  "type": "string"
1006
1006
  },
1007
1007
  {
@@ -1039,6 +1039,15 @@
1039
1039
  "required": false,
1040
1040
  "defaultValue": "{250}",
1041
1041
  "type": "number"
1042
+ },
1043
+ {
1044
+ "name": "disableCitationAnchoring",
1045
+ "attribute": "disable-citation-anchoring",
1046
+ "access": "@api",
1047
+ "description": "<p>Whether to disable citation anchoring.</p>",
1048
+ "required": false,
1049
+ "defaultValue": "false",
1050
+ "type": "boolean"
1042
1051
  }
1043
1052
  ]
1044
1053
  },
@@ -7,6 +7,7 @@
7
7
  aria-label={citationTitle}
8
8
  href={hrefValue}
9
9
  target="_blank"
10
+ rel="noopener"
10
11
  onmouseenter={handleMouseEnter}
11
12
  onmouseleave={handleMouseLeave}
12
13
  onclick={handleClick}
@@ -1,4 +1,4 @@
1
- import {LinkUtils} from 'c/quanticUtils';
1
+ import {LinkUtils, generateTextFragmentUrl} from 'c/quanticUtils';
2
2
  import {NavigationMixin} from 'lightning/navigation';
3
3
  import {LightningElement, api} from 'lwc';
4
4
 
@@ -18,7 +18,7 @@ export default class QuanticCitation extends NavigationMixin(LightningElement) {
18
18
  /**
19
19
  * The citation item information.
20
20
  * @api
21
- * @type {{title: string, index: number, text: string, clickUri: string, fields: object}}
21
+ * @type {{title: string, index: number, text: string, uri: string, clickUri: string, fields: object}}
22
22
  */
23
23
  @api citation;
24
24
  /**
@@ -27,6 +27,13 @@ export default class QuanticCitation extends NavigationMixin(LightningElement) {
27
27
  * @type {InteractiveCitation}
28
28
  */
29
29
  @api interactiveCitation;
30
+ /**
31
+ * Whether to disable citation anchoring.
32
+ * @api
33
+ * @type {boolean}
34
+ * @default false
35
+ */
36
+ @api disableCitationAnchoring = false;
30
37
 
31
38
  /** @type {Object} */
32
39
  timeout;
@@ -42,6 +49,14 @@ export default class QuanticCitation extends NavigationMixin(LightningElement) {
42
49
  isInitialRender = true;
43
50
  /** @type {string} */
44
51
  salesforceRecordUrl;
52
+ /** @type {boolean} */
53
+ isWithTextFragment = false;
54
+
55
+ connectedCallback() {
56
+ const fileType = this.citation?.fields?.filetype;
57
+ this.isWithTextFragment =
58
+ !this.disableCitationAnchoring || fileType !== 'html' || !this.text;
59
+ }
45
60
 
46
61
  renderedCallback() {
47
62
  if (this.isInitialRender) {
@@ -111,7 +126,9 @@ export default class QuanticCitation extends NavigationMixin(LightningElement) {
111
126
  }
112
127
 
113
128
  handleClick(event) {
114
- if (this.isSalesforceLink) {
129
+ // Only apply the Salesforce navigation using the mixin for Salesforce documents and when citation anchoring is disabled.
130
+ // Otherwise we rely on the default behavior of the browser with a `hrefValue`.
131
+ if (this.isSalesforceLink && this.disableCitationAnchoring) {
115
132
  event.preventDefault();
116
133
  this.navigateToSalesforceRecord(event);
117
134
  }
@@ -145,10 +162,9 @@ export default class QuanticCitation extends NavigationMixin(LightningElement) {
145
162
  }
146
163
 
147
164
  get hrefValue() {
148
- if (this.isSalesforceLink) {
149
- return this.salesforceRecordUrl;
150
- }
151
- return this.clickUri;
165
+ return this.isWithTextFragment
166
+ ? generateTextFragmentUrl(this.sourceUri, this.text)
167
+ : this.sourceUri;
152
168
  }
153
169
 
154
170
  /**
@@ -180,4 +196,10 @@ export default class QuanticCitation extends NavigationMixin(LightningElement) {
180
196
  get index() {
181
197
  return this.citation?.index;
182
198
  }
199
+
200
+ get sourceUri() {
201
+ return this.isSalesforceLink
202
+ ? this.salesforceRecordUrl
203
+ : (this.clickUri ?? this.citation?.uri);
204
+ }
183
205
  }
@@ -49,6 +49,7 @@ const MAX_VALID_COLLAPSED_HEIGHT = 500;
49
49
  const MIN_VALID_COLLAPSED_HEIGHT = 150;
50
50
 
51
51
  const GENERATED_ANSWER_DATA_KEY = 'coveo-generated-answer-data';
52
+ const DEFAULT_CITATION_FIELDS = ['sfid', 'sfkbid', 'sfkavid', 'filetype'];
52
53
 
53
54
  /**
54
55
  * The `QuanticGeneratedAnswer` component automatically generates an answer using Coveo machine learning models to answer the query executed by the user.
@@ -71,9 +72,9 @@ export default class QuanticGeneratedAnswer extends LightningElement {
71
72
  * A list of fields to fetch with the citations used to generate the answer.
72
73
  * @api
73
74
  * @type {string}
74
- * @defaultValue `'sfid,sfkbid,sfkavid'`
75
+ * @defaultValue `'sfid,sfkbid,sfkavid,filetype'`
75
76
  */
76
- @api fieldsToIncludeInCitations = 'sfid,sfkbid,sfkavid';
77
+ @api fieldsToIncludeInCitations = 'sfid,sfkbid,sfkavid,filetype';
77
78
  /**
78
79
  * Whether the generated answer should be collapsible when it exceeds the maximum height of 250px.
79
80
  * @api
@@ -118,6 +119,13 @@ export default class QuanticGeneratedAnswer extends LightningElement {
118
119
  this._maxCollapsedHeight = DEFAULT_COLLAPSED_HEIGHT;
119
120
  }
120
121
  }
122
+ /**
123
+ * Whether to disable citation anchoring.
124
+ * @api
125
+ * @type {boolean}
126
+ * @default false
127
+ */
128
+ @api disableCitationAnchoring = false;
121
129
 
122
130
  labels = {
123
131
  generatedAnswerForYou,
@@ -530,10 +538,17 @@ export default class QuanticGeneratedAnswer extends LightningElement {
530
538
  }
531
539
 
532
540
  get citationFields() {
533
- return this.fieldsToIncludeInCitations
534
- ?.split(',')
535
- .map((field) => field.trim())
536
- .filter((field) => field.length > 0);
541
+ const userCitationFields =
542
+ this.fieldsToIncludeInCitations
543
+ ?.split(',')
544
+ .map((field) => field.trim())
545
+ .filter((field) => field.length > 0) || [];
546
+
547
+ const combinedCitationFields = [
548
+ ...DEFAULT_CITATION_FIELDS,
549
+ ...userCitationFields,
550
+ ];
551
+ return [...new Set(combinedCitationFields)];
537
552
  }
538
553
 
539
554
  get shouldShowDisclaimer() {
@@ -65,6 +65,7 @@
65
65
  data-testid="generated-answer__citations"
66
66
  citations={citations}
67
67
  citation-hover-handler={handleCitationHover}
68
+ disable-citation-anchoring={disableCitationAnchoring}
68
69
  ></c-quantic-source-citations>
69
70
  </div>
70
71
  </template>
@@ -10,6 +10,7 @@
10
10
  citation={citation.data}
11
11
  interactive-citation={citation.interactiveCitation}
12
12
  onquantic__citationhover={citation.handleCitationHover}
13
+ disable-citation-anchoring={disableCitationAnchoring}
13
14
  ></c-quantic-citation>
14
15
  </template>
15
16
  </ol>
@@ -37,6 +37,13 @@ export default class QuanticSourceCitations extends LightningElement {
37
37
  * @type {function}
38
38
  */
39
39
  @api citationHoverHandler;
40
+ /**
41
+ * Whether to disable citation anchoring.
42
+ * @api
43
+ * @type {boolean}
44
+ * @default false
45
+ */
46
+ @api disableCitationAnchoring = false;
40
47
 
41
48
  /** @type {AnyHeadless} */
42
49
  headless;
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Extracts a text to highlight from a given text.
3
+ * It tries to extract the first sentence, and if it fails, it returns the first few words.
4
+ * @param {string} text The text from which to extract the text to highlight.
5
+ * @returns {string} The extracted text to highlight.
6
+ */
7
+ export function extractTextToHighlight(text) {
8
+ const fallbackWordCount = 5;
9
+
10
+ const trimmedText = text.trim();
11
+ if (!trimmedText || typeof trimmedText !== 'string') {
12
+ return '';
13
+ }
14
+
15
+ const sentenceRegex =
16
+ /(?<=^|\p{P}\s)\p{P}?\p{Lu}.*?[.!?]+(?=\s+(?:\p{Lu}|\d)|$)/gu;
17
+ const sentences = trimmedText.match(sentenceRegex);
18
+
19
+ if (Array.isArray(sentences) && sentences?.length > 0) {
20
+ return sentences[0].trim();
21
+ }
22
+
23
+ return trimmedText.split(/\s+/).slice(0, fallbackWordCount).join(' ');
24
+ }
25
+
26
+ /**
27
+ * Generates an encoded text fragment URL for the citation.
28
+ * This URL will highlight the text in the citation if it is an HTML file.
29
+ * @param {string} uri
30
+ * @param {string} text
31
+ * @returns {string}
32
+ * @example
33
+ * generateTextFragmentUrl('https://example.com', 'This is a sample text.');
34
+ * // Returns: 'https://example.com#:~:text=This%20is%20a%20sample%20text.'
35
+ */
36
+ export function generateTextFragmentUrl(uri, text) {
37
+ const highlight = extractTextToHighlight(text);
38
+ const encodedTextFragment = encodeURIComponent(highlight).replace(
39
+ /-/g,
40
+ '%2D'
41
+ );
42
+ return `${uri}#:~:text=${encodedTextFragment}`;
43
+ }
@@ -35,6 +35,7 @@ import pastYear_plural from '@salesforce/label/c.quantic_PastYear_plural';
35
35
  export * from './recentQueriesUtils';
36
36
  export * from './markdownUtils';
37
37
  export * from './facetDependenciesUtils';
38
+ export * from './citationAnchoringUtils';
38
39
 
39
40
  export class Debouncer {
40
41
  _timeout;