@getlupa/client 1.18.5 → 1.19.0

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.
@@ -12586,7 +12586,7 @@ const getNormalizedString = (str) => {
12586
12586
  return "";
12587
12587
  }
12588
12588
  const transformedStr = typeof str === "string" ? str : str.toString();
12589
- return transformedStr.normalize === void 0 ? (_a = transformedStr.toLocaleLowerCase()) == null ? void 0 : _a.trim() : (_b = transformedStr.toLocaleLowerCase().normalize("NFKD").replace(/[^\w\s.-_/]/g, "")) == null ? void 0 : _b.trim();
12589
+ return transformedStr.normalize === void 0 ? (_a = transformedStr.toLocaleLowerCase()) == null ? void 0 : _a.trim() : (_b = transformedStr.toLocaleLowerCase().normalize("NFKD").replace(/[^\p{L}\p{N}\s]/gu, "")) == null ? void 0 : _b.trim();
12590
12590
  };
12591
12591
  const getTransformedString = (str) => {
12592
12592
  var _a, _b;
@@ -14326,8 +14326,8 @@ const Env = {
14326
14326
  staging: "https://api.staging.lupasearch.com/v1/"
14327
14327
  };
14328
14328
  const VoiceServiceEnv = {
14329
- production: "ws://voice.lupasearch.com:3001",
14330
- staging: "ws://voice.lupasearch.dev:3001"
14329
+ production: "wss://voice.lupasearch.com",
14330
+ staging: "wss://voice.lupasearch.dev"
14331
14331
  };
14332
14332
  const DEFAULT_REQUEST_CONFIG = {
14333
14333
  method: "POST",
@@ -14368,7 +14368,7 @@ const _sfc_main$1z = /* @__PURE__ */ defineComponent({
14368
14368
  }
14369
14369
  };
14370
14370
  const startProgressBar = () => {
14371
- if (!progressBar.value || !props.isRecording) {
14371
+ if (!progressBar.value) {
14372
14372
  return;
14373
14373
  }
14374
14374
  const duration = props.timesliceLimit * props.timeSliceLength;
@@ -14426,6 +14426,7 @@ function useVoiceRecorder(options) {
14426
14426
  const mediaStream = ref(null);
14427
14427
  const mediaRecorder = ref(null);
14428
14428
  const isRecording = ref(false);
14429
+ const isSocketReady = ref(false);
14429
14430
  const errorRef = ref(null);
14430
14431
  const transcription = ref("");
14431
14432
  const timeSliceLength = computed(() => {
@@ -14438,35 +14439,51 @@ function useVoiceRecorder(options) {
14438
14439
  });
14439
14440
  const initSocket = (url, onMessage) => {
14440
14441
  socket.value = new WebSocket(url);
14441
- socket.value.onopen = () => __async2(this, null, function* () {
14442
+ socket.value.onmessage = (event) => __async2(this, null, function* () {
14442
14443
  var _a;
14443
- if (((_a = mediaRecorder.value) == null ? void 0 : _a.state) !== "recording") {
14444
- yield startRecording();
14444
+ try {
14445
+ const msg = JSON.parse(event.data);
14446
+ if (msg.event === "ready") {
14447
+ if (((_a = mediaRecorder.value) == null ? void 0 : _a.state) !== "recording") {
14448
+ try {
14449
+ isSocketReady.value = true;
14450
+ yield startRecording();
14451
+ } catch (error) {
14452
+ console.error("Recording failed to start:", error);
14453
+ closeSocket();
14454
+ }
14455
+ }
14456
+ } else if (msg.event === "transcription") {
14457
+ transcription.value = msg.transcription;
14458
+ onMessage == null ? void 0 : onMessage(msg.transcription);
14459
+ stopSocketConnection();
14460
+ } else if (msg.event === "error") {
14461
+ errorRef.value = msg.message || "An error occurred during transcription";
14462
+ isSocketReady.value = false;
14463
+ stopRecording();
14464
+ closeSocket();
14465
+ }
14466
+ } catch (error) {
14467
+ console.error("Error processing WebSocket message:", error);
14445
14468
  }
14446
14469
  });
14447
- socket.value.onmessage = (event) => {
14448
- const msg = JSON.parse(event.data);
14449
- if (msg.event === "transcription") {
14450
- transcription.value = msg.transcription;
14451
- onMessage == null ? void 0 : onMessage(msg.transcription);
14452
- stopSocketConnection();
14453
- } else if (msg.event === "error") {
14454
- errorRef.value = "Server error during transcription";
14455
- stopRecording();
14470
+ socket.value.onclose = (event) => {
14471
+ if (event.code === 4001) {
14472
+ errorRef.value = event.reason || "Connection closed by server";
14456
14473
  }
14457
- };
14458
- socket.value.onclose = () => {
14459
14474
  stopRecording();
14460
14475
  };
14461
14476
  socket.value.onerror = () => {
14462
- stopRecording();
14463
14477
  errorRef.value = "Service connection error";
14478
+ stopRecording();
14464
14479
  };
14465
14480
  };
14466
14481
  const onMediaRecorderDataAvailable = (event) => __async2(this, null, function* () {
14467
14482
  var _a, _b;
14468
- if (((_a = mediaRecorder.value) == null ? void 0 : _a.state) !== "recording")
14483
+ if (!isSocketReady.value || ((_a = socket.value) == null ? void 0 : _a.readyState) !== WebSocket.OPEN) {
14484
+ console.warn("Skipping audio chunk: socket not ready.");
14469
14485
  return;
14486
+ }
14470
14487
  const audioBuffer = yield event.data.arrayBuffer();
14471
14488
  const header = buildSocketMessageFrameHeader("audio-chunk", audioBuffer.byteLength);
14472
14489
  const buffer = new Uint8Array(header.length + audioBuffer.byteLength);
@@ -14475,20 +14492,29 @@ function useVoiceRecorder(options) {
14475
14492
  (_b = socket.value) == null ? void 0 : _b.send(buffer);
14476
14493
  });
14477
14494
  const startRecording = () => __async2(this, null, function* () {
14478
- mediaStream.value = yield navigator.mediaDevices.getUserMedia({
14479
- video: false,
14480
- audio: {
14481
- channelCount: 1,
14482
- echoCancellation: true,
14483
- sampleRate: 16e3
14495
+ var _a, _b;
14496
+ try {
14497
+ mediaStream.value = yield navigator.mediaDevices.getUserMedia({
14498
+ video: false,
14499
+ audio: {
14500
+ channelCount: 1,
14501
+ echoCancellation: true,
14502
+ sampleRate: options.sampleRate || 16e3
14503
+ }
14504
+ });
14505
+ mediaRecorder.value = new MediaRecorder(mediaStream.value, {
14506
+ mimeType: "audio/webm; codecs=opus"
14507
+ });
14508
+ mediaRecorder.value.ondataavailable = onMediaRecorderDataAvailable;
14509
+ mediaRecorder.value.start(timeSliceLength.value);
14510
+ isRecording.value = true;
14511
+ } catch (error) {
14512
+ if (error.name === "NotAllowedError") {
14513
+ errorRef.value = ((_a = options.labels) == null ? void 0 : _a.microphoneNotAllowed) || "Microphone access denied. Please allow microphone access in your browser settings.";
14514
+ } else if (error.name === "NotFoundError") {
14515
+ errorRef.value = ((_b = options.labels) == null ? void 0 : _b.microphoneNotFound) || "No microphone found. Please connect a microphone and try again.";
14484
14516
  }
14485
- });
14486
- mediaRecorder.value = new MediaRecorder(mediaStream.value, {
14487
- mimeType: "audio/webm; codecs=opus"
14488
- });
14489
- mediaRecorder.value.ondataavailable = onMediaRecorderDataAvailable;
14490
- mediaRecorder.value.start(timeSliceLength.value);
14491
- isRecording.value = true;
14517
+ }
14492
14518
  });
14493
14519
  const stopRecording = () => {
14494
14520
  var _a, _b;
@@ -14511,6 +14537,7 @@ function useVoiceRecorder(options) {
14511
14537
  var _a;
14512
14538
  (_a = socket.value) == null ? void 0 : _a.close();
14513
14539
  socket.value = null;
14540
+ isSocketReady.value = false;
14514
14541
  };
14515
14542
  const reset = () => {
14516
14543
  stopRecording();
@@ -14518,9 +14545,11 @@ function useVoiceRecorder(options) {
14518
14545
  transcription.value = "";
14519
14546
  errorRef.value = null;
14520
14547
  isRecording.value = false;
14548
+ isSocketReady.value = false;
14521
14549
  };
14522
14550
  return {
14523
14551
  isRecording,
14552
+ isSocketReady,
14524
14553
  transcription,
14525
14554
  errorRef,
14526
14555
  initSocket,
@@ -14554,6 +14583,7 @@ const _sfc_main$1y = /* @__PURE__ */ defineComponent({
14554
14583
  const optionsStore = useOptionsStore();
14555
14584
  const {
14556
14585
  isRecording,
14586
+ isSocketReady,
14557
14587
  transcription,
14558
14588
  errorRef,
14559
14589
  initSocket,
@@ -14579,20 +14609,28 @@ const _sfc_main$1y = /* @__PURE__ */ defineComponent({
14579
14609
  return (_a = props.options.labels) != null ? _a : {};
14580
14610
  });
14581
14611
  const description = computed(() => {
14582
- var _a, _b, _c;
14612
+ var _a, _b;
14583
14613
  if (errorRef.value) {
14584
- return (_a = labels.value.serviceError) != null ? _a : errorRef.value;
14614
+ return errorRef.value;
14585
14615
  }
14586
- if (!isRecording.value) {
14587
- return (_b = labels.value.microphoneOff) != null ? _b : "Microphone is off. Try again.";
14616
+ if (!isSocketReady.value || !isRecording.value) {
14617
+ return (_a = labels.value.connecting) != null ? _a : "Connecting...";
14588
14618
  }
14589
- return (_c = labels.value.listening) != null ? _c : "Listening...";
14619
+ return (_b = labels.value.listening) != null ? _b : "Listening...";
14590
14620
  });
14591
14621
  watch(transcription, (newValue) => {
14592
14622
  emit2("transcript-update", newValue);
14593
14623
  });
14594
- const handleRecordingButtonClick = () => {
14624
+ watch(isRecording, (newVal) => {
14595
14625
  var _a, _b;
14626
+ if (newVal === true) {
14627
+ (_a = voiceSearchProgressBar.value) == null ? void 0 : _a.startProgressBar();
14628
+ } else {
14629
+ (_b = voiceSearchProgressBar.value) == null ? void 0 : _b.stopProgressBar();
14630
+ }
14631
+ });
14632
+ const handleRecordingButtonClick = () => {
14633
+ var _a;
14596
14634
  if (isRecording.value) {
14597
14635
  setTimeout(() => {
14598
14636
  stopSocketConnection();
@@ -14606,7 +14644,6 @@ const _sfc_main$1y = /* @__PURE__ */ defineComponent({
14606
14644
  );
14607
14645
  const socketUrl = `${voiceServiceUrl}?clientId=${clientId.value}&queryKey=${props.options.queryKey}&languageCode=${(_a = props.options.language) != null ? _a : "en-US"}&connectionType=write-first`;
14608
14646
  initSocket(socketUrl);
14609
- (_b = voiceSearchProgressBar.value) == null ? void 0 : _b.startProgressBar();
14610
14647
  setTimeout(() => {
14611
14648
  stopSocketConnection();
14612
14649
  handleOnStopEvent();
@@ -24289,6 +24326,15 @@ const processDisplayCondition = (displayCondition, doc2 = {}) => {
24289
24326
  return false;
24290
24327
  }
24291
24328
  };
24329
+ function shouldDisplay(displayOpt, doc2) {
24330
+ if (!displayOpt)
24331
+ return true;
24332
+ if (typeof displayOpt === "function") {
24333
+ return displayOpt(doc2);
24334
+ }
24335
+ const rules = Array.isArray(displayOpt) ? displayOpt : [displayOpt];
24336
+ return rules.every((rule2) => processDisplayCondition(rule2, doc2));
24337
+ }
24292
24338
  const checkHasFullImageUrl = (imageUrl) => typeof imageUrl === "string" && (imageUrl.indexOf("http://") === 0 || imageUrl.indexOf("https://") === 0);
24293
24339
  const computeImageUrl = (imageUrl, rootImageUrl) => {
24294
24340
  const mainUrl = Array.isArray(imageUrl) ? imageUrl[0] : imageUrl;
@@ -25105,7 +25151,7 @@ const _sfc_main$1g = /* @__PURE__ */ defineComponent(__spreadProps2(__spreadValu
25105
25151
  if (!element.display) {
25106
25152
  return true;
25107
25153
  }
25108
- return typeof element.display === "function" ? element.display(item) : processDisplayCondition(element.display, item);
25154
+ return shouldDisplay(element.display, item);
25109
25155
  });
25110
25156
  const enhancedItem = computed(() => {
25111
25157
  var _a, _b, _c, _d;
@@ -25428,7 +25474,7 @@ const _sfc_main$19 = /* @__PURE__ */ defineComponent(__spreadProps2(__spreadValu
25428
25474
  if (!element.display) {
25429
25475
  return true;
25430
25476
  }
25431
- return typeof element.display === "function" ? element.display(item) : processDisplayCondition(element.display, item);
25477
+ return shouldDisplay(element.display, item);
25432
25478
  };
25433
25479
  const badges = computed(() => {
25434
25480
  if (!props.options.elements) {
@@ -25506,10 +25552,6 @@ const _sfc_main$18 = /* @__PURE__ */ defineComponent({
25506
25552
  const badgeOptions = computed(() => {
25507
25553
  return __spreadProps2(__spreadValues2({}, props.panelOptions.badges), { product: props.item });
25508
25554
  });
25509
- const imageElements = computed(() => {
25510
- var _a, _b;
25511
- return (_b = (_a = props.panelOptions.elements) == null ? void 0 : _a.filter((e2) => e2.type === DocumentElementType.IMAGE)) != null ? _b : [];
25512
- });
25513
25555
  const mainImageElement = computed(() => {
25514
25556
  return imageElements.value[0];
25515
25557
  });
@@ -25529,12 +25571,6 @@ const _sfc_main$18 = /* @__PURE__ */ defineComponent({
25529
25571
  minWidth: widthOverride.value ? `${widthOverride.value + 10}px` : void 0
25530
25572
  } : {};
25531
25573
  });
25532
- const detailElements = computed(() => {
25533
- var _a, _b;
25534
- return (_b = (_a = props.panelOptions.elements) == null ? void 0 : _a.filter(
25535
- (e2) => e2.type !== DocumentElementType.IMAGE && e2.type !== DocumentElementType.ADDTOCART
25536
- )) != null ? _b : [];
25537
- });
25538
25574
  const addToCartElement = computed(() => {
25539
25575
  var _a;
25540
25576
  return (_a = props.panelOptions.elements) == null ? void 0 : _a.find((e2) => e2.type === DocumentElementType.ADDTOCART);
@@ -25553,12 +25589,42 @@ const _sfc_main$18 = /* @__PURE__ */ defineComponent({
25553
25589
  onMounted(() => {
25554
25590
  checkIfIsInStock();
25555
25591
  });
25556
- const processIsInStock = () => {
25557
- return typeof props.panelOptions.isInStock === "function" ? props.panelOptions.isInStock(props.item) : processDisplayCondition(props.panelOptions.isInStock, props.item);
25558
- };
25592
+ const processIsInStock = computed(() => {
25593
+ const raw = props.panelOptions.isInStock;
25594
+ if (!raw)
25595
+ return true;
25596
+ const rules = Array.isArray(raw) ? raw : [raw];
25597
+ return rules.every((rule2) => shouldDisplay(rule2, props.item));
25598
+ });
25559
25599
  const checkIfIsInStock = () => __async2(this, null, function* () {
25560
- isInStock.value = props.panelOptions.isInStock ? processIsInStock() : true;
25600
+ isInStock.value = props.panelOptions.isInStock ? processIsInStock.value : true;
25561
25601
  });
25602
+ const imageElements = computed(
25603
+ () => {
25604
+ var _a, _b;
25605
+ return (_b = (_a = props.panelOptions.elements) == null ? void 0 : _a.filter((e2) => e2.type === DocumentElementType.IMAGE && !e2.group)) != null ? _b : [];
25606
+ }
25607
+ );
25608
+ const detailElements = computed(
25609
+ () => {
25610
+ var _a, _b;
25611
+ return (_b = (_a = props.panelOptions.elements) == null ? void 0 : _a.filter(
25612
+ (e2) => e2.type !== DocumentElementType.IMAGE && e2.type !== DocumentElementType.ADDTOCART && !e2.group
25613
+ )) != null ? _b : [];
25614
+ }
25615
+ );
25616
+ const elementGroups = computed(
25617
+ () => {
25618
+ var _a;
25619
+ return Array.from(
25620
+ new Set((_a = props.panelOptions.elements) == null ? void 0 : _a.map((e2) => e2.group).filter((g) => Boolean(g)))
25621
+ );
25622
+ }
25623
+ );
25624
+ function getGroupElements(group) {
25625
+ var _a, _b;
25626
+ return (_b = (_a = props.panelOptions.elements) == null ? void 0 : _a.filter((e2) => e2.group === group)) != null ? _b : [];
25627
+ }
25562
25628
  return (_ctx, _cache) => {
25563
25629
  return openBlock(), createElementBlock("a", mergeProps({
25564
25630
  class: ["lupa-search-box-product", { "lupa-search-box-product-highlighted": _ctx.highlighted }],
@@ -25574,9 +25640,9 @@ const _sfc_main$18 = /* @__PURE__ */ defineComponent({
25574
25640
  (openBlock(true), createElementBlock(Fragment, null, renderList(imageElements.value, (element) => {
25575
25641
  return openBlock(), createBlock(_sfc_main$1g, {
25576
25642
  class: "lupa-search-box-product-element",
25643
+ key: element.key,
25577
25644
  item: _ctx.item,
25578
25645
  element,
25579
- key: element.key,
25580
25646
  labels: _ctx.labels,
25581
25647
  link: link.value
25582
25648
  }, null, 8, ["item", "element", "labels", "link"]);
@@ -25586,14 +25652,14 @@ const _sfc_main$18 = /* @__PURE__ */ defineComponent({
25586
25652
  (openBlock(true), createElementBlock(Fragment, null, renderList(detailElements.value, (element) => {
25587
25653
  var _a;
25588
25654
  return openBlock(), createBlock(_sfc_main$1g, {
25589
- key: element.key,
25590
25655
  class: "lupa-search-box-product-element",
25656
+ key: element.key,
25591
25657
  item: _ctx.item,
25592
25658
  element,
25593
25659
  labels: _ctx.labels,
25594
25660
  link: link.value
25595
25661
  }, createSlots({ _: 2 }, [
25596
- badgeOptions.value && ((_a = badgeOptions.value) == null ? void 0 : _a.anchorElementKey) === element.key ? {
25662
+ ((_a = badgeOptions.value) == null ? void 0 : _a.anchorElementKey) === element.key ? {
25597
25663
  name: "badges",
25598
25664
  fn: withCtx(() => [
25599
25665
  createVNode(_sfc_main$19, {
@@ -25606,6 +25672,24 @@ const _sfc_main$18 = /* @__PURE__ */ defineComponent({
25606
25672
  ]), 1032, ["item", "element", "labels", "link"]);
25607
25673
  }), 128))
25608
25674
  ]),
25675
+ (openBlock(true), createElementBlock(Fragment, null, renderList(elementGroups.value, (group) => {
25676
+ return openBlock(), createElementBlock("div", {
25677
+ key: group,
25678
+ class: normalizeClass(`lupa-search-box-group-${group}`)
25679
+ }, [
25680
+ (openBlock(true), createElementBlock(Fragment, null, renderList(getGroupElements(group), (element) => {
25681
+ return openBlock(), createBlock(_sfc_main$1g, {
25682
+ class: "lupa-search-box-product-element",
25683
+ key: element.key,
25684
+ item: _ctx.item,
25685
+ element,
25686
+ labels: _ctx.labels,
25687
+ link: link.value,
25688
+ isInStock: isInStock.value
25689
+ }, null, 8, ["item", "element", "labels", "link", "isInStock"]);
25690
+ }), 128))
25691
+ ], 2);
25692
+ }), 128)),
25609
25693
  addToCartElement.value ? (openBlock(), createElementBlock("div", _hoisted_3$z, [
25610
25694
  createVNode(_sfc_main$1g, {
25611
25695
  class: "lupa-search-box-product-element",
@@ -30602,14 +30686,9 @@ const _sfc_main$t = /* @__PURE__ */ defineComponent(__spreadProps2(__spreadValue
30602
30686
  const enhancementData = (_d = (_c = dynamicDataIdMap.value) == null ? void 0 : _c[(_b = props.item) == null ? void 0 : _b.id]) != null ? _d : {};
30603
30687
  return __spreadValues2(__spreadValues2({}, props.item), enhancementData);
30604
30688
  });
30605
- const displayElement = computed(() => {
30606
- const element = props.element;
30607
- const item = enhancedItem.value;
30608
- if (!element.display) {
30609
- return true;
30610
- }
30611
- return typeof element.display === "function" ? element.display(item) : processDisplayCondition(element.display, item);
30612
- });
30689
+ const displayElement = computed(
30690
+ () => shouldDisplay(props.element.display, enhancedItem.value)
30691
+ );
30613
30692
  const dynamicAttributes = computed(() => {
30614
30693
  return getDynamicAttributes(props.element.dynamicAttributes, enhancedItem.value);
30615
30694
  });
@@ -30737,15 +30816,21 @@ const _sfc_main$s = /* @__PURE__ */ defineComponent({
30737
30816
  var _a, _b;
30738
30817
  return (_b = (_a = props.options.elements) == null ? void 0 : _a.filter((e2) => e2.group === group)) != null ? _b : [];
30739
30818
  };
30740
- const processIsInStock = () => {
30741
- return typeof props.options.isInStock === "function" ? props.options.isInStock(props.product) : processDisplayCondition(props.options.isInStock, props.product);
30819
+ const shouldShowInStock = () => {
30820
+ const raw = props.options.isInStock;
30821
+ if (!raw)
30822
+ return true;
30823
+ const rules = Array.isArray(raw) ? raw : [raw];
30824
+ return rules.every(
30825
+ (rule2) => typeof rule2 === "function" ? rule2(props.product) : processDisplayCondition(rule2, props.product)
30826
+ );
30827
+ };
30828
+ const checkIfIsInStock = () => {
30829
+ isInStock.value = shouldShowInStock();
30742
30830
  };
30743
30831
  onMounted(() => {
30744
30832
  checkIfIsInStock();
30745
30833
  });
30746
- const checkIfIsInStock = () => __async2(this, null, function* () {
30747
- isInStock.value = props.options.isInStock ? yield processIsInStock() : true;
30748
- });
30749
30834
  const handleClick = () => {
30750
30835
  var _a, _b, _c, _d;
30751
30836
  const event = {