@nuskin/react-search-sdk 1.0.0-comx-2233.1 → 1.0.0-comx-2233.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.
- package/dist/index.d.mts +17 -12
- package/dist/index.d.ts +17 -12
- package/dist/index.js +86 -34
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +86 -34
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -237,9 +237,9 @@ interface SearchService {
|
|
|
237
237
|
* SearchApiService — Phase 0 lift-and-shift of
|
|
238
238
|
* `header-mfe/src/services/HeaderSearchService.js`.
|
|
239
239
|
*
|
|
240
|
-
* Behaviour
|
|
241
|
-
* `sessionStorage` key format,
|
|
242
|
-
*
|
|
240
|
+
* Behaviour follows the header search contract for URL formats,
|
|
241
|
+
* `sessionStorage` key format, fetch parameter shape, and fallback values.
|
|
242
|
+
* Navigation side effects are left to the consuming router.
|
|
243
243
|
*
|
|
244
244
|
* Two responsibilities were intentionally moved OUT of the SDK to preserve
|
|
245
245
|
* the SDK-narrow boundary:
|
|
@@ -292,10 +292,14 @@ interface ChatServiceOptions {
|
|
|
292
292
|
conversationsUrl?: string;
|
|
293
293
|
/** POST endpoint for sending a message (SSE response). */
|
|
294
294
|
messagesUrl?: string;
|
|
295
|
-
/** X-
|
|
296
|
-
|
|
297
|
-
/**
|
|
298
|
-
|
|
295
|
+
/** X-Sales-AI-App-Key header value (from CS configurations). */
|
|
296
|
+
salesAiAppKey?: string;
|
|
297
|
+
/** X-Sales-AI-Parent-Origin header value (from CS configurations). */
|
|
298
|
+
salesAiParentOrigin?: string;
|
|
299
|
+
/** Market code (e.g., "US", "JP") for scope_context in message requests. */
|
|
300
|
+
market?: string;
|
|
301
|
+
/** Language code (e.g., "en", "ja") for scope_context in message requests. */
|
|
302
|
+
language?: string;
|
|
299
303
|
/** Override fetch for testing. */
|
|
300
304
|
fetchImpl?: typeof fetch | null;
|
|
301
305
|
}
|
|
@@ -385,6 +389,7 @@ interface SpeechRecognitionErrorEventLike {
|
|
|
385
389
|
interface UseVoiceSearchOptions {
|
|
386
390
|
lang?: string;
|
|
387
391
|
onResult: (transcript: string) => void;
|
|
392
|
+
onInterimResult?: (transcript: string) => void;
|
|
388
393
|
onError?: (error: SpeechRecognitionErrorEventLike) => void;
|
|
389
394
|
}
|
|
390
395
|
interface UseVoiceSearchResult {
|
|
@@ -393,7 +398,7 @@ interface UseVoiceSearchResult {
|
|
|
393
398
|
start: () => void;
|
|
394
399
|
stop: () => void;
|
|
395
400
|
}
|
|
396
|
-
declare function useVoiceSearch({ lang, onResult, onError, }: UseVoiceSearchOptions): UseVoiceSearchResult;
|
|
401
|
+
declare function useVoiceSearch({ lang, onInterimResult, onResult, onError, }: UseVoiceSearchOptions): UseVoiceSearchResult;
|
|
397
402
|
|
|
398
403
|
/**
|
|
399
404
|
* Catalog-product normalisation helpers — Phase 0 lift-and-shift of the
|
|
@@ -502,12 +507,12 @@ declare const TRIGGER_PLACEHOLDER = "Product Search";
|
|
|
502
507
|
/**
|
|
503
508
|
* Full path segment for the AI commerce chat SSE endpoint.
|
|
504
509
|
* Appended to the consumer-provided `chatBaseUrl`.
|
|
505
|
-
* Source: https://admin.tst.nuskin.ai/api/analytics/v1/
|
|
510
|
+
* Source: https://admin.tst.nuskin.ai/api/analytics/v1/embed
|
|
506
511
|
*/
|
|
507
|
-
declare const DEFAULT_AI_CHAT_BASE_URL = "https://admin.tst.nuskin.ai/api/analytics/v1/
|
|
512
|
+
declare const DEFAULT_AI_CHAT_BASE_URL = "https://admin.tst.nuskin.ai/api/analytics/v1/embed";
|
|
508
513
|
/** Default POST endpoint for creating an AI chat conversation. */
|
|
509
|
-
declare const DEFAULT_AI_CHAT_CONVERSATIONS_URL = "https://admin.tst.nuskin.ai/api/analytics/v1/
|
|
514
|
+
declare const DEFAULT_AI_CHAT_CONVERSATIONS_URL = "https://admin.tst.nuskin.ai/api/analytics/v1/embed/conversations";
|
|
510
515
|
/** Default POST endpoint for sending AI chat messages. */
|
|
511
|
-
declare const DEFAULT_AI_CHAT_MESSAGES_URL = "https://admin.tst.nuskin.ai/api/analytics/v1/
|
|
516
|
+
declare const DEFAULT_AI_CHAT_MESSAGES_URL = "https://admin.tst.nuskin.ai/api/analytics/v1/embed/messages";
|
|
512
517
|
|
|
513
518
|
export { CATALOG_SUGGESTIONS_PATH, type CatalogSuggestionItem, type ChatMessageResult, type ChatProduct, type ChatService, type ChatServiceOptions, DEBOUNCE_MS, DEFAULT_AI_CHAT_BASE_URL, DEFAULT_AI_CHAT_CONVERSATIONS_URL, DEFAULT_AI_CHAT_MESSAGES_URL, DEFAULT_SEARCH_PATH, DEFAULT_SUGGESTION_PAGE, DEFAULT_SUGGESTION_SIZE, type DidYouMeanItem, type GetMarketConfig, type LocaleParts, type LocationLike, MAX_VISIBLE_SUGGESTIONS, type MarketConfig, type ProductType, RECENT_SEARCHES_DEFAULT_LIMIT, RECENT_SEARCHES_PREFIX, SEARCH_PLACEHOLDER, STOREFRONT_SHOPPING_CONTEXT, type SearchChromeCopy, type SearchHistoryEntry, type SearchHistoryState, type SearchHistoryEntry as SearchHistoryStoreEntry, type SearchService, type SearchServiceOptions, type SuggestionOption, type SuggestionsResponse, TRIGGER_PLACEHOLDER, type UseSearchHistoryResult, type UseSearchSuggestionsOptions, type UseSearchSuggestionsResult, type UseVoiceSearchOptions, type UseVoiceSearchResult, createChatService, createSearchService, getImageFromProperty, getIngredientTerms, getSeeMoreResultsPayload, getVisibleSuggestions, isCartActionDisabled, isSellableProductStatus, isSuggestionDisabled, mapCatalogProductToSuggestion, normalizeDidYouMean, resolveApiGatewayPath, resolveSuggestionImageSrc, useSearchHistory, useSearchHistoryStore, useSearchSuggestions, useVoiceSearch };
|
package/dist/index.d.ts
CHANGED
|
@@ -237,9 +237,9 @@ interface SearchService {
|
|
|
237
237
|
* SearchApiService — Phase 0 lift-and-shift of
|
|
238
238
|
* `header-mfe/src/services/HeaderSearchService.js`.
|
|
239
239
|
*
|
|
240
|
-
* Behaviour
|
|
241
|
-
* `sessionStorage` key format,
|
|
242
|
-
*
|
|
240
|
+
* Behaviour follows the header search contract for URL formats,
|
|
241
|
+
* `sessionStorage` key format, fetch parameter shape, and fallback values.
|
|
242
|
+
* Navigation side effects are left to the consuming router.
|
|
243
243
|
*
|
|
244
244
|
* Two responsibilities were intentionally moved OUT of the SDK to preserve
|
|
245
245
|
* the SDK-narrow boundary:
|
|
@@ -292,10 +292,14 @@ interface ChatServiceOptions {
|
|
|
292
292
|
conversationsUrl?: string;
|
|
293
293
|
/** POST endpoint for sending a message (SSE response). */
|
|
294
294
|
messagesUrl?: string;
|
|
295
|
-
/** X-
|
|
296
|
-
|
|
297
|
-
/**
|
|
298
|
-
|
|
295
|
+
/** X-Sales-AI-App-Key header value (from CS configurations). */
|
|
296
|
+
salesAiAppKey?: string;
|
|
297
|
+
/** X-Sales-AI-Parent-Origin header value (from CS configurations). */
|
|
298
|
+
salesAiParentOrigin?: string;
|
|
299
|
+
/** Market code (e.g., "US", "JP") for scope_context in message requests. */
|
|
300
|
+
market?: string;
|
|
301
|
+
/** Language code (e.g., "en", "ja") for scope_context in message requests. */
|
|
302
|
+
language?: string;
|
|
299
303
|
/** Override fetch for testing. */
|
|
300
304
|
fetchImpl?: typeof fetch | null;
|
|
301
305
|
}
|
|
@@ -385,6 +389,7 @@ interface SpeechRecognitionErrorEventLike {
|
|
|
385
389
|
interface UseVoiceSearchOptions {
|
|
386
390
|
lang?: string;
|
|
387
391
|
onResult: (transcript: string) => void;
|
|
392
|
+
onInterimResult?: (transcript: string) => void;
|
|
388
393
|
onError?: (error: SpeechRecognitionErrorEventLike) => void;
|
|
389
394
|
}
|
|
390
395
|
interface UseVoiceSearchResult {
|
|
@@ -393,7 +398,7 @@ interface UseVoiceSearchResult {
|
|
|
393
398
|
start: () => void;
|
|
394
399
|
stop: () => void;
|
|
395
400
|
}
|
|
396
|
-
declare function useVoiceSearch({ lang, onResult, onError, }: UseVoiceSearchOptions): UseVoiceSearchResult;
|
|
401
|
+
declare function useVoiceSearch({ lang, onInterimResult, onResult, onError, }: UseVoiceSearchOptions): UseVoiceSearchResult;
|
|
397
402
|
|
|
398
403
|
/**
|
|
399
404
|
* Catalog-product normalisation helpers — Phase 0 lift-and-shift of the
|
|
@@ -502,12 +507,12 @@ declare const TRIGGER_PLACEHOLDER = "Product Search";
|
|
|
502
507
|
/**
|
|
503
508
|
* Full path segment for the AI commerce chat SSE endpoint.
|
|
504
509
|
* Appended to the consumer-provided `chatBaseUrl`.
|
|
505
|
-
* Source: https://admin.tst.nuskin.ai/api/analytics/v1/
|
|
510
|
+
* Source: https://admin.tst.nuskin.ai/api/analytics/v1/embed
|
|
506
511
|
*/
|
|
507
|
-
declare const DEFAULT_AI_CHAT_BASE_URL = "https://admin.tst.nuskin.ai/api/analytics/v1/
|
|
512
|
+
declare const DEFAULT_AI_CHAT_BASE_URL = "https://admin.tst.nuskin.ai/api/analytics/v1/embed";
|
|
508
513
|
/** Default POST endpoint for creating an AI chat conversation. */
|
|
509
|
-
declare const DEFAULT_AI_CHAT_CONVERSATIONS_URL = "https://admin.tst.nuskin.ai/api/analytics/v1/
|
|
514
|
+
declare const DEFAULT_AI_CHAT_CONVERSATIONS_URL = "https://admin.tst.nuskin.ai/api/analytics/v1/embed/conversations";
|
|
510
515
|
/** Default POST endpoint for sending AI chat messages. */
|
|
511
|
-
declare const DEFAULT_AI_CHAT_MESSAGES_URL = "https://admin.tst.nuskin.ai/api/analytics/v1/
|
|
516
|
+
declare const DEFAULT_AI_CHAT_MESSAGES_URL = "https://admin.tst.nuskin.ai/api/analytics/v1/embed/messages";
|
|
512
517
|
|
|
513
518
|
export { CATALOG_SUGGESTIONS_PATH, type CatalogSuggestionItem, type ChatMessageResult, type ChatProduct, type ChatService, type ChatServiceOptions, DEBOUNCE_MS, DEFAULT_AI_CHAT_BASE_URL, DEFAULT_AI_CHAT_CONVERSATIONS_URL, DEFAULT_AI_CHAT_MESSAGES_URL, DEFAULT_SEARCH_PATH, DEFAULT_SUGGESTION_PAGE, DEFAULT_SUGGESTION_SIZE, type DidYouMeanItem, type GetMarketConfig, type LocaleParts, type LocationLike, MAX_VISIBLE_SUGGESTIONS, type MarketConfig, type ProductType, RECENT_SEARCHES_DEFAULT_LIMIT, RECENT_SEARCHES_PREFIX, SEARCH_PLACEHOLDER, STOREFRONT_SHOPPING_CONTEXT, type SearchChromeCopy, type SearchHistoryEntry, type SearchHistoryState, type SearchHistoryEntry as SearchHistoryStoreEntry, type SearchService, type SearchServiceOptions, type SuggestionOption, type SuggestionsResponse, TRIGGER_PLACEHOLDER, type UseSearchHistoryResult, type UseSearchSuggestionsOptions, type UseSearchSuggestionsResult, type UseVoiceSearchOptions, type UseVoiceSearchResult, createChatService, createSearchService, getImageFromProperty, getIngredientTerms, getSeeMoreResultsPayload, getVisibleSuggestions, isCartActionDisabled, isSellableProductStatus, isSuggestionDisabled, mapCatalogProductToSuggestion, normalizeDidYouMean, resolveApiGatewayPath, resolveSuggestionImageSrc, useSearchHistory, useSearchHistoryStore, useSearchSuggestions, useVoiceSearch };
|
package/dist/index.js
CHANGED
|
@@ -18,7 +18,7 @@ var DEBOUNCE_MS = 220;
|
|
|
18
18
|
var MAX_VISIBLE_SUGGESTIONS = 6;
|
|
19
19
|
var SEARCH_PLACEHOLDER = "Search by product name, ingredient, benefits, or SKU";
|
|
20
20
|
var TRIGGER_PLACEHOLDER = "Product Search";
|
|
21
|
-
var DEFAULT_AI_CHAT_BASE_URL = "https://admin.tst.nuskin.ai/api/analytics/v1/
|
|
21
|
+
var DEFAULT_AI_CHAT_BASE_URL = "https://admin.tst.nuskin.ai/api/analytics/v1/embed";
|
|
22
22
|
var DEFAULT_AI_CHAT_CONVERSATIONS_URL = `${DEFAULT_AI_CHAT_BASE_URL}/conversations`;
|
|
23
23
|
var DEFAULT_AI_CHAT_MESSAGES_URL = `${DEFAULT_AI_CHAT_BASE_URL}/messages`;
|
|
24
24
|
|
|
@@ -343,17 +343,13 @@ function createSearchService(options = {}) {
|
|
|
343
343
|
if (!trimmedQuery) return void 0;
|
|
344
344
|
addRecentHeaderSearch(localeParts, trimmedQuery, storage);
|
|
345
345
|
const optionHref = resolveOptionHref(option, localeParts);
|
|
346
|
-
if (optionHref
|
|
347
|
-
locationLike.assign(optionHref);
|
|
346
|
+
if (optionHref) {
|
|
348
347
|
return stripLocalePrefix(optionHref, localeParts);
|
|
349
348
|
}
|
|
350
349
|
const targetPath = buildSearchResultsPath(
|
|
351
350
|
trimmedQuery,
|
|
352
351
|
localeParts
|
|
353
352
|
);
|
|
354
|
-
if (locationLike && typeof locationLike.assign === "function") {
|
|
355
|
-
locationLike.assign(targetPath);
|
|
356
|
-
}
|
|
357
353
|
return stripLocalePrefix(targetPath, localeParts);
|
|
358
354
|
},
|
|
359
355
|
activateSuggestionAction(option) {
|
|
@@ -366,9 +362,7 @@ function createSearchService(options = {}) {
|
|
|
366
362
|
option,
|
|
367
363
|
localeParts
|
|
368
364
|
);
|
|
369
|
-
if (optionHref2
|
|
370
|
-
locationLike.assign(optionHref2);
|
|
371
|
-
} else {
|
|
365
|
+
if (!optionHref2) {
|
|
372
366
|
const optionQuery2 = option?.value ?? option?.label ?? option?.identifier ?? "";
|
|
373
367
|
service.submitProductSearch(
|
|
374
368
|
String(optionQuery2)
|
|
@@ -387,9 +381,8 @@ function createSearchService(options = {}) {
|
|
|
387
381
|
}
|
|
388
382
|
}
|
|
389
383
|
const optionHref = resolveOptionHref(option, localeParts);
|
|
390
|
-
if (optionHref
|
|
391
|
-
|
|
392
|
-
return optionHref;
|
|
384
|
+
if (optionHref) {
|
|
385
|
+
return stripLocalePrefix(optionHref, localeParts);
|
|
393
386
|
}
|
|
394
387
|
const optionQuery = option?.value ?? option?.label ?? option?.identifier ?? "";
|
|
395
388
|
return service.submitProductSearch(String(optionQuery));
|
|
@@ -769,6 +762,19 @@ function findClosingFence(content, fromIndex) {
|
|
|
769
762
|
}
|
|
770
763
|
return -1;
|
|
771
764
|
}
|
|
765
|
+
function isRecord(value) {
|
|
766
|
+
return value !== null && typeof value === "object";
|
|
767
|
+
}
|
|
768
|
+
function getRenderProductListItems(event) {
|
|
769
|
+
if (event["type"] !== "render" || event["pattern"] !== "product_list") {
|
|
770
|
+
return [];
|
|
771
|
+
}
|
|
772
|
+
const data = event["data"];
|
|
773
|
+
if (!isRecord(data) || !Array.isArray(data["items"])) {
|
|
774
|
+
return [];
|
|
775
|
+
}
|
|
776
|
+
return data["items"].filter(isRecord);
|
|
777
|
+
}
|
|
772
778
|
function extractProductsFromMarkdown(content) {
|
|
773
779
|
const products = [];
|
|
774
780
|
const summaryParts = [];
|
|
@@ -797,7 +803,8 @@ function extractProductsFromMarkdown(content) {
|
|
|
797
803
|
if (Array.isArray(parsed)) {
|
|
798
804
|
products.push(...parsed);
|
|
799
805
|
}
|
|
800
|
-
} catch {
|
|
806
|
+
} catch (e) {
|
|
807
|
+
console.error("Failed to parse products block:", e);
|
|
801
808
|
}
|
|
802
809
|
summaryParts.push(content.slice(summaryCursor, start));
|
|
803
810
|
summaryCursor = end + MARKDOWN_CLOSING_FENCE.length;
|
|
@@ -810,8 +817,10 @@ function createChatService(options) {
|
|
|
810
817
|
const {
|
|
811
818
|
conversationsUrl = DEFAULT_AI_CHAT_CONVERSATIONS_URL,
|
|
812
819
|
messagesUrl = DEFAULT_AI_CHAT_MESSAGES_URL,
|
|
813
|
-
|
|
814
|
-
|
|
820
|
+
salesAiAppKey,
|
|
821
|
+
salesAiParentOrigin,
|
|
822
|
+
market,
|
|
823
|
+
language,
|
|
815
824
|
fetchImpl: fetchImplOption
|
|
816
825
|
} = options;
|
|
817
826
|
let fetchImpl;
|
|
@@ -824,22 +833,30 @@ function createChatService(options) {
|
|
|
824
833
|
throw new Error("ChatService: no fetch implementation available");
|
|
825
834
|
};
|
|
826
835
|
}
|
|
827
|
-
function buildHeaders(extra) {
|
|
836
|
+
function buildHeaders(extra, includeJsonContentType = true) {
|
|
828
837
|
const headers = {
|
|
829
|
-
"Content-Type": "application/json",
|
|
830
838
|
...extra
|
|
831
839
|
};
|
|
832
|
-
if (
|
|
833
|
-
|
|
834
|
-
|
|
840
|
+
if (includeJsonContentType) {
|
|
841
|
+
headers["Content-Type"] = "application/json";
|
|
842
|
+
}
|
|
843
|
+
const accessToken = typeof window !== "undefined" ? window.localStorage?.getItem("accessToken") : null;
|
|
844
|
+
if (accessToken) {
|
|
845
|
+
headers["Authorization"] = `Bearer ${accessToken}`;
|
|
846
|
+
}
|
|
847
|
+
if (salesAiAppKey) {
|
|
848
|
+
headers["X-Sales-AI-App-Key"] = salesAiAppKey;
|
|
849
|
+
}
|
|
850
|
+
if (salesAiParentOrigin) {
|
|
851
|
+
headers["X-Sales-AI-Parent-Origin"] = salesAiParentOrigin;
|
|
852
|
+
}
|
|
835
853
|
return headers;
|
|
836
854
|
}
|
|
837
855
|
return {
|
|
838
|
-
async createConversation(
|
|
856
|
+
async createConversation() {
|
|
839
857
|
const res = await fetchImpl(conversationsUrl, {
|
|
840
858
|
method: "POST",
|
|
841
|
-
headers: buildHeaders()
|
|
842
|
-
body: JSON.stringify({ title })
|
|
859
|
+
headers: buildHeaders(void 0, false)
|
|
843
860
|
});
|
|
844
861
|
if (!res.ok) {
|
|
845
862
|
throw new Error(
|
|
@@ -856,14 +873,26 @@ function createChatService(options) {
|
|
|
856
873
|
return id;
|
|
857
874
|
},
|
|
858
875
|
async sendMessage(conversationId, content, onChunk) {
|
|
876
|
+
const requestBody = {
|
|
877
|
+
conversation_id: conversationId,
|
|
878
|
+
content
|
|
879
|
+
};
|
|
880
|
+
if (market || language) {
|
|
881
|
+
requestBody.scope_context = {
|
|
882
|
+
market: market ?? "US",
|
|
883
|
+
language: language ?? "en"
|
|
884
|
+
};
|
|
885
|
+
}
|
|
886
|
+
requestBody.client_capabilities = {
|
|
887
|
+
patterns: {
|
|
888
|
+
product_list: [1],
|
|
889
|
+
follow_up: [1]
|
|
890
|
+
}
|
|
891
|
+
};
|
|
859
892
|
const res = await fetchImpl(messagesUrl, {
|
|
860
893
|
method: "POST",
|
|
861
|
-
headers: buildHeaders({ Accept: "
|
|
862
|
-
body: JSON.stringify(
|
|
863
|
-
conversation_id: conversationId,
|
|
864
|
-
content,
|
|
865
|
-
compact: false
|
|
866
|
-
})
|
|
894
|
+
headers: buildHeaders({ Accept: "text/event-stream" }),
|
|
895
|
+
body: JSON.stringify(requestBody)
|
|
867
896
|
});
|
|
868
897
|
if (!res.ok) {
|
|
869
898
|
throw new Error(
|
|
@@ -871,6 +900,7 @@ function createChatService(options) {
|
|
|
871
900
|
);
|
|
872
901
|
}
|
|
873
902
|
let accumulated = "";
|
|
903
|
+
const renderedProducts = [];
|
|
874
904
|
const parser = createParser({
|
|
875
905
|
onEvent(event) {
|
|
876
906
|
const data = event.data;
|
|
@@ -879,6 +909,9 @@ function createChatService(options) {
|
|
|
879
909
|
const parsed = JSON.parse(data);
|
|
880
910
|
const parsedType = parsed["type"];
|
|
881
911
|
const type = typeof parsedType === "string" ? parsedType : "";
|
|
912
|
+
renderedProducts.push(
|
|
913
|
+
...getRenderProductListItems(parsed)
|
|
914
|
+
);
|
|
882
915
|
if (type === "content" && typeof parsed["text"] === "string") {
|
|
883
916
|
const text = parsed["text"];
|
|
884
917
|
accumulated += text;
|
|
@@ -906,7 +939,12 @@ function createChatService(options) {
|
|
|
906
939
|
parser.feed(rawBody);
|
|
907
940
|
}
|
|
908
941
|
const { summary, products } = extractProductsFromMarkdown(accumulated);
|
|
909
|
-
|
|
942
|
+
const finalProducts = renderedProducts.length > 0 ? renderedProducts : products;
|
|
943
|
+
return {
|
|
944
|
+
summary,
|
|
945
|
+
products: finalProducts,
|
|
946
|
+
rawContent: accumulated
|
|
947
|
+
};
|
|
910
948
|
}
|
|
911
949
|
};
|
|
912
950
|
}
|
|
@@ -1653,6 +1691,7 @@ function getSpeechRecognition() {
|
|
|
1653
1691
|
}
|
|
1654
1692
|
function useVoiceSearch({
|
|
1655
1693
|
lang,
|
|
1694
|
+
onInterimResult,
|
|
1656
1695
|
onResult,
|
|
1657
1696
|
onError
|
|
1658
1697
|
}) {
|
|
@@ -1670,11 +1709,24 @@ function useVoiceSearch({
|
|
|
1670
1709
|
stop();
|
|
1671
1710
|
const rec = new SR();
|
|
1672
1711
|
rec.lang = lang ?? navigator.language ?? "en-US";
|
|
1673
|
-
rec.interimResults =
|
|
1712
|
+
rec.interimResults = true;
|
|
1674
1713
|
rec.maxAlternatives = 1;
|
|
1675
1714
|
rec.onresult = (event) => {
|
|
1676
|
-
|
|
1677
|
-
|
|
1715
|
+
let finalTranscript = "";
|
|
1716
|
+
let interimTranscript = "";
|
|
1717
|
+
const startIndex = event.resultIndex ?? 0;
|
|
1718
|
+
for (let index = startIndex; index < event.results.length; index += 1) {
|
|
1719
|
+
const result = event.results[index];
|
|
1720
|
+
const transcript = result?.[0]?.transcript ?? "";
|
|
1721
|
+
if (!transcript) continue;
|
|
1722
|
+
if (result?.isFinal) {
|
|
1723
|
+
finalTranscript += transcript;
|
|
1724
|
+
} else {
|
|
1725
|
+
interimTranscript += transcript;
|
|
1726
|
+
}
|
|
1727
|
+
}
|
|
1728
|
+
if (interimTranscript) onInterimResult?.(interimTranscript);
|
|
1729
|
+
if (finalTranscript) onResult(finalTranscript);
|
|
1678
1730
|
};
|
|
1679
1731
|
rec.onerror = (event) => {
|
|
1680
1732
|
onError?.(event);
|
|
@@ -1684,7 +1736,7 @@ function useVoiceSearch({
|
|
|
1684
1736
|
recognitionRef.current = rec;
|
|
1685
1737
|
rec.start();
|
|
1686
1738
|
setListening(true);
|
|
1687
|
-
}, [lang, onResult, onError, stop]);
|
|
1739
|
+
}, [lang, onInterimResult, onResult, onError, stop]);
|
|
1688
1740
|
return { listening, supported, start, stop };
|
|
1689
1741
|
}
|
|
1690
1742
|
|