@getlupa/client 1.18.3 → 1.18.5

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.
@@ -12617,6 +12617,11 @@ const getRandomString = (length) => {
12617
12617
  }
12618
12618
  return result2;
12619
12619
  };
12620
+ const getSocketClientId = () => {
12621
+ const timestamp = Date.now().toString(36);
12622
+ const randomString = getRandomString(8);
12623
+ return `${timestamp}-${randomString}`;
12624
+ };
12620
12625
  const toFixedIfNecessary = (value, precision = 2) => {
12621
12626
  return (+parseFloat(value).toFixed(precision)).toString();
12622
12627
  };
@@ -12731,6 +12736,10 @@ const DEFAULT_SEARCH_BOX_OPTIONS$1 = {
12731
12736
  links: {
12732
12737
  searchResults: "/search"
12733
12738
  },
12739
+ voiceSearch: {
12740
+ enabled: false,
12741
+ queryKey: ""
12742
+ },
12734
12743
  panels: [
12735
12744
  {
12736
12745
  type: "suggestion",
@@ -14312,6 +14321,350 @@ const useSearchBoxStore = defineStore("searchBox", () => {
14312
14321
  resetHighlightIndex
14313
14322
  };
14314
14323
  });
14324
+ const Env = {
14325
+ production: "https://api.lupasearch.com/v1/",
14326
+ staging: "https://api.staging.lupasearch.com/v1/"
14327
+ };
14328
+ const VoiceServiceEnv = {
14329
+ production: "ws://voice.lupasearch.com:3001",
14330
+ staging: "ws://voice.lupasearch.dev:3001"
14331
+ };
14332
+ const DEFAULT_REQUEST_CONFIG = {
14333
+ method: "POST",
14334
+ headers: { "Content-Type": "application/json" }
14335
+ };
14336
+ const DEFAULT_HEADERS = DEFAULT_REQUEST_CONFIG.headers;
14337
+ const getVoiceServiceApiUrl = (environment, customVoiceServiceUrl) => {
14338
+ if (customVoiceServiceUrl) {
14339
+ return customVoiceServiceUrl;
14340
+ }
14341
+ return VoiceServiceEnv[environment] || VoiceServiceEnv["production"];
14342
+ };
14343
+ const getApiUrl = (environment, customBaseUrl) => {
14344
+ if (customBaseUrl) {
14345
+ return customBaseUrl;
14346
+ }
14347
+ return Env[environment] || Env["production"];
14348
+ };
14349
+ const _sfc_main$1z = /* @__PURE__ */ defineComponent({
14350
+ __name: "VoiceSearchProgressCircle",
14351
+ props: {
14352
+ isRecording: { type: Boolean },
14353
+ timesliceLimit: {},
14354
+ timeSliceLength: {}
14355
+ },
14356
+ setup(__props, { expose: __expose }) {
14357
+ const props = __props;
14358
+ const progressBar = ref(null);
14359
+ const getProgressBarColor = (progressBarStyle) => {
14360
+ if (!progressBarStyle.backgroundImage.startsWith("conic-gradient")) {
14361
+ return progressBarStyle.backgroundColor;
14362
+ }
14363
+ const colorStops = progressBarStyle.backgroundImage.replace(/conic-gradient\(|\)$/g, "").split(")");
14364
+ if (colorStops.length > 1) {
14365
+ return `${colorStops[0]})`;
14366
+ } else {
14367
+ return progressBarStyle.backgroundColor;
14368
+ }
14369
+ };
14370
+ const startProgressBar = () => {
14371
+ if (!progressBar.value || !props.isRecording) {
14372
+ return;
14373
+ }
14374
+ const duration = props.timesliceLimit * props.timeSliceLength;
14375
+ const progressBarStyle = window.getComputedStyle(progressBar.value);
14376
+ const progressBarColor = getProgressBarColor(progressBarStyle);
14377
+ progressBar.value.style.background = `conic-gradient(${progressBarColor} 0%, transparent 0%)`;
14378
+ let startTime = null;
14379
+ function updateProgress(timestamp) {
14380
+ if (!progressBar.value || !props.isRecording) {
14381
+ return;
14382
+ }
14383
+ if (!startTime)
14384
+ startTime = timestamp;
14385
+ const elapsed = timestamp - startTime;
14386
+ const progress = Math.min(elapsed / duration, 1) * 100;
14387
+ progressBar.value.style.background = `conic-gradient(${progressBarColor} ${progress}%, transparent ${progress}%)`;
14388
+ if (elapsed < duration) {
14389
+ requestAnimationFrame(updateProgress);
14390
+ }
14391
+ }
14392
+ requestAnimationFrame(updateProgress);
14393
+ };
14394
+ const stopProgressBar = () => {
14395
+ if (!progressBar.value) {
14396
+ return;
14397
+ }
14398
+ progressBar.value.style.background = "";
14399
+ };
14400
+ __expose({
14401
+ startProgressBar,
14402
+ stopProgressBar
14403
+ });
14404
+ return (_ctx, _cache) => {
14405
+ return openBlock(), createElementBlock("div", {
14406
+ ref_key: "progressBar",
14407
+ ref: progressBar,
14408
+ class: "lupa-progress-circle"
14409
+ }, null, 512);
14410
+ };
14411
+ }
14412
+ });
14413
+ const buildSocketMessageFrameHeader = (event, payloadLength) => {
14414
+ const headerObj = { event, length: payloadLength };
14415
+ const headerJson = JSON.stringify(headerObj);
14416
+ const headerBytes = new TextEncoder().encode(headerJson);
14417
+ const headerLength = new Uint32Array([headerBytes.length]);
14418
+ const headerLengthBytes = new Uint8Array(headerLength.buffer);
14419
+ const result2 = new Uint8Array(4 + headerBytes.length);
14420
+ result2.set(headerLengthBytes, 0);
14421
+ result2.set(headerBytes, 4);
14422
+ return result2;
14423
+ };
14424
+ function useVoiceRecorder(options) {
14425
+ const socket = ref(null);
14426
+ const mediaStream = ref(null);
14427
+ const mediaRecorder = ref(null);
14428
+ const isRecording = ref(false);
14429
+ const errorRef = ref(null);
14430
+ const transcription = ref("");
14431
+ const timeSliceLength = computed(() => {
14432
+ var _a;
14433
+ return (_a = options.timesliceLength) != null ? _a : 1e3;
14434
+ });
14435
+ onBeforeUnmount(() => {
14436
+ closeSocket();
14437
+ stopRecording();
14438
+ });
14439
+ const initSocket = (url, onMessage) => {
14440
+ socket.value = new WebSocket(url);
14441
+ socket.value.onopen = () => __async2(this, null, function* () {
14442
+ var _a;
14443
+ if (((_a = mediaRecorder.value) == null ? void 0 : _a.state) !== "recording") {
14444
+ yield startRecording();
14445
+ }
14446
+ });
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();
14456
+ }
14457
+ };
14458
+ socket.value.onclose = () => {
14459
+ stopRecording();
14460
+ };
14461
+ socket.value.onerror = () => {
14462
+ stopRecording();
14463
+ errorRef.value = "Service connection error";
14464
+ };
14465
+ };
14466
+ const onMediaRecorderDataAvailable = (event) => __async2(this, null, function* () {
14467
+ var _a, _b;
14468
+ if (((_a = mediaRecorder.value) == null ? void 0 : _a.state) !== "recording")
14469
+ return;
14470
+ const audioBuffer = yield event.data.arrayBuffer();
14471
+ const header = buildSocketMessageFrameHeader("audio-chunk", audioBuffer.byteLength);
14472
+ const buffer = new Uint8Array(header.length + audioBuffer.byteLength);
14473
+ buffer.set(header, 0);
14474
+ buffer.set(new Uint8Array(audioBuffer), header.length);
14475
+ (_b = socket.value) == null ? void 0 : _b.send(buffer);
14476
+ });
14477
+ 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
14484
+ }
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;
14492
+ });
14493
+ const stopRecording = () => {
14494
+ var _a, _b;
14495
+ (_a = mediaRecorder.value) == null ? void 0 : _a.stop();
14496
+ (_b = mediaStream.value) == null ? void 0 : _b.getTracks().forEach((track2) => {
14497
+ track2.stop();
14498
+ });
14499
+ isRecording.value = false;
14500
+ };
14501
+ const stopSocketConnection = () => {
14502
+ if (socket.value && socket.value.readyState === WebSocket.OPEN) {
14503
+ const endHeader = buildSocketMessageFrameHeader("audio-chunk-end", 0);
14504
+ socket.value.send(endHeader);
14505
+ setTimeout(() => {
14506
+ closeSocket();
14507
+ }, 1e3);
14508
+ }
14509
+ };
14510
+ const closeSocket = () => {
14511
+ var _a;
14512
+ (_a = socket.value) == null ? void 0 : _a.close();
14513
+ socket.value = null;
14514
+ };
14515
+ const reset = () => {
14516
+ stopRecording();
14517
+ closeSocket();
14518
+ transcription.value = "";
14519
+ errorRef.value = null;
14520
+ isRecording.value = false;
14521
+ };
14522
+ return {
14523
+ isRecording,
14524
+ transcription,
14525
+ errorRef,
14526
+ initSocket,
14527
+ startRecording,
14528
+ stopRecording,
14529
+ stopSocketConnection,
14530
+ reset,
14531
+ closeSocket
14532
+ };
14533
+ }
14534
+ const _hoisted_1$1l = {
14535
+ key: 0,
14536
+ class: "lupa-dialog-overlay"
14537
+ };
14538
+ const _hoisted_2$W = { class: "lupa-dialog-content" };
14539
+ const _hoisted_3$F = { class: "lupa-listening-text" };
14540
+ const _hoisted_4$v = { class: "lupa-mic-button-wrapper" };
14541
+ const _sfc_main$1y = /* @__PURE__ */ defineComponent({
14542
+ __name: "VoiceSearchDialog",
14543
+ props: {
14544
+ isOpen: { type: Boolean },
14545
+ options: {}
14546
+ },
14547
+ emits: [
14548
+ "close",
14549
+ "transcript-update",
14550
+ "stop-recognize"
14551
+ ],
14552
+ setup(__props, { expose: __expose, emit: emit2 }) {
14553
+ const props = __props;
14554
+ const optionsStore = useOptionsStore();
14555
+ const {
14556
+ isRecording,
14557
+ transcription,
14558
+ errorRef,
14559
+ initSocket,
14560
+ stopSocketConnection,
14561
+ reset
14562
+ } = useVoiceRecorder(props.options);
14563
+ const clientId = ref(null);
14564
+ const voiceSearchProgressBar = ref(null);
14565
+ const timesliceLimit = computed(() => {
14566
+ var _a;
14567
+ return (_a = props.options.timesliceLimit) != null ? _a : 4;
14568
+ });
14569
+ const timeSliceLength = computed(() => {
14570
+ var _a;
14571
+ return (_a = props.options.timesliceLength) != null ? _a : 1e3;
14572
+ });
14573
+ const stopDelay = computed(() => {
14574
+ var _a;
14575
+ return (_a = props.options.stopDelay) != null ? _a : 700;
14576
+ });
14577
+ const labels = computed(() => {
14578
+ var _a;
14579
+ return (_a = props.options.labels) != null ? _a : {};
14580
+ });
14581
+ const description = computed(() => {
14582
+ var _a, _b, _c;
14583
+ if (errorRef.value) {
14584
+ return (_a = labels.value.serviceError) != null ? _a : errorRef.value;
14585
+ }
14586
+ if (!isRecording.value) {
14587
+ return (_b = labels.value.microphoneOff) != null ? _b : "Microphone is off. Try again.";
14588
+ }
14589
+ return (_c = labels.value.listening) != null ? _c : "Listening...";
14590
+ });
14591
+ watch(transcription, (newValue) => {
14592
+ emit2("transcript-update", newValue);
14593
+ });
14594
+ const handleRecordingButtonClick = () => {
14595
+ var _a, _b;
14596
+ if (isRecording.value) {
14597
+ setTimeout(() => {
14598
+ stopSocketConnection();
14599
+ handleOnStopEvent();
14600
+ }, stopDelay.value);
14601
+ return;
14602
+ }
14603
+ const voiceServiceUrl = getVoiceServiceApiUrl(
14604
+ optionsStore.envOptions.environment,
14605
+ props.options.customVoiceServiceUrl
14606
+ );
14607
+ const socketUrl = `${voiceServiceUrl}?clientId=${clientId.value}&queryKey=${props.options.queryKey}&languageCode=${(_a = props.options.language) != null ? _a : "en-US"}&connectionType=write-first`;
14608
+ initSocket(socketUrl);
14609
+ (_b = voiceSearchProgressBar.value) == null ? void 0 : _b.startProgressBar();
14610
+ setTimeout(() => {
14611
+ stopSocketConnection();
14612
+ handleOnStopEvent();
14613
+ }, timesliceLimit.value * timeSliceLength.value);
14614
+ };
14615
+ const handleOnStopEvent = () => {
14616
+ var _a;
14617
+ setTimeout(() => {
14618
+ if (errorRef.value)
14619
+ return;
14620
+ emit2("stop-recognize", transcription.value);
14621
+ }, 1500);
14622
+ (_a = voiceSearchProgressBar.value) == null ? void 0 : _a.stopProgressBar();
14623
+ };
14624
+ onMounted(() => {
14625
+ clientId.value = getSocketClientId();
14626
+ });
14627
+ onBeforeUnmount(() => {
14628
+ clientId.value = null;
14629
+ });
14630
+ const dialogReset = () => {
14631
+ var _a;
14632
+ reset();
14633
+ (_a = voiceSearchProgressBar.value) == null ? void 0 : _a.stopProgressBar();
14634
+ };
14635
+ __expose({
14636
+ handleRecordingButtonClick,
14637
+ reset: dialogReset
14638
+ });
14639
+ return (_ctx, _cache) => {
14640
+ return openBlock(), createElementBlock("div", null, [
14641
+ props.isOpen ? (openBlock(), createElementBlock("div", _hoisted_1$1l, [
14642
+ createBaseVNode("button", {
14643
+ class: "lupa-dialog-box-close-button",
14644
+ onClick: _cache[0] || (_cache[0] = () => emit2("close"))
14645
+ }),
14646
+ createBaseVNode("div", _hoisted_2$W, [
14647
+ createBaseVNode("p", _hoisted_3$F, toDisplayString(description.value), 1),
14648
+ createBaseVNode("div", _hoisted_4$v, [
14649
+ createBaseVNode("button", {
14650
+ class: normalizeClass(["lupa-mic-button", { recording: unref(isRecording) }]),
14651
+ onClick: handleRecordingButtonClick
14652
+ }, null, 2),
14653
+ createVNode(_sfc_main$1z, {
14654
+ ref_key: "voiceSearchProgressBar",
14655
+ ref: voiceSearchProgressBar,
14656
+ class: "lupa-progress-circle",
14657
+ isRecording: unref(isRecording),
14658
+ timesliceLimit: timesliceLimit.value,
14659
+ timeSliceLength: timeSliceLength.value
14660
+ }, null, 8, ["isRecording", "timesliceLimit", "timeSliceLength"])
14661
+ ])
14662
+ ])
14663
+ ])) : createCommentVNode("", true)
14664
+ ]);
14665
+ };
14666
+ }
14667
+ });
14315
14668
  const _hoisted_1$1k = { id: "lupa-search-box-input-container" };
14316
14669
  const _hoisted_2$V = { class: "lupa-input-clear" };
14317
14670
  const _hoisted_3$E = { id: "lupa-search-box-input" };
@@ -14325,6 +14678,7 @@ const _hoisted_8$3 = {
14325
14678
  key: 0,
14326
14679
  class: "lupa-close-label"
14327
14680
  };
14681
+ const _hoisted_9$3 = { key: 1 };
14328
14682
  const _sfc_main$1x = /* @__PURE__ */ defineComponent({
14329
14683
  __name: "SearchBoxInput",
14330
14684
  props: {
@@ -14340,6 +14694,8 @@ const _sfc_main$1x = /* @__PURE__ */ defineComponent({
14340
14694
  const searchBoxStore = useSearchBoxStore();
14341
14695
  const { query } = storeToRefs(paramStore);
14342
14696
  const mainInput = ref(null);
14697
+ const voiceDialogOverlay = ref(null);
14698
+ const isVoiceDialogOpen = ref(false);
14343
14699
  const emitInputOnFocus = computed(() => {
14344
14700
  var _a;
14345
14701
  return (_a = props.emitInputOnFocus) != null ? _a : true;
@@ -14350,6 +14706,10 @@ const _sfc_main$1x = /* @__PURE__ */ defineComponent({
14350
14706
  return (_a = props.suggestedValue) != null ? _a : { value: "", override: false, item: { suggestion: "" } };
14351
14707
  }
14352
14708
  );
14709
+ const isVoiceSearchEnabled = computed(() => {
14710
+ var _a, _b;
14711
+ return (_b = (_a = props.options.voiceSearch) == null ? void 0 : _a.enabled) != null ? _b : false;
14712
+ });
14353
14713
  const labels = computed(() => props.options.labels);
14354
14714
  const input2 = ref("");
14355
14715
  const inputValue = computed({
@@ -14373,6 +14733,12 @@ const _sfc_main$1x = /* @__PURE__ */ defineComponent({
14373
14733
  var _a;
14374
14734
  return (_a = labels.value.searchInputAriaLabel) != null ? _a : "Search input";
14375
14735
  });
14736
+ onMounted(() => {
14737
+ document.addEventListener("click", handleClickOutsideVoiceDialogOverlay);
14738
+ });
14739
+ onBeforeUnmount(() => {
14740
+ document.removeEventListener("click", handleClickOutsideVoiceDialogOverlay);
14741
+ });
14376
14742
  watch(suggestedValue, () => {
14377
14743
  if (suggestedValue.value.override) {
14378
14744
  input2.value = suggestedValue.value.item.suggestion;
@@ -14407,6 +14773,37 @@ const _sfc_main$1x = /* @__PURE__ */ defineComponent({
14407
14773
  }
14408
14774
  (_a = mainInput == null ? void 0 : mainInput.value) == null ? void 0 : _a.focus();
14409
14775
  };
14776
+ const openVoiceSearchDialog = () => {
14777
+ var _a;
14778
+ isVoiceDialogOpen.value = true;
14779
+ (_a = voiceDialogOverlay.value) == null ? void 0 : _a.handleRecordingButtonClick();
14780
+ };
14781
+ const closeDialog = () => {
14782
+ var _a;
14783
+ isVoiceDialogOpen.value = false;
14784
+ (_a = voiceDialogOverlay.value) == null ? void 0 : _a.reset();
14785
+ };
14786
+ const handleVoiceSearchOutput = (transcription) => {
14787
+ inputValue.value = transcription;
14788
+ handleSubmit();
14789
+ };
14790
+ const stopRecognition = (trascription) => {
14791
+ setTimeout(() => {
14792
+ isVoiceDialogOpen.value = false;
14793
+ handleVoiceSearchOutput(trascription);
14794
+ }, 500);
14795
+ };
14796
+ const handleClickOutsideVoiceDialogOverlay = (event) => {
14797
+ if (event.target.classList.contains("lupa-voice-search-button")) {
14798
+ return;
14799
+ }
14800
+ if (voiceDialogOverlay.value && voiceDialogOverlay.value.$el.contains(event.target)) {
14801
+ return;
14802
+ }
14803
+ if (isVoiceDialogOpen.value) {
14804
+ closeDialog();
14805
+ }
14806
+ };
14410
14807
  __expose({ focus });
14411
14808
  return (_ctx, _cache) => {
14412
14809
  return openBlock(), createElementBlock("div", _hoisted_1$1k, [
@@ -14450,7 +14847,23 @@ const _sfc_main$1x = /* @__PURE__ */ defineComponent({
14450
14847
  onClick: _cache[1] || (_cache[1] = ($event) => _ctx.$emit("close"))
14451
14848
  }, [
14452
14849
  labels.value.close ? (openBlock(), createElementBlock("span", _hoisted_8$3, toDisplayString(labels.value.close), 1)) : createCommentVNode("", true)
14453
- ])) : createCommentVNode("", true)
14850
+ ])) : createCommentVNode("", true),
14851
+ isVoiceSearchEnabled.value ? (openBlock(), createElementBlock("div", _hoisted_9$3, [
14852
+ createBaseVNode("button", {
14853
+ onClick: openVoiceSearchDialog,
14854
+ class: "lupa-voice-search-button"
14855
+ })
14856
+ ])) : createCommentVNode("", true),
14857
+ isVoiceSearchEnabled.value ? (openBlock(), createBlock(_sfc_main$1y, {
14858
+ key: 2,
14859
+ ref_key: "voiceDialogOverlay",
14860
+ ref: voiceDialogOverlay,
14861
+ isOpen: isVoiceDialogOpen.value,
14862
+ options: props.options.voiceSearch,
14863
+ onClose: closeDialog,
14864
+ onTranscriptUpdate: handleVoiceSearchOutput,
14865
+ onStopRecognize: stopRecognition
14866
+ }, null, 8, ["isOpen", "options"])) : createCommentVNode("", true)
14454
14867
  ]);
14455
14868
  };
14456
14869
  }
@@ -25989,7 +26402,8 @@ const _sfc_main$12 = /* @__PURE__ */ defineComponent({
25989
26402
  "labels",
25990
26403
  "links",
25991
26404
  "inputAttributes",
25992
- "showSubmitButton"
26405
+ "showSubmitButton",
26406
+ "voiceSearch"
25993
26407
  ])
25994
26408
  );
25995
26409
  const panelOptions = computed(
@@ -26573,22 +26987,42 @@ const _sfc_main$_ = /* @__PURE__ */ defineComponent({
26573
26987
  emits: ["remove"],
26574
26988
  setup(__props, { emit: emit2 }) {
26575
26989
  const props = __props;
26576
- const facetKeyClass = computed(() => {
26577
- return `lupa-facet-active-filter-${props.filter.key}`;
26990
+ const facetKeyClass = computed(() => `lupa-facet-active-filter-${props.filter.key}`);
26991
+ const { searchResultOptions } = storeToRefs(useOptionsStore());
26992
+ const units = computed(() => {
26993
+ var _a, _b, _c, _d, _e;
26994
+ return (_e = (_d = (_c = (_b = (_a = searchResultOptions.value) == null ? void 0 : _a.filters) == null ? void 0 : _b.facets) == null ? void 0 : _c.stats) == null ? void 0 : _d.units) != null ? _e : {};
26578
26995
  });
26579
- const handleClick = () => {
26996
+ function handleClick() {
26580
26997
  emit2("remove", { filter: props.filter });
26581
- };
26998
+ }
26999
+ function formatFilterValue(filter2) {
27000
+ const unit = units.value[filter2.key] || "";
27001
+ let min, max;
27002
+ if (Array.isArray(filter2.value)) {
27003
+ [min, max] = filter2.value.map(String);
27004
+ } else if (typeof filter2.value === "string" && filter2.value.includes("-")) {
27005
+ const parts = filter2.value.split("-").map((s) => s.trim());
27006
+ if (parts.length === 2)
27007
+ [min, max] = parts;
27008
+ }
27009
+ if (min != null && max != null) {
27010
+ return `${min} ${unit} – ${max} ${unit}`;
27011
+ }
27012
+ return `${filter2.value} ${unit}`.trim();
27013
+ }
26582
27014
  return (_ctx, _cache) => {
26583
27015
  return openBlock(), createElementBlock("div", {
26584
- class: normalizeClass(["lupa-search-result-filter-value", { [facetKeyClass.value]: true }])
27016
+ class: normalizeClass(["lupa-search-result-filter-value", [facetKeyClass.value]]),
27017
+ "data-cy": "lupa-current-filter-item"
26585
27018
  }, [
26586
27019
  createBaseVNode("div", {
26587
27020
  class: "lupa-current-filter-action",
26588
- onClick: handleClick
26589
- }, ""),
27021
+ onClick: handleClick,
27022
+ "aria-label": "Remove filter"
27023
+ }, " ⨉ "),
26590
27024
  createBaseVNode("div", _hoisted_1$U, toDisplayString(_ctx.filter.label) + ": ", 1),
26591
- createBaseVNode("div", _hoisted_2$F, toDisplayString(_ctx.filter.value), 1)
27025
+ createBaseVNode("div", _hoisted_2$F, toDisplayString(formatFilterValue(props.filter)), 1)
26592
27026
  ], 2);
26593
27027
  };
26594
27028
  }
@@ -26610,6 +27044,14 @@ const _sfc_main$Z = /* @__PURE__ */ defineComponent({
26610
27044
  expandable: { type: Boolean }
26611
27045
  },
26612
27046
  setup(__props) {
27047
+ const optionsStore = useOptionsStore();
27048
+ const { searchResultOptions } = storeToRefs(optionsStore);
27049
+ const units = computed(
27050
+ () => {
27051
+ var _a, _b, _c, _d, _e;
27052
+ return (_e = (_d = (_c = (_b = (_a = searchResultOptions == null ? void 0 : searchResultOptions.value) == null ? void 0 : _a.filters) == null ? void 0 : _b.facets) == null ? void 0 : _c.stats) == null ? void 0 : _d.units) != null ? _e : {};
27053
+ }
27054
+ );
26613
27055
  const isOpen = ref(false);
26614
27056
  const paramsStore = useParamsStore();
26615
27057
  const optionStore = useOptionsStore();
@@ -26691,8 +27133,9 @@ const _sfc_main$Z = /* @__PURE__ */ defineComponent({
26691
27133
  return openBlock(), createBlock(_sfc_main$_, {
26692
27134
  key: filter2.key + "_" + filter2.value,
26693
27135
  filter: filter2,
27136
+ units: units.value,
26694
27137
  onRemove: handleRemove
26695
- }, null, 8, ["filter"]);
27138
+ }, null, 8, ["filter", "units"]);
26696
27139
  }), 128))
26697
27140
  ]),
26698
27141
  createBaseVNode("div", {
@@ -28016,15 +28459,17 @@ const _hoisted_4$j = {
28016
28459
  const _hoisted_5$d = { class: "lupa-stats-from" };
28017
28460
  const _hoisted_6$7 = ["max", "min", "pattern", "aria-label"];
28018
28461
  const _hoisted_7$5 = { key: 0 };
28019
- const _hoisted_8$1 = /* @__PURE__ */ createBaseVNode("div", { class: "lupa-stats-separator" }, null, -1);
28020
- const _hoisted_9$1 = {
28462
+ const _hoisted_8$1 = { key: 1 };
28463
+ const _hoisted_9$1 = /* @__PURE__ */ createBaseVNode("div", { class: "lupa-stats-separator" }, null, -1);
28464
+ const _hoisted_10 = {
28021
28465
  key: 0,
28022
28466
  class: "lupa-stats-range-label"
28023
28467
  };
28024
- const _hoisted_10 = { class: "lupa-stats-to" };
28025
- const _hoisted_11 = ["max", "min", "pattern", "aria-label"];
28026
- const _hoisted_12 = { key: 0 };
28027
- const _hoisted_13 = {
28468
+ const _hoisted_11 = { class: "lupa-stats-to" };
28469
+ const _hoisted_12 = ["max", "min", "pattern", "aria-label"];
28470
+ const _hoisted_13 = { key: 0 };
28471
+ const _hoisted_14 = { key: 1 };
28472
+ const _hoisted_15 = {
28028
28473
  key: 2,
28029
28474
  class: "lupa-stats-slider-wrapper"
28030
28475
  };
@@ -28091,7 +28536,7 @@ const _sfc_main$V = /* @__PURE__ */ defineComponent({
28091
28536
  if (!value || value > facetMax.value) {
28092
28537
  return;
28093
28538
  }
28094
- innerSliderRange.value = [value, sliderRange.value[1]];
28539
+ innerSliderRange.value = [sliderRange.value[1], value];
28095
28540
  handleInputChange();
28096
28541
  }
28097
28542
  });
@@ -28147,7 +28592,18 @@ const _sfc_main$V = /* @__PURE__ */ defineComponent({
28147
28592
  });
28148
28593
  const statsSummary = computed(() => {
28149
28594
  const [min, max] = sliderRange.value;
28150
- return isPrice.value ? formatPriceSummary([min, max], currency.value, separator.value, currencyTemplate.value) : formatRange({ gte: min, lte: max });
28595
+ if (isPrice.value) {
28596
+ return formatPriceSummary(
28597
+ [min, max],
28598
+ currency.value,
28599
+ separator.value,
28600
+ currencyTemplate.value
28601
+ );
28602
+ }
28603
+ if (unit.value) {
28604
+ return `${min} ${unit.value} - ${max} ${unit.value}`;
28605
+ }
28606
+ return formatRange({ gte: min, lte: max });
28151
28607
  });
28152
28608
  const separator = computed(() => {
28153
28609
  var _a, _b, _c;
@@ -28208,6 +28664,12 @@ const _sfc_main$V = /* @__PURE__ */ defineComponent({
28208
28664
  const handleDragging = (value) => {
28209
28665
  innerSliderRange.value = value;
28210
28666
  };
28667
+ const unit = computed(
28668
+ () => {
28669
+ var _a, _b, _c, _d, _e;
28670
+ return (_e = (_d = (_a = props.options.stats) == null ? void 0 : _a.units) == null ? void 0 : _d[(_c = (_b = props.facet) == null ? void 0 : _b.key) != null ? _c : ""]) != null ? _e : "";
28671
+ }
28672
+ );
28211
28673
  return (_ctx, _cache) => {
28212
28674
  return openBlock(), createElementBlock("div", _hoisted_1$P, [
28213
28675
  !isInputVisible.value ? (openBlock(), createElementBlock("div", _hoisted_2$B, toDisplayString(statsSummary.value), 1)) : (openBlock(), createElementBlock("div", _hoisted_3$s, [
@@ -28230,13 +28692,14 @@ const _sfc_main$V = /* @__PURE__ */ defineComponent({
28230
28692
  { lazy: true }
28231
28693
  ]
28232
28694
  ]),
28233
- isPrice.value ? (openBlock(), createElementBlock("span", _hoisted_7$5, toDisplayString(currency.value), 1)) : createCommentVNode("", true)
28695
+ isPrice.value ? (openBlock(), createElementBlock("span", _hoisted_7$5, toDisplayString(currency.value), 1)) : createCommentVNode("", true),
28696
+ unit.value ? (openBlock(), createElementBlock("span", _hoisted_8$1, toDisplayString(unit.value), 1)) : createCommentVNode("", true)
28234
28697
  ])
28235
28698
  ]),
28236
- _hoisted_8$1,
28699
+ _hoisted_9$1,
28237
28700
  createBaseVNode("div", null, [
28238
- rangeLabelTo.value ? (openBlock(), createElementBlock("div", _hoisted_9$1, toDisplayString(rangeLabelTo.value), 1)) : createCommentVNode("", true),
28239
- createBaseVNode("div", _hoisted_10, [
28701
+ rangeLabelTo.value ? (openBlock(), createElementBlock("div", _hoisted_10, toDisplayString(rangeLabelTo.value), 1)) : createCommentVNode("", true),
28702
+ createBaseVNode("div", _hoisted_11, [
28240
28703
  withDirectives(createBaseVNode("input", {
28241
28704
  "onUpdate:modelValue": _cache[1] || (_cache[1] = ($event) => toValue.value = $event),
28242
28705
  type: "text",
@@ -28245,7 +28708,7 @@ const _sfc_main$V = /* @__PURE__ */ defineComponent({
28245
28708
  min: facetMin.value,
28246
28709
  pattern: sliderInputFormat.value,
28247
28710
  "aria-label": ariaLabelTo.value
28248
- }, null, 8, _hoisted_11), [
28711
+ }, null, 8, _hoisted_12), [
28249
28712
  [
28250
28713
  vModelText,
28251
28714
  toValue.value,
@@ -28253,11 +28716,12 @@ const _sfc_main$V = /* @__PURE__ */ defineComponent({
28253
28716
  { lazy: true }
28254
28717
  ]
28255
28718
  ]),
28256
- isPrice.value ? (openBlock(), createElementBlock("span", _hoisted_12, toDisplayString(currency.value), 1)) : createCommentVNode("", true)
28719
+ isPrice.value ? (openBlock(), createElementBlock("span", _hoisted_13, toDisplayString(currency.value), 1)) : createCommentVNode("", true),
28720
+ unit.value ? (openBlock(), createElementBlock("span", _hoisted_14, toDisplayString(unit.value), 1)) : createCommentVNode("", true)
28257
28721
  ])
28258
28722
  ])
28259
28723
  ])),
28260
- isSliderVisible.value ? (openBlock(), createElementBlock("div", _hoisted_13, [
28724
+ isSliderVisible.value ? (openBlock(), createElementBlock("div", _hoisted_15, [
28261
28725
  createVNode(unref(m), {
28262
28726
  class: "slider",
28263
28727
  tooltips: false,
@@ -33160,21 +33624,6 @@ const _sfc_main$8 = /* @__PURE__ */ defineComponent({
33160
33624
  };
33161
33625
  }
33162
33626
  });
33163
- const Env = {
33164
- production: "https://api.lupasearch.com/v1/",
33165
- staging: "https://api.staging.lupasearch.com/v1/"
33166
- };
33167
- const DEFAULT_REQUEST_CONFIG = {
33168
- method: "POST",
33169
- headers: { "Content-Type": "application/json" }
33170
- };
33171
- const DEFAULT_HEADERS = DEFAULT_REQUEST_CONFIG.headers;
33172
- const getApiUrl = (environment, customBaseUrl) => {
33173
- if (customBaseUrl) {
33174
- return customBaseUrl;
33175
- }
33176
- return Env[environment] || Env["production"];
33177
- };
33178
33627
  const suggestSearchChatPhrases = (options, request, chatSettings) => __async2(void 0, null, function* () {
33179
33628
  var _a, _b, _c;
33180
33629
  const { environment, customBaseUrl } = options;
@@ -33681,7 +34130,7 @@ const _hoisted_4 = {
33681
34130
  key: 0,
33682
34131
  class: "lupasearch-chat-content"
33683
34132
  };
33684
- const _sfc_main$1y = /* @__PURE__ */ defineComponent({
34133
+ const _sfc_main$1A = /* @__PURE__ */ defineComponent({
33685
34134
  __name: "ChatContainer",
33686
34135
  props: {
33687
34136
  options: {}
@@ -40335,7 +40784,7 @@ const chat = (options, mountOptions) => {
40335
40784
  const instance = createVue(
40336
40785
  options.displayOptions.containerSelector,
40337
40786
  mountOptions == null ? void 0 : mountOptions.mountingBehavior,
40338
- _sfc_main$1y,
40787
+ _sfc_main$1A,
40339
40788
  {
40340
40789
  options
40341
40790
  }