blocfeed 0.9.0 → 0.10.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,45 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.10.1 — 2026-03-03
4
+
5
+ ### Bug fixes
6
+
7
+ - **Voice errors now visible in the widget** — `startVoice()` was catching errors internally and returning `undefined`, so mic-denied and transcription-failed states were silently swallowed. The widget now checks `controller.getState().lastError` and displays the appropriate i18n string (`voiceDeniedText`, `voiceErrorText`, `voiceEmptyText`).
8
+ - **Debug logging** — Controller now logs `console.warn` messages when voice is blocked: wrong phase, voice not enabled, or recording/transcription error.
9
+
10
+ ---
11
+
12
+ ## 0.10.0 — 2026-03-03
13
+
14
+ ### New features
15
+
16
+ - **Voice feedback** — Users can record audio via the microphone and have it automatically transcribed to text using OpenAI Whisper. The transcribed text populates the feedback textarea for review before submission.
17
+ - Enabled via `config.voice.enabled: true` (off by default)
18
+ - Configurable max duration (`maxDurationMs`, default 60s) with auto-stop
19
+ - Audio codec negotiation: WebM/Opus → WebM → Ogg/Opus
20
+ - Voice button (mic icon) appears in the review phase; pulsing red dot + timer during recording
21
+ - "Transcribing..." spinner while audio is sent to the server
22
+ - Graceful error handling: microphone denied, transcription failed, no speech detected
23
+ - Auto-hidden on unsupported browsers (no `getUserMedia` or `MediaRecorder`)
24
+ - Requires project-level opt-in via dashboard toggle (`voice_feedback_enabled`)
25
+
26
+ ### Improvements
27
+
28
+ - New types exported: `VoiceConfig`.
29
+ - Engine exports: `isVoiceSupported`, `startVoiceRecording`, `transcribeAudio`, `VoiceSession` for headless usage.
30
+ - `BlocFeedStrings` extended with `voiceButton`, `voiceStopButton`, `voiceRecordingText`, `voiceTranscribingText`, `voiceDeniedText`, `voiceErrorText`, `voiceEmptyText`.
31
+ - `BlocFeedState` extended with `voiceRecording`, `voiceElapsedMs`, `voiceTranscribing` fields.
32
+ - `BlocFeedController` extended with `startVoice()` and `stopVoice()` methods.
33
+
34
+ ### Platform changes (blocfeed-frontend)
35
+
36
+ - New SQL migration `scripts/040_voice_feedback.sql` adds `voice_feedback_enabled BOOLEAN NOT NULL DEFAULT FALSE` column to projects.
37
+ - New `POST /api/feedback/voice` endpoint: accepts raw audio body with `X-Blocfeed-Id` header, validates project + feature flag, calls OpenAI Whisper API, returns transcribed text.
38
+ - Dashboard Settings page adds a "Voice Feedback" toggle card under General tab.
39
+ - New server action `updateVoiceFeedbackEnabled` for toggling the feature per project.
40
+
41
+ ---
42
+
3
43
  ## 0.9.0 — 2026-02-25
4
44
 
5
45
  ### New features
package/README.md CHANGED
@@ -17,6 +17,7 @@ Drop-in in-app feedback widget for **Next.js** and **React**:
17
17
  - **Programmatic API** — `ref.open()`, `ref.close()`, `ref.submit(msg)` via React ref
18
18
  - **Video recording** — record a short screen capture clip (via `getDisplayMedia`) to show bug reproduction steps
19
19
  - **Click tracking** — automatically captures every click during video recording with page path, component name, element tag, and text
20
+ - **Voice feedback** — record audio via microphone, auto-transcribe with Whisper, and populate the feedback textarea
20
21
  - **Secret leak detection** — scans client-side code for exposed API keys, database credentials, and tokens
21
22
 
22
23
  Docs live in `docs/` (start at `docs/index.md`). Architecture pointers are in `ARCHITECTURE.md`.
@@ -141,6 +142,12 @@ All configuration is passed via the `config` prop on `<BlocFeedWidget>` or `<Blo
141
142
  videoBitsPerSecond: 2_500_000,
142
143
  },
143
144
 
145
+ // Voice feedback (mic recording + transcription)
146
+ voice: {
147
+ enabled: true,
148
+ maxDurationMs: 60_000,
149
+ },
150
+
144
151
  // Screenshot defaults
145
152
  capture: {
146
153
  element: true,
@@ -295,6 +302,20 @@ config={{
295
302
  }}
296
303
  ```
297
304
 
305
+ Voice feedback strings are also localizable:
306
+
307
+ ```tsx
308
+ strings: {
309
+ voiceButton: "Voice",
310
+ voiceStopButton: "Stop",
311
+ voiceRecordingText: "Recording…",
312
+ voiceTranscribingText: "Transcribing…",
313
+ voiceDeniedText: "Microphone access was denied",
314
+ voiceErrorText: "Voice transcription failed. Please type instead.",
315
+ voiceEmptyText: "No speech detected. Try again or type instead.",
316
+ }
317
+ ```
318
+
298
319
  All fields are optional — only override the ones you need. Category labels are also localizable:
299
320
 
300
321
  ```tsx
@@ -765,6 +786,97 @@ const events = drainClickEvents(); // returns ClickEvent[]
765
786
  clearClickEvents(); // reset the buffer
766
787
  ```
767
788
 
789
+ ## Voice Feedback
790
+
791
+ Let users speak their feedback instead of typing. Audio is recorded via the microphone, transcribed server-side with OpenAI Whisper, and the text populates the feedback textarea for review before submission.
792
+
793
+ ```tsx
794
+ <BlocFeedWidget
795
+ blocfeed_id="bf_..."
796
+ config={{
797
+ voice: {
798
+ enabled: true, // default: false
799
+ maxDurationMs: 60_000, // default: 60s — auto-stops at this limit
800
+ },
801
+ }}
802
+ />
803
+ ```
804
+
805
+ ### Setup
806
+
807
+ Voice feedback requires two things:
808
+
809
+ 1. **Enable in the SDK** — set `voice.enabled: true` in the widget config
810
+ 2. **Enable per project** — toggle "Voice Feedback" on in the BlocFeed dashboard under **Settings > General**
811
+
812
+ The project-level toggle (`voice_feedback_enabled`) must be enabled for the transcription API to accept requests.
813
+
814
+ ### `VoiceConfig` options
815
+
816
+ | Option | Type | Default | Description |
817
+ |--------|------|---------|-------------|
818
+ | `enabled` | `boolean` | `false` | Show the Voice button in the review phase |
819
+ | `maxDurationMs` | `number` | `60000` | Maximum recording duration in ms. Auto-stops when reached |
820
+
821
+ ### How it works
822
+
823
+ 1. A **Voice** button (mic icon) appears in the review phase (after element selection)
824
+ 2. Clicking it triggers the browser's microphone permission prompt
825
+ 3. A pulsing red dot + elapsed timer shows during recording
826
+ 4. Recording stops when the user clicks **Stop** or the max duration is reached
827
+ 5. A "Transcribing..." spinner appears while audio is sent to the server
828
+ 6. The transcribed text populates the textarea — the user can edit before submitting
829
+ 7. If no speech is detected or transcription fails, an inline message is shown
830
+
831
+ ### Browser support
832
+
833
+ Voice recording requires `getUserMedia` and `MediaRecorder` with WebM/Ogg audio support. The Voice button is **automatically hidden** on unsupported browsers.
834
+
835
+ | Browser | Supported |
836
+ |---------|-----------|
837
+ | Chrome / Edge | Yes |
838
+ | Firefox | Yes |
839
+ | Safari 14.5+ | Yes (with Opus codec) |
840
+
841
+ ### Localization
842
+
843
+ Override voice-related UI text via `config.ui.strings`:
844
+
845
+ ```tsx
846
+ strings: {
847
+ voiceButton: "Voice",
848
+ voiceStopButton: "Stop",
849
+ voiceRecordingText: "Recording…",
850
+ voiceTranscribingText: "Transcribing…",
851
+ voiceDeniedText: "Microphone access was denied",
852
+ voiceErrorText: "Voice transcription failed. Please type instead.",
853
+ voiceEmptyText: "No speech detected. Try again or type instead.",
854
+ }
855
+ ```
856
+
857
+ ### Headless voice API
858
+
859
+ ```ts
860
+ import {
861
+ isVoiceSupported,
862
+ startVoiceRecording,
863
+ transcribeAudio,
864
+ } from "blocfeed/engine";
865
+
866
+ if (isVoiceSupported()) {
867
+ const session = await startVoiceRecording({ config: { maxDurationMs: 30_000 } });
868
+ // session.stop() to stop early
869
+ const { blob, mime, durationMs } = await session.result;
870
+
871
+ const { text } = await transcribeAudio({
872
+ blob,
873
+ mime,
874
+ blocfeedId: "bf_...",
875
+ });
876
+ console.log("Transcribed:", text);
877
+ }
878
+ ```
879
+
768
880
  ### Known limitations
769
881
 
770
882
  The default screenshot engine (`html-to-image`) has known issues with:
@@ -882,6 +994,7 @@ import type {
882
994
  SecurityFinding,
883
995
  SecuritySnapshot,
884
996
  ClickEvent,
997
+ VoiceConfig,
885
998
  // ... and more
886
999
  } from "blocfeed";
887
1000
  ```
@@ -0,0 +1,2 @@
1
+ 'use strict';function y(){return typeof window<"u"&&typeof document<"u"}function Ae(e){let t=globalThis.CSS;return typeof t?.escape=="function"?t.escape(e):e.replace(/[^a-zA-Z0-9_-]/g,n=>{let o=n.codePointAt(0);return o===void 0?"":`\\${o.toString(16)} `})}function se(e){return {x:e.x,y:e.y,width:e.width,height:e.height}}function it(e){return {x:e.x+window.scrollX,y:e.y+window.scrollY,width:e.width,height:e.height}}function at(e){return e.replace(/\s+/g," ").trim()}function ce(e,t=140){let n=e.textContent;if(!n)return;let o=at(n);if(o)return o.length<=t?o:`${o.slice(0,t-1)}\u2026`}function st(e){let t=1;for(let n=e.previousElementSibling;n;n=n.previousElementSibling)n.tagName===e.tagName&&(t+=1);return t}var Ce=["data-testid","data-test-id","data-test","data-qa","data-cy"],Re="data-blocfeed-component";function le(e){let t=e.closest(`[${Re}]`);if(!t)return;let o=t.getAttribute(Re)?.trim();return o||void 0}function ct(e){for(let t of Ce){let n=e.closest(`[${t}]`);if(!n)continue;let r=n.getAttribute(t)?.trim();if(r)return r}}function xe(e){try{let t=e,n=Object.getOwnPropertyNames(t);for(let o of n)if(o.startsWith("__reactFiber$")||o.startsWith("__reactInternalInstance$")){let r=t[o];if(r&&typeof r=="object")return r}}catch{}return null}function U(e){if(e.length<=1||e.length===2&&e[0]===e[0].toLowerCase())return true;let t=e[0];return t>="a"&&t<="z"}function $(e){if(e){for(let t of e)if(typeof t.name=="string"&&t.name&&!U(t.name))return t.name}}function P(e){if(e&&typeof e!="string"){if(typeof e=="function"){let t=e;return typeof t.displayName=="string"&&t.displayName?t.displayName:typeof t.name=="string"&&t.name?t.name:void 0}if(typeof e=="object"){let t=e,n=t.displayName;if(typeof n=="string"&&n)return n;let o=t.render;if(typeof o=="function"){let i=o;if(typeof i.displayName=="string"&&i.displayName)return i.displayName;if(typeof i.name=="string"&&i.name)return i.name}let r=t.type;return P(r)}}}function ue(e){let t=xe(e);if(!t)return;let n=$(t._debugInfo);if(n)return n;let o=t._debugOwner!==void 0;if(o){let c=t._debugOwner;for(let u=0;c&&u<50;u+=1){let b=$(c._debugInfo);if(b)return b;let m=P(c.type)??P(c.elementType);if(m&&!U(m))return m;c=c._debugOwner;}}if(t._owner!==void 0&&t._owner!==t._debugOwner){let c=t._owner;for(let u=0;c&&u<50;u+=1){let b=$(c._debugInfo);if(b)return b;let m=P(c.type)??P(c.elementType);if(m&&!U(m))return m;c=c._owner;}}let i=t,a=o?80:25;for(let c=0;i&&c<a;c+=1){let u=$(i._debugInfo);if(u)return u;let b=P(i.type)??P(i.elementType);if(b&&!U(b))return b;i=i.return;}let s=e.parentElement;for(let c=0;s&&c<15;c+=1){let u=xe(s);if(u){let b=$(u._debugInfo);if(b)return b;let m=P(u.type)??P(u.elementType);if(m&&!U(m))return m;if(u._debugOwner){let l=P(u._debugOwner.type)??P(u._debugOwner.elementType);if(l&&!U(l))return l}if(u._owner&&u._owner!==u._debugOwner){let l=P(u._owner.type)??P(u._owner.elementType);if(l&&!U(l))return l}}s=s.parentElement;}}function lt(e){let t=e.tagName.toLowerCase(),n=e.getAttribute("id");if(n)return `#${Ae(n)}`;for(let o of Ce){let r=e.getAttribute(o);if(r)return `${t}[${o}="${Ae(r)}"]`}return `${t}:nth-of-type(${st(e)})`}function ut(e,t=10){let n=[],o=e;for(;o&&n.length<t;){let r=lt(o);if(n.unshift(r),r.startsWith("#"))break;o=o.parentElement;}return n.join(" > ")}function Pe(e,t){if(!t||t.length===0)return false;for(let n of t)if(e.closest(n))return true;return false}function de(e,t){if(!e||Pe(e,t.ignoreSelectors))return null;let n=e;for(;n;){if(Pe(n,t.ignoreSelectors))return null;if(t.isSelectable?.(n)??dt(n))return n;n=n.parentElement;}return null}function dt(e){let t=e.tagName;return !(t==="HTML"||t==="BODY")}function Fe(e){let t=e.getBoundingClientRect(),n={selector:ut(e),tagName:e.tagName.toLowerCase(),rect:se(t),pageRect:it(t)},o=e.getAttribute("id");o&&(n.id=o);let r=e.className;typeof r=="string"&&r.trim()&&(n.className=r);let i=ce(e);i&&(n.textSnippet=i);let a=le(e)??ue(e);a&&(n.componentName=a);let s=ct(e);return s&&(n.testId=s),n}var fe=null;async function Me(){return fe||(fe=import('html-to-image')),fe}async function ft(e){return await new Promise((t,n)=>{let o=new Image;o.onload=()=>t({width:o.naturalWidth,height:o.naturalHeight}),o.onerror=()=>n(new Error("Failed to load generated screenshot")),o.src=e;})}async function De(e,t){let{width:n,height:o}=await ft(e);return {dataUrl:e,mime:t,width:n,height:o}}function q(e){if(e?.aborted)throw new Error("Aborted")}function Le(){return {async captureElement(e,t){if(!y())throw new Error("captureElement can only run in the browser");q(t.signal);let n=await Me();q(t.signal);let o=t.mime==="image/jpeg"?await n.toJpeg(e,{quality:t.quality??.92,pixelRatio:t.pixelRatio}):await n.toPng(e,{pixelRatio:t.pixelRatio});return q(t.signal),await De(o,t.mime)},async captureFullPage(e){if(!y())throw new Error("captureFullPage can only run in the browser");q(e.signal);let t=document.documentElement,n=Math.max(t.scrollWidth,t.clientWidth),o=Math.max(t.scrollHeight,t.clientHeight),r=Math.min(1,e.maxDimension/Math.max(n,o)),i=Math.max(1,Math.round(n*r)),a=Math.max(1,Math.round(o*r)),s=await Me();q(e.signal);let c=e.mime==="image/jpeg"?await s.toJpeg(t,{width:i,height:a,quality:e.quality??.92,pixelRatio:e.pixelRatio}):await s.toPng(t,{width:i,height:a,pixelRatio:e.pixelRatio});return q(e.signal),await De(c,e.mime)}}}function mt(e){if(e instanceof Error)return e.message;if(typeof e=="string")return e;try{return JSON.stringify(e)}catch{return "Unknown error"}}function h(e,t,n){let o={kind:e,message:mt(t)};return n&&(o.detail=n),o}var pt=12e3,gt=2048,ht=.92;function Be(){return Date.now()}function wt(e){return new Promise((t,n)=>{let o=()=>n(new Error("Aborted"));if(e.aborted){o();return}e.addEventListener("abort",o,{once:true});})}async function Ie(e,t,n){let o=new Promise((i,a)=>{let s=setTimeout(()=>a(new Error("Timeout")),t);typeof s.unref=="function"&&s.unref();}),r=[e,o];return n&&r.push(wt(n)),await Promise.race(r)}function bt(e){if(!y())return 1;let t=window.devicePixelRatio||1,n=e?.pixelRatio??Math.min(t,2);return Math.max(.1,n)}function yt(e){return !!(e?.element||e?.fullPage)}function Ne(e){let t={mime:e.mime,pixelRatio:e.pixelRatio,maxDimension:e.maxDimension};return e.includeQuality&&(t.quality=e.quality),e.signal&&(t.signal=e.signal),t}async function Oe(e){let{selectionElement:t,capture:n,signal:o}=e;if(!y()||!yt(n))return;let r=Be(),i=[],a=n?.timeoutMs??pt,s=n?.maxDimension??gt,c=n?.mime??"image/png",u=n?.quality??ht,b=n?.adapter??Le(),m={},l=bt(n);if(n?.element&&t)try{let p=t.getBoundingClientRect(),d=Math.min(1,s/Math.max(p.width,p.height)),k=Math.min(l,l*d),_=await Ie(Promise.resolve(b.captureElement(t,{...Ne({mime:c,quality:u,pixelRatio:k,maxDimension:s,includeQuality:c==="image/jpeg",...o?{signal:o}:{}})})),a,o);m.element=_;}catch(p){if(o?.aborted)throw p;i.push(h("capture_failed",p,{target:"element"}));}if(n?.fullPage)try{let p=await Ie(Promise.resolve(b.captureFullPage(Ne({mime:c,quality:u,pixelRatio:l,maxDimension:s,includeQuality:c==="image/jpeg",...o?{signal:o}:{}}))),a,o);m.fullPage=p;}catch(p){if(o?.aborted)throw p;i.push(h("capture_failed",p,{target:"fullPage"}));}let w=Be(),f={startedAt:r,finishedAt:w,durationMs:Math.max(0,w-r)};return i.length>0&&(f.errors=i),{...m,diagnostics:f}}function Et(){try{return Intl.DateTimeFormat().resolvedOptions().timeZone}catch{return}}function vt(){return y()?{url:window.location.href,title:document.title,referrer:document.referrer||void 0,userAgent:navigator.userAgent,language:navigator.language,platform:navigator.platform,viewport:{width:window.innerWidth,height:window.innerHeight},screen:{width:window.screen.width,height:window.screen.height},scroll:{x:window.scrollX,y:window.scrollY},devicePixelRatio:window.devicePixelRatio||1,timezone:Et()}:{}}function St(e){if(!e)return {};let t={};return e.id&&(t.userId=e.id),e.email&&(t.userEmail=e.email),e.name&&(t.userName=e.name),t}async function Ue(e){let{config:t,context:n,user:o}=e;if(t?.enabled===false)return {};let r={...vt(),...St(o)},i=t?.enrich;if(!i)return r;try{let a=await i(n);return {...r,...a}}catch(a){let s=h("unknown",a);return {...r,blocfeedMetadataError:s.message}}}var me="blocfeed-queue",kt=50;function pe(){if(!y())return [];try{let e=localStorage.getItem(me);if(!e)return [];let t=JSON.parse(e);return Array.isArray(t)?t:[]}catch{return []}}function ge(e){if(y())try{e.length===0?localStorage.removeItem(me):localStorage.setItem(me,JSON.stringify(e));}catch{}}function _t(e){let t={...e};if(t.screenshots){let n={...t.screenshots};n.element&&(n.element={...n.element,dataUrl:""}),n.fullPage&&(n.fullPage={...n.fullPage,dataUrl:""}),t.screenshots=n;}return delete t.video,t}function he(e){let t=pe(),n=_t(e);for(n.metadata={...n.metadata,_queued:true},t.push({payload:n,timestamp:Date.now()});t.length>kt;)t.shift();ge(t);}function Ve(){let e=pe();return e.length===0?[]:(ge([]),e.map(t=>t.payload))}function En(){ge([]);}function vn(){return pe().length}var Tt=200,X=[],He=0,ee=false;function qe(e){let t=e.target;if(!(t instanceof Element)||t.closest("[data-blocfeed-ui]"))return;let n={timestampMs:Date.now()-He,path:window.location.pathname,tagName:t.tagName.toLowerCase()},o=ce(t,100);o&&(n.textSnippet=o);let r=le(t)??ue(t);r&&(n.componentName=r),X.length<Tt&&X.push(n);}function ze(){ee||!y()||(ee=true,X=[],He=Date.now(),document.addEventListener("click",qe,{capture:true,passive:true}));}function je(){ee&&(ee=false,document.removeEventListener("click",qe,{capture:true}));}function At(){return [...X]}function Rt(){X=[];}var xt=3e4,Pt=25e5,$e="video/webm",Ct=250,Ft=1e3,Mt=["video/webm;codecs=vp9","video/webm;codecs=vp8","video/webm"];function Xe(){if(typeof MediaRecorder>"u")return null;for(let e of Mt)if(MediaRecorder.isTypeSupported(e))return e;return null}function Dt(){return !y()||typeof navigator?.mediaDevices?.getDisplayMedia!="function"?false:Xe()!==null}async function we(e){let{config:t,signal:n}=e;if(!y()||typeof navigator?.mediaDevices?.getDisplayMedia!="function")throw h("recording_failed",new Error("Screen recording is not supported in this browser"));let o=Xe();if(!o)throw h("recording_failed",new Error("No supported video codec found (WebM required)"));if(n?.aborted)throw h("aborted",new Error("Recording aborted before start"));let r;try{r=await navigator.mediaDevices.getDisplayMedia({video:{displaySurface:"browser"},audio:!1});}catch(d){let k=d instanceof DOMException&&d.name==="NotAllowedError"?"Screen share permission denied":"Failed to start screen share";throw h("recording_failed",new Error(k))}if(n?.aborted)throw r.getTracks().forEach(d=>d.stop()),h("aborted",new Error("Recording aborted after permission"));let i=t?.maxDurationMs??xt,a=t?.videoBitsPerSecond??Pt,s=new MediaRecorder(r,{mimeType:o,videoBitsPerSecond:a}),c=[],u=[],b=0,m=null,l=null,w=false,f=()=>{je(),m!==null&&(clearInterval(m),m=null),l!==null&&(clearTimeout(l),l=null),r.getTracks().forEach(d=>d.stop());},p=new Promise((d,k)=>{s.ondataavailable=v=>{v.data.size>0&&c.push(v.data);},s.onstop=()=>{if(w)return;w=true,f();let v=Date.now()-b,C=new Blob(c,{type:$e}),L=URL.createObjectURL(C);d({mime:$e,blobUrl:L,blob:C,durationMs:v,sizeBytes:C.size});},s.onerror=()=>{w||(w=true,f(),k(h("recording_failed",new Error("MediaRecorder error"))));};let _=r.getVideoTracks()[0];if(_&&_.addEventListener("ended",()=>{s.state!=="inactive"&&s.stop();}),n){let v=()=>{w||(w=true,s.state!=="inactive"&&s.stop(),f(),k(h("aborted",new Error("Recording aborted"))));};n.addEventListener("abort",v,{once:true});}});return s.start(Ft),ze(),b=Date.now(),m=setInterval(()=>{let d=Date.now()-b;for(let k of u)k(d);},Ct),l=setTimeout(()=>{!w&&s.state!=="inactive"&&s.stop();},i),{result:p,stop(){!w&&s.state!=="inactive"&&s.stop();},onTick(d){u.push(d);},abort(){w||(w=true,s.state!=="inactive"&&s.stop(),f());}}}var Lt=6e4,Ze="audio/webm",Bt=250,It=["audio/webm;codecs=opus","audio/webm","audio/ogg;codecs=opus"];function We(){if(typeof MediaRecorder>"u")return null;for(let e of It)if(MediaRecorder.isTypeSupported(e))return e;return null}function Nt(){return !y()||typeof navigator?.mediaDevices?.getUserMedia!="function"?false:We()!==null}async function be(e){let{config:t,signal:n}=e;if(!y()||typeof navigator?.mediaDevices?.getUserMedia!="function")throw h("recording_failed",new Error("Microphone recording is not supported in this browser"));let o=We();if(!o)throw h("recording_failed",new Error("No supported audio codec found"));if(n?.aborted)throw h("aborted",new Error("Voice recording aborted before start"));let r;try{r=await navigator.mediaDevices.getUserMedia({audio:!0});}catch(p){let d=p instanceof DOMException&&p.name==="NotAllowedError"?"Microphone permission denied":"Failed to access microphone";throw h("recording_failed",new Error(d))}if(n?.aborted)throw r.getTracks().forEach(p=>p.stop()),h("aborted",new Error("Voice recording aborted after permission"));let i=t?.maxDurationMs??Lt,a=new MediaRecorder(r,{mimeType:o}),s=[],c=[],u=0,b=null,m=null,l=false,w=()=>{b!==null&&(clearInterval(b),b=null),m!==null&&(clearTimeout(m),m=null),r.getTracks().forEach(p=>p.stop());},f=new Promise((p,d)=>{if(a.ondataavailable=k=>{k.data.size>0&&s.push(k.data);},a.onstop=()=>{if(l)return;l=true,w();let k=Date.now()-u,_=new Blob(s,{type:Ze});p({blob:_,mime:Ze,durationMs:k});},a.onerror=()=>{l||(l=true,w(),d(h("recording_failed",new Error("MediaRecorder error"))));},n){let k=()=>{l||(l=true,a.state!=="inactive"&&a.stop(),w(),d(h("aborted",new Error("Voice recording aborted"))));};n.addEventListener("abort",k,{once:true});}});return a.start(1e3),u=Date.now(),b=setInterval(()=>{let p=Date.now()-u;for(let d of c)d(p);},Bt),m=setTimeout(()=>{!l&&a.state!=="inactive"&&a.stop();},i),{result:f,stop(){!l&&a.state!=="inactive"&&a.stop();},onTick(p){c.push(p);},abort(){l||(l=true,a.state!=="inactive"&&a.stop(),w());}}}var Ot=12e3,Ut=2,Vt=500,Ht=2e3,ye="https://blocfeed.com/api/feedback",qt="https://blocfeed.com/api/feedback/voice",Ke=0;function Qe(e,t){return new Promise((n,o)=>{if(t?.aborted){o(new Error("Aborted"));return}let r=setTimeout(n,e),i=()=>{clearTimeout(r),o(new Error("Aborted"));};t?.addEventListener("abort",i,{once:true});})}function zt(e){return e>=500&&e<=599}function jt(e){let[t,n]=e.split(",",2);if(!t||!n)throw new Error("Invalid data URL");let r=/data:(.*?);base64/.exec(t)?.[1]||"application/octet-stream",i=atob(n),a=new Uint8Array(i.length);for(let s=0;s<i.length;s+=1)a[s]=i.charCodeAt(s);return new Blob([a],{type:r})}function $t(e){let t={},n={},o={...e};if(o.screenshots){let r={},i={...o.screenshots};i.element&&(t.element=i.element.dataUrl,r.element={mime:i.element.mime,width:i.element.width,height:i.element.height},i.element={...i.element,dataUrl:""}),i.fullPage&&(t.fullPage=i.fullPage.dataUrl,r.fullPage={mime:i.fullPage.mime,width:i.fullPage.width,height:i.fullPage.height},i.fullPage={...i.fullPage,dataUrl:""}),o.screenshots=i,(r.element||r.fullPage)&&(o.screenshot_intent=r);}return o.video&&(n.blob=o.video.blob,o.video_intent={mime:o.video.mime,durationMs:o.video.durationMs,sizeBytes:o.video.sizeBytes},delete o.video),{lean:o,extracted:t,extractedVideo:n}}async function Ye(e,t,n){let o=jt(t);await fetch(e,{method:"PUT",body:o,headers:{"content-type":o.type},...n?{signal:n}:{}});}async function Xt(e){let{feedbackId:t,extracted:n,screenshots:o,signal:r}=e,i={};n.element&&o?.element&&(i.element={dataUrl:n.element,mime:o.element.mime,width:o.element.width,height:o.element.height}),n.fullPage&&o?.fullPage&&(i.fullPage={dataUrl:n.fullPage,mime:o.fullPage.mime,width:o.fullPage.width,height:o.fullPage.height}),Object.keys(i).length!==0&&await fetch(`${ye}/${t}/screenshots`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(i),...r?{signal:r}:{}});}async function Zt(e){await fetch(`${ye}/${e.feedbackId}/video`,{method:"POST",body:e.blob,headers:{"content-type":e.blob.type},...e.signal?{signal:e.signal}:{}});}async function Ge(e){let{signal:t,transport:n}=e;if(Date.now()-Ke<Ht)return {ok:false,error:h("configuration",new Error("Please wait before submitting again"))};let r=n?.timeoutMs??Ot,i=n?.maxAttempts??Ut,a=n?.backoffMs??Vt,s=!!(e.payload.screenshots?.element?.dataUrl||e.payload.screenshots?.fullPage?.dataUrl),c=!!e.payload.video?.blob,u=s||c,{lean:b,extracted:m,extractedVideo:l}=u?$t(e.payload):{lean:e.payload,extracted:{},extractedVideo:{}},w={...m,...e.screenshotDataUrls};for(let f=1;f<=i;f+=1){let p=new AbortController,d=setTimeout(()=>p.abort(),r),k=()=>p.abort();t&&t.addEventListener("abort",k,{once:true});try{let _=await fetch(ye,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(b),signal:p.signal});if(_.ok){Ke=Date.now();let v;try{v=await _.json();}catch{}if((w.element||w.fullPage)&&v){let F=v.upload_urls;if(F){let H=[];w.element&&F.element&&H.push(Ye(F.element,w.element,t)),w.fullPage&&F.fullPage&&H.push(Ye(F.fullPage,w.fullPage,t));try{await Promise.all(H);}catch{}}else if(v.feedback_id)try{await Xt({feedbackId:v.feedback_id,extracted:w,screenshots:e.payload.screenshots,...t?{signal:t}:{}});}catch{}}if(l.blob&&v){let F=v.upload_urls;if(F?.video)try{await fetch(F.video,{method:"PUT",body:l.blob,headers:{"content-type":l.blob.type},...t?{signal:t}:{}});}catch{}else if(v.feedback_id)try{await Zt({feedbackId:v.feedback_id,blob:l.blob,...t?{signal:t}:{}});}catch{}}let L={ok:!0,status:_.status};return v&&(L.apiResponse=v),L}if(f<i&&zt(_.status)){let v=.85+Math.random()*.3,C=Math.round(a*2**(f-1)*v);await Qe(C,t);continue}return {ok:!1,status:_.status,error:h("api_failed",new Error(`HTTP ${_.status}`))}}catch(_){if(p.signal.aborted||t?.aborted)return {ok:false,error:h("aborted",_)};if(f<i){let v=.85+Math.random()*.3,C=Math.round(a*2**(f-1)*v);await Qe(C,t);continue}return {ok:false,error:h("api_failed",_)}}finally{clearTimeout(d),t&&t.removeEventListener("abort",k);}}return {ok:false,error:h("api_failed",new Error("Failed"))}}async function Je(e){let{blob:t,mime:n,blocfeedId:o,signal:r}=e,i=await fetch(qt,{method:"POST",headers:{"Content-Type":n,"X-Blocfeed-Id":o},body:t,...r?{signal:r}:{}});if(!i.ok){let s=await i.json().catch(()=>({error:"Unknown error"}));throw h("api_failed",new Error(s?.error??`Voice API returned ${i.status}`))}let a=await i.json();return {text:a.text??"",warning:a.warning}}function Ee(e){let t=null,n=null,o=(...r)=>{n=r,t===null&&(t=requestAnimationFrame(()=>{if(t=null,!n)return;let i=n;n=null,e(...i);}));};return o.cancel=()=>{t!==null&&cancelAnimationFrame(t),t=null,n=null;},o}function te(e){return e instanceof Element?!!e.closest("[data-blocfeed-ui]"):false}function ne(e){e.stopPropagation(),e.stopImmediatePropagation?.();}function et(e,t){if(!y())throw new Error("BlocFeed picker can only run in a browser environment.");let n=e.ignoreSelectors,o=e.isSelectable,r={};n&&n.length>0&&(r.ignoreSelectors=n),o&&(r.isSelectable=o);let i=null,a=null,s=(f,p=false)=>{if(!f){i=null,a=null,t.onHover(null);return}let d=se(f.getBoundingClientRect()),k=`${Math.round(d.x)}:${Math.round(d.y)}:${Math.round(d.width)}:${Math.round(d.height)}`;!p&&f===i&&k===a||(i=f,a=k,t.onHover({element:f,rect:d}));},c=Ee(f=>{if(te(f.target))return;let p=document.elementFromPoint(f.clientX,f.clientY),d=de(p,r);s(d);}),u=Ee(()=>{i&&s(i,true);}),b=f=>{te(f.target)||(ne(f),f.pointerType==="mouse"&&f.preventDefault());},m=f=>{te(f.target)||(ne(f),f.pointerType==="mouse"&&f.preventDefault());},l=f=>{if(te(f.target))return;ne(f),f.preventDefault();let p=document.elementFromPoint(f.clientX,f.clientY),d=de(p,r);d&&t.onSelect({element:d,descriptor:Fe(d)});},w=f=>{f.key==="Escape"&&(ne(f),f.preventDefault(),t.onCancel());};return window.addEventListener("pointermove",c,{capture:true,passive:true}),window.addEventListener("pointerdown",b,{capture:true}),window.addEventListener("pointerup",m,{capture:true}),window.addEventListener("click",l,{capture:true}),window.addEventListener("keydown",w,{capture:true}),window.addEventListener("scroll",u,{capture:true,passive:true}),window.addEventListener("resize",u,{passive:true}),{stop(){window.removeEventListener("pointermove",c,{capture:true}),window.removeEventListener("pointerdown",b,{capture:true}),window.removeEventListener("pointerup",m,{capture:true}),window.removeEventListener("click",l,{capture:true}),window.removeEventListener("keydown",w,{capture:true}),window.removeEventListener("scroll",u,{capture:true}),window.removeEventListener("resize",u),c.cancel(),u.cancel(),t.onHover(null);}}}async function ve(e){let{signal:t,transport:n}=e,o={ok:false};try{let r={payload:e.payload,...t?{signal:t}:{},...n?{transport:n}:{}};o.api=await Ge(r),o.ok=!!o.api?.ok;}catch(r){o.api={ok:false,error:h("api_failed",r)},o.ok=false;}return {payload:e.payload,result:o}}var Wt=["[data-blocfeed-ui]","[data-blocfeed-ignore]"];function Kt(e){let t=[...Wt,...e?.ignoreSelectors??[]],n=Array.from(new Set(t));return {...e,ignoreSelectors:n}}function tt(){return {phase:"idle"}}function Qt(e){if(e.ok)return false;let t=e.api;return t?t.status&&t.status>=400&&t.status<500||t.error?.kind==="aborted"||t.error?.kind==="configuration"?false:!t.ok:true}function no(e){let t=e,n=tt(),o=new Set,r=new Set,i=null,a=null,s=null,c=null,u=0,b=null,m=null,l=null,w=null,f=()=>{for(let g of o)g(n);},p=g=>{for(let E of r)E(g);},d=g=>{n=g,f();},k=()=>{u+=1,c?.abort(),c=null;},_=()=>{i?.stop(),i=null,p(null),s!==null&&y()&&(document.documentElement.style.cursor=s,s=null);},v=()=>{m&&(m.abort(),m=null),l&&(URL.revokeObjectURL(l.blobUrl),l=null);},C=()=>{w&&(w.abort(),w=null);},L=()=>{k(),_(),v(),C(),a=null,d(tt());},F=()=>{if(!y())return;_(),a=null;let g=Kt(t.picker);s=document.documentElement.style.cursor,document.documentElement.style.cursor="crosshair",d({phase:"picking"}),i=et(g,{onHover:p,onSelect:({element:E,descriptor:x})=>{a=E,_(),d({phase:"review",selection:x});},onCancel:()=>{L();}});},H=()=>{let g=Ve();if(g.length!==0)for(let E of g)ve({payload:E,...t.transport?{transport:t.transport}:{}}).catch(()=>{he(E);});};if(y()){setTimeout(H,1e3);let g=()=>H();window.addEventListener("online",g),b=()=>window.removeEventListener("online",g);}return {getState:()=>n,subscribe(g){return o.add(g),()=>o.delete(g)},subscribeHover(g){return r.add(g),()=>r.delete(g)},start(){n.phase==="capturing"||n.phase==="submitting"||n.phase==="recording"||n.phase!=="picking"&&F();},stop(){L();},clearSelection(){n.phase==="capturing"||n.phase==="submitting"||n.phase==="recording"||F();},setConfig(g){t=g;},async submit(g,E){if(!y()){let T=h("configuration",new Error("BlocFeed submit can only run in the browser"));return d({phase:"error",lastError:T}),{ok:false}}let x=t.blocfeed_id?.trim?.()??"";if(!x){let M={phase:"error",lastError:h("configuration",new Error("Missing blocfeed_id. Create a project in BlocFeed and pass its blocfeed_id."))};return n.selection&&(M.selection=n.selection),d(M),{ok:false}}if(n.phase==="capturing"||n.phase==="submitting"||n.phase==="recording")return {ok:false};let R=u+1;u=R,c?.abort(),c=new AbortController;let A=c.signal,S=n.selection,D=E?.capture?{...t.capture,...E.capture}:t.capture,B=!!(D?.element||D?.fullPage),z={phase:B?"capturing":"submitting"};S&&(z.selection=S),d(z);try{let T=B?await Oe({selectionElement:a,capture:D,signal:A}):void 0;if(A.aborted||u!==R)return {ok:!1};let M={phase:"submitting"};S&&(M.selection=S),T&&(M.capture=T),d(M);let I={};S&&(I.selection=S),T&&(I.capture=T);let j=await Ue({config:t.metadata,context:I,...t.user?{user:t.user}:{}}),O={version:1,createdAt:new Date().toISOString(),blocfeed_id:x,message:g,metadata:j};E?.category&&(O.category=E.category),t.user&&(O.user=t.user),S&&(O.selection=S),T&&(O.screenshots=T),l&&(O.video=l);let{result:N}=await ve({payload:O,signal:A,...t.transport?{transport:t.transport}:{}});if(A.aborted||u!==R)return N;if(N.ok){l&&(URL.revokeObjectURL(l.blobUrl),l=null);let ae={phase:"success",lastSubmit:N};return S&&(ae.selection=S),T&&(ae.capture=T),d(ae),N}Qt(N)&&he(O);let rt=N.api?.error??h("unknown",new Error("Submission failed")),ie={phase:"error",lastSubmit:N,lastError:rt};return S&&(ie.selection=S),T&&(ie.capture=T),d(ie),N}catch(T){if(A.aborted||u!==R)return {ok:false};let I={phase:"error",lastError:A.aborted?h("aborted",T):h("unknown",T)};return S&&(I.selection=S),d(I),{ok:false}}finally{u===R&&(c=null);}},async startRecording(){if(n.phase!=="review"||!t.recording?.enabled)return;let g=n.selection,E={phase:"recording",recordingElapsedMs:0};g&&(E.selection=g),d(E);try{let x={config:t.recording};c&&(x.signal=c.signal);let R=await we(x);m=R,R.onTick(D=>{if(n.phase==="recording"){let B={phase:"recording",recordingElapsedMs:D};g&&(B.selection=g),d(B);}});let A=await R.result;m=null,l=A;let S={phase:"review",video:A};g&&(S.selection=g),d(S);}catch(x){m=null;let A={phase:"review",lastError:x?.kind?x:h("recording_failed",x)};g&&(A.selection=g),d(A);}},stopRecording(){n.phase!=="recording"||!m||m.stop();},removeVideo(){l&&(URL.revokeObjectURL(l.blobUrl),l=null);let g=n.selection,E={phase:"review"};g&&(E.selection=g),d(E);},async startVoice(){if(n.phase!=="review"){console.warn("[BlocFeed] startVoice: ignored \u2014 phase is",n.phase,'(expected "review")');return}let g=t.voice;if(!g?.enabled){console.warn("[BlocFeed] startVoice: ignored \u2014 voice is not enabled in config");return}C();let E=n.selection,x={phase:"review",voiceRecording:true,voiceElapsedMs:0};E&&(x.selection=E),l&&(x.video=l),d(x);try{let R={config:g};c&&(R.signal=c.signal);let A=await be(R);w=A,A.onTick(I=>{let j={phase:"review",voiceRecording:!0,voiceElapsedMs:I};E&&(j.selection=E),l&&(j.video=l),d(j);});let S=await A.result;w=null;let D={phase:"review",voiceRecording:!1,voiceTranscribing:!0};E&&(D.selection=E),l&&(D.video=l),d(D);let B={blob:S.blob,mime:S.mime,blocfeedId:t.blocfeed_id};c&&(B.signal=c.signal);let{text:Te,warning:z}=await Je(B),T={phase:"review",voiceRecording:!1,voiceTranscribing:!1};E&&(T.selection=E),l&&(T.video=l),d(T);let M={text:Te};return z&&(M.warning=z),M}catch(R){C();let A=R?.kind?R:h("recording_failed",R);console.warn("[BlocFeed] Voice error:",A.message);let S={phase:"review",voiceRecording:false,voiceTranscribing:false,lastError:A};E&&(S.selection=E),l&&(S.video=l),d(S);return}},stopVoice(){w&&w.stop();},__unsafeGetSelectedElement(){return a},destroy(){L(),o.clear(),r.clear(),b?.(),b=null;}}}var Z=[],W=[],nt=20,ot=15,oe=[],K={},Q=null,Y=null,G=null,re=false,Yt=["blocfeed.com","google-analytics.com","googletagmanager.com","analytics.google.com","googleads.g.doubleclick.net","googlesyndication.com","googleadservices.com","google.com/pagead","google.com/ads","facebook.net","facebook.com/tr","connect.facebook.net","graph.facebook.com","api.mixpanel.com","api-js.mixpanel.com","api.amplitude.com","api2.amplitude.com","api.segment.io","cdn.segment.com","vars.hotjar.com","in.hotjar.com","script.hotjar.com","heapanalytics.com","fullstory.com/s/fs.js","rs.fullstory.com","app.posthog.com","us.posthog.com","eu.posthog.com","api-iam.intercom.io","widget.intercom.io","sentry.io/api","browser.sentry-cdn.com","browser-intake-datadoghq.com","clarity.ms","js.hs-analytics.net","api.hubapi.com","forms.hubspot.com","plausible.io/api","client.crisp.chat","js.driftt.com","r.logrocket.com","app.pendo.io","events.launchdarkly.com","app.launchdarkly.com","grammarly.com","gnar.grammarly.com","capi.grammarly.com","api.languagetool.org","api.languagetoolplus.com","fonts.googleapis.com","fonts.gstatic.com","cdn.cookielaw.org","consent.cookiebot.com","js.stripe.com","api.stripe.com/v1/tokens","google.com/recaptcha","hcaptcha.com","bam.nr-data.net","rec.smartlook.com","o2.mouseflow.com","api.luckyorange.com","static.zdassets.com","ekr.zdassets.com"];function Gt(e){if(e instanceof Error)return e.message;if(typeof e=="string")return e;try{return JSON.stringify(e)}catch{return String(e)}}function Jt(e,t){let n=t.map(Gt).join(" "),o=t.find(i=>i instanceof Error),r={level:e,message:n.slice(0,2e3),timestamp:Date.now()};for(o?.stack&&(r.stack=o.stack.slice(0,2e3)),Z.push(r);Z.length>nt;)Z.shift();}function Se(e){let t=e.url.toLowerCase();for(let n of oe)if(t.includes(n))return;for(W.push(e);W.length>ot;)W.shift();}function io(e={}){if(re||!y())return;re=true,nt=e.consoleLimit??20,ot=e.networkLimit??15;let t=e.ignoreUrls;if(t!==void 0?oe=t.map(n=>n.toLowerCase()):oe=[...Yt],e.console!==false){let n=e.consoleLevels??["error","warn"];for(let o of n)K[o]=console[o],console[o]=(...r)=>{Jt(o,r),K[o]?.apply(console,r);};}if(e.network!==false&&typeof window.fetch=="function"){Q=window.fetch;let n=Q;window.fetch=async function(r,i){let a=typeof r=="string"?r:r instanceof URL?r.toString():r.url,s=(i?.method??"GET").toUpperCase(),c=Date.now();try{let u=await n.call(window,r,i);return u.ok||Se({url:a.slice(0,500),method:s,status:u.status,statusText:u.statusText,timestamp:c,durationMs:Date.now()-c}),u}catch(u){throw Se({url:a.slice(0,500),method:s,status:0,statusText:u instanceof Error?u.message:"Network error",timestamp:c,durationMs:Date.now()-c}),u}};}if(e.network!==false&&typeof XMLHttpRequest<"u"){Y=XMLHttpRequest.prototype.open,G=XMLHttpRequest.prototype.send;let n=Y,o=G;XMLHttpRequest.prototype.open=function(r,i,...a){return this.__bf_method=r.toUpperCase(),this.__bf_url=String(i),n.apply(this,[r,i,...a])},XMLHttpRequest.prototype.send=function(...r){let i=this.__bf_method||"GET",a=this.__bf_url||"",s=Date.now();return this.addEventListener("loadend",function(){if(this.status>=400||this.status===0){let c={url:a.slice(0,500),method:i,status:this.status,timestamp:s,durationMs:Date.now()-s};this.statusText&&(c.statusText=this.statusText),Se(c);}}),o.apply(this,r)};}}function ao(){if(re){for(let[e,t]of Object.entries(K))console[e]=t;for(let e of Object.keys(K))delete K[e];Q&&(window.fetch=Q,Q=null),Y&&(XMLHttpRequest.prototype.open=Y,Y=null),G&&(XMLHttpRequest.prototype.send=G,G=null),oe=[],re=false;}}function so(){return {consoleLogs:[...Z],networkErrors:[...W]}}function co(){Z=[],W=[];}var en=[{name:"supabase_service_role_key",pattern:/SUPABASE_SERVICE_ROLE_KEY["'=:\s]+[A-Za-z0-9_.=-]{30,}/},{name:"supabase_db_password",pattern:/SUPABASE_DB_PASSWORD["'=:\s]+[^\s"']{6,}/},{name:"postgres_password",pattern:/POSTGRES_PASSWORD["'=:\s]+[^\s"']{6,}/},{name:"database_url_with_creds",pattern:/(?:postgres|postgresql|mysql|mongodb|redis|amqp):\/\/[^:]+:[^@\s"']+@/},{name:"aws_access_key",pattern:/AKIA[0-9A-Z]{16}/},{name:"aws_secret_key",pattern:/AWS_SECRET_ACCESS_KEY["'=:\s]+[A-Za-z0-9/+=]{30,}/},{name:"aws_session_token",pattern:/AWS_SESSION_TOKEN["'=:\s]+[A-Za-z0-9/+=]{30,}/},{name:"stripe_secret_key",pattern:/sk_live_[a-zA-Z0-9]{10,}/},{name:"stripe_secret_key_test",pattern:/sk_test_[a-zA-Z0-9]{10,}/},{name:"stripe_restricted_key",pattern:/rk_(?:live|test)_[a-zA-Z0-9]{10,}/},{name:"github_pat",pattern:/ghp_[A-Za-z0-9_]{36,}/},{name:"github_oauth",pattern:/gho_[A-Za-z0-9_]{36,}/},{name:"github_user_token",pattern:/ghu_[A-Za-z0-9_]{36,}/},{name:"github_server_token",pattern:/ghs_[A-Za-z0-9_]{36,}/},{name:"github_fine_grained",pattern:/github_pat_[A-Za-z0-9_]{22,}/},{name:"openai_api_key",pattern:/sk-[a-zA-Z0-9]{20,}(?![a-zA-Z0-9_])/},{name:"anthropic_api_key",pattern:/sk-ant-[a-zA-Z0-9\-_]{20,}/},{name:"private_key",pattern:/-----BEGIN (?:RSA |EC |DSA |OPENSSH |PGP )?PRIVATE KEY-----/},{name:"sendgrid_api_key",pattern:/SG\.[a-zA-Z0-9_-]{22,}\.[a-zA-Z0-9_-]{22,}/},{name:"twilio_auth_token",pattern:/TWILIO_AUTH_TOKEN["'=:\s]+[a-f0-9]{32}/},{name:"generic_secret_key",pattern:/[A-Z_]{2,}_SECRET_KEY["'=:\s]+[^\s"']{8,}/},{name:"generic_secret",pattern:/[A-Z_]{2,}_SECRET["'=:\s]+[^\s"']{8,}/},{name:"generic_password",pattern:/[A-Z_]{2,}_PASSWORD["'=:\s]+[^\s"']{6,}/},{name:"generic_private_key_var",pattern:/[A-Z_]{2,}_PRIVATE_KEY["'=:\s]+[^\s"']{8,}/}],V=[],_e=0,ke=false;function tn(e){let t=e.slice(0,80);return t.length<=6?"***":t.slice(0,6)+"***"}function nn(e,t,n,o){if(V.some(a=>a.rule===e&&a.source===t))return;let i={rule:e,source:t,hint:tn(n),timestamp:Date.now()};o&&(i.location=o),V.push(i);}function J(e,t,n,o){for(let{name:r,pattern:i}of n){let a=i.exec(e);a&&nn(r,t,a[0],o);}}function on(e){try{let t=window;if(t.__NEXT_DATA__){let n=JSON.stringify(t.__NEXT_DATA__);J(n,"hydration",e,"__NEXT_DATA__");}if(t.__NUXT__){let n=JSON.stringify(t.__NUXT__);J(n,"hydration",e,"__NUXT__");}}catch{}}function rn(e){try{document.querySelectorAll("script:not([src])").forEach((n,o)=>{let r=n.textContent;r&&r.length>0&&J(r,"scripts",e,`inline-script[${o}]`);});}catch{}}function an(e){try{document.querySelectorAll("meta[content]").forEach(n=>{let o=n.getAttribute("content"),r=n.getAttribute("name")||n.getAttribute("property")||"";o&&o.length>10&&J(o,"meta",e,r?`meta[${r}]`:void 0);});}catch{}}function sn(e){try{document.querySelectorAll("[data-api-key], [data-secret], [data-token], [data-password]").forEach(n=>{let o=n.attributes;for(let r=0;r<o.length;r++){let i=o[r];i.name.startsWith("data-")&&i.value.length>10&&J(i.value,"dom",e,`${n.tagName.toLowerCase()}[${i.name}]`);}});}catch{}}function fo(e={}){if(ke||!y()||(ke=true,e.secretScan===false))return;let t=[...en,...e.customPatterns??[]],n=e.scanTargets??["hydration","scripts","meta","dom"];setTimeout(()=>{_e=Date.now(),n.includes("hydration")&&on(t),n.includes("scripts")&&rn(t),n.includes("meta")&&an(t),n.includes("dom")&&sn(t);let o=e.notify??"both";V.length>0&&(o==="console"||o==="both")&&console.warn(`[BlocFeed Security] Found ${V.length} potential secret(s) exposed in client code:`,V.map(r=>`${r.rule} (${r.source}${r.location?`: ${r.location}`:""})`));},100);}function mo(){return {findings:[...V],scannedAt:_e}}function po(){V=[],_e=0,ke=false;}
2
+ exports.a=y;exports.b=se;exports.c=Le;exports.d=Oe;exports.e=Ue;exports.f=he;exports.g=Ve;exports.h=En;exports.i=vn;exports.j=At;exports.k=Rt;exports.l=Dt;exports.m=we;exports.n=Nt;exports.o=be;exports.p=Je;exports.q=no;exports.r=io;exports.s=ao;exports.t=so;exports.u=co;exports.v=fo;exports.w=mo;exports.x=po;
@@ -0,0 +1,2 @@
1
+ function y(){return typeof window<"u"&&typeof document<"u"}function Ae(e){let t=globalThis.CSS;return typeof t?.escape=="function"?t.escape(e):e.replace(/[^a-zA-Z0-9_-]/g,n=>{let o=n.codePointAt(0);return o===void 0?"":`\\${o.toString(16)} `})}function se(e){return {x:e.x,y:e.y,width:e.width,height:e.height}}function it(e){return {x:e.x+window.scrollX,y:e.y+window.scrollY,width:e.width,height:e.height}}function at(e){return e.replace(/\s+/g," ").trim()}function ce(e,t=140){let n=e.textContent;if(!n)return;let o=at(n);if(o)return o.length<=t?o:`${o.slice(0,t-1)}\u2026`}function st(e){let t=1;for(let n=e.previousElementSibling;n;n=n.previousElementSibling)n.tagName===e.tagName&&(t+=1);return t}var Ce=["data-testid","data-test-id","data-test","data-qa","data-cy"],Re="data-blocfeed-component";function le(e){let t=e.closest(`[${Re}]`);if(!t)return;let o=t.getAttribute(Re)?.trim();return o||void 0}function ct(e){for(let t of Ce){let n=e.closest(`[${t}]`);if(!n)continue;let r=n.getAttribute(t)?.trim();if(r)return r}}function xe(e){try{let t=e,n=Object.getOwnPropertyNames(t);for(let o of n)if(o.startsWith("__reactFiber$")||o.startsWith("__reactInternalInstance$")){let r=t[o];if(r&&typeof r=="object")return r}}catch{}return null}function U(e){if(e.length<=1||e.length===2&&e[0]===e[0].toLowerCase())return true;let t=e[0];return t>="a"&&t<="z"}function $(e){if(e){for(let t of e)if(typeof t.name=="string"&&t.name&&!U(t.name))return t.name}}function P(e){if(e&&typeof e!="string"){if(typeof e=="function"){let t=e;return typeof t.displayName=="string"&&t.displayName?t.displayName:typeof t.name=="string"&&t.name?t.name:void 0}if(typeof e=="object"){let t=e,n=t.displayName;if(typeof n=="string"&&n)return n;let o=t.render;if(typeof o=="function"){let i=o;if(typeof i.displayName=="string"&&i.displayName)return i.displayName;if(typeof i.name=="string"&&i.name)return i.name}let r=t.type;return P(r)}}}function ue(e){let t=xe(e);if(!t)return;let n=$(t._debugInfo);if(n)return n;let o=t._debugOwner!==void 0;if(o){let c=t._debugOwner;for(let u=0;c&&u<50;u+=1){let b=$(c._debugInfo);if(b)return b;let m=P(c.type)??P(c.elementType);if(m&&!U(m))return m;c=c._debugOwner;}}if(t._owner!==void 0&&t._owner!==t._debugOwner){let c=t._owner;for(let u=0;c&&u<50;u+=1){let b=$(c._debugInfo);if(b)return b;let m=P(c.type)??P(c.elementType);if(m&&!U(m))return m;c=c._owner;}}let i=t,a=o?80:25;for(let c=0;i&&c<a;c+=1){let u=$(i._debugInfo);if(u)return u;let b=P(i.type)??P(i.elementType);if(b&&!U(b))return b;i=i.return;}let s=e.parentElement;for(let c=0;s&&c<15;c+=1){let u=xe(s);if(u){let b=$(u._debugInfo);if(b)return b;let m=P(u.type)??P(u.elementType);if(m&&!U(m))return m;if(u._debugOwner){let l=P(u._debugOwner.type)??P(u._debugOwner.elementType);if(l&&!U(l))return l}if(u._owner&&u._owner!==u._debugOwner){let l=P(u._owner.type)??P(u._owner.elementType);if(l&&!U(l))return l}}s=s.parentElement;}}function lt(e){let t=e.tagName.toLowerCase(),n=e.getAttribute("id");if(n)return `#${Ae(n)}`;for(let o of Ce){let r=e.getAttribute(o);if(r)return `${t}[${o}="${Ae(r)}"]`}return `${t}:nth-of-type(${st(e)})`}function ut(e,t=10){let n=[],o=e;for(;o&&n.length<t;){let r=lt(o);if(n.unshift(r),r.startsWith("#"))break;o=o.parentElement;}return n.join(" > ")}function Pe(e,t){if(!t||t.length===0)return false;for(let n of t)if(e.closest(n))return true;return false}function de(e,t){if(!e||Pe(e,t.ignoreSelectors))return null;let n=e;for(;n;){if(Pe(n,t.ignoreSelectors))return null;if(t.isSelectable?.(n)??dt(n))return n;n=n.parentElement;}return null}function dt(e){let t=e.tagName;return !(t==="HTML"||t==="BODY")}function Fe(e){let t=e.getBoundingClientRect(),n={selector:ut(e),tagName:e.tagName.toLowerCase(),rect:se(t),pageRect:it(t)},o=e.getAttribute("id");o&&(n.id=o);let r=e.className;typeof r=="string"&&r.trim()&&(n.className=r);let i=ce(e);i&&(n.textSnippet=i);let a=le(e)??ue(e);a&&(n.componentName=a);let s=ct(e);return s&&(n.testId=s),n}var fe=null;async function Me(){return fe||(fe=import('html-to-image')),fe}async function ft(e){return await new Promise((t,n)=>{let o=new Image;o.onload=()=>t({width:o.naturalWidth,height:o.naturalHeight}),o.onerror=()=>n(new Error("Failed to load generated screenshot")),o.src=e;})}async function De(e,t){let{width:n,height:o}=await ft(e);return {dataUrl:e,mime:t,width:n,height:o}}function q(e){if(e?.aborted)throw new Error("Aborted")}function Le(){return {async captureElement(e,t){if(!y())throw new Error("captureElement can only run in the browser");q(t.signal);let n=await Me();q(t.signal);let o=t.mime==="image/jpeg"?await n.toJpeg(e,{quality:t.quality??.92,pixelRatio:t.pixelRatio}):await n.toPng(e,{pixelRatio:t.pixelRatio});return q(t.signal),await De(o,t.mime)},async captureFullPage(e){if(!y())throw new Error("captureFullPage can only run in the browser");q(e.signal);let t=document.documentElement,n=Math.max(t.scrollWidth,t.clientWidth),o=Math.max(t.scrollHeight,t.clientHeight),r=Math.min(1,e.maxDimension/Math.max(n,o)),i=Math.max(1,Math.round(n*r)),a=Math.max(1,Math.round(o*r)),s=await Me();q(e.signal);let c=e.mime==="image/jpeg"?await s.toJpeg(t,{width:i,height:a,quality:e.quality??.92,pixelRatio:e.pixelRatio}):await s.toPng(t,{width:i,height:a,pixelRatio:e.pixelRatio});return q(e.signal),await De(c,e.mime)}}}function mt(e){if(e instanceof Error)return e.message;if(typeof e=="string")return e;try{return JSON.stringify(e)}catch{return "Unknown error"}}function h(e,t,n){let o={kind:e,message:mt(t)};return n&&(o.detail=n),o}var pt=12e3,gt=2048,ht=.92;function Be(){return Date.now()}function wt(e){return new Promise((t,n)=>{let o=()=>n(new Error("Aborted"));if(e.aborted){o();return}e.addEventListener("abort",o,{once:true});})}async function Ie(e,t,n){let o=new Promise((i,a)=>{let s=setTimeout(()=>a(new Error("Timeout")),t);typeof s.unref=="function"&&s.unref();}),r=[e,o];return n&&r.push(wt(n)),await Promise.race(r)}function bt(e){if(!y())return 1;let t=window.devicePixelRatio||1,n=e?.pixelRatio??Math.min(t,2);return Math.max(.1,n)}function yt(e){return !!(e?.element||e?.fullPage)}function Ne(e){let t={mime:e.mime,pixelRatio:e.pixelRatio,maxDimension:e.maxDimension};return e.includeQuality&&(t.quality=e.quality),e.signal&&(t.signal=e.signal),t}async function Oe(e){let{selectionElement:t,capture:n,signal:o}=e;if(!y()||!yt(n))return;let r=Be(),i=[],a=n?.timeoutMs??pt,s=n?.maxDimension??gt,c=n?.mime??"image/png",u=n?.quality??ht,b=n?.adapter??Le(),m={},l=bt(n);if(n?.element&&t)try{let p=t.getBoundingClientRect(),d=Math.min(1,s/Math.max(p.width,p.height)),k=Math.min(l,l*d),_=await Ie(Promise.resolve(b.captureElement(t,{...Ne({mime:c,quality:u,pixelRatio:k,maxDimension:s,includeQuality:c==="image/jpeg",...o?{signal:o}:{}})})),a,o);m.element=_;}catch(p){if(o?.aborted)throw p;i.push(h("capture_failed",p,{target:"element"}));}if(n?.fullPage)try{let p=await Ie(Promise.resolve(b.captureFullPage(Ne({mime:c,quality:u,pixelRatio:l,maxDimension:s,includeQuality:c==="image/jpeg",...o?{signal:o}:{}}))),a,o);m.fullPage=p;}catch(p){if(o?.aborted)throw p;i.push(h("capture_failed",p,{target:"fullPage"}));}let w=Be(),f={startedAt:r,finishedAt:w,durationMs:Math.max(0,w-r)};return i.length>0&&(f.errors=i),{...m,diagnostics:f}}function Et(){try{return Intl.DateTimeFormat().resolvedOptions().timeZone}catch{return}}function vt(){return y()?{url:window.location.href,title:document.title,referrer:document.referrer||void 0,userAgent:navigator.userAgent,language:navigator.language,platform:navigator.platform,viewport:{width:window.innerWidth,height:window.innerHeight},screen:{width:window.screen.width,height:window.screen.height},scroll:{x:window.scrollX,y:window.scrollY},devicePixelRatio:window.devicePixelRatio||1,timezone:Et()}:{}}function St(e){if(!e)return {};let t={};return e.id&&(t.userId=e.id),e.email&&(t.userEmail=e.email),e.name&&(t.userName=e.name),t}async function Ue(e){let{config:t,context:n,user:o}=e;if(t?.enabled===false)return {};let r={...vt(),...St(o)},i=t?.enrich;if(!i)return r;try{let a=await i(n);return {...r,...a}}catch(a){let s=h("unknown",a);return {...r,blocfeedMetadataError:s.message}}}var me="blocfeed-queue",kt=50;function pe(){if(!y())return [];try{let e=localStorage.getItem(me);if(!e)return [];let t=JSON.parse(e);return Array.isArray(t)?t:[]}catch{return []}}function ge(e){if(y())try{e.length===0?localStorage.removeItem(me):localStorage.setItem(me,JSON.stringify(e));}catch{}}function _t(e){let t={...e};if(t.screenshots){let n={...t.screenshots};n.element&&(n.element={...n.element,dataUrl:""}),n.fullPage&&(n.fullPage={...n.fullPage,dataUrl:""}),t.screenshots=n;}return delete t.video,t}function he(e){let t=pe(),n=_t(e);for(n.metadata={...n.metadata,_queued:true},t.push({payload:n,timestamp:Date.now()});t.length>kt;)t.shift();ge(t);}function Ve(){let e=pe();return e.length===0?[]:(ge([]),e.map(t=>t.payload))}function En(){ge([]);}function vn(){return pe().length}var Tt=200,X=[],He=0,ee=false;function qe(e){let t=e.target;if(!(t instanceof Element)||t.closest("[data-blocfeed-ui]"))return;let n={timestampMs:Date.now()-He,path:window.location.pathname,tagName:t.tagName.toLowerCase()},o=ce(t,100);o&&(n.textSnippet=o);let r=le(t)??ue(t);r&&(n.componentName=r),X.length<Tt&&X.push(n);}function ze(){ee||!y()||(ee=true,X=[],He=Date.now(),document.addEventListener("click",qe,{capture:true,passive:true}));}function je(){ee&&(ee=false,document.removeEventListener("click",qe,{capture:true}));}function At(){return [...X]}function Rt(){X=[];}var xt=3e4,Pt=25e5,$e="video/webm",Ct=250,Ft=1e3,Mt=["video/webm;codecs=vp9","video/webm;codecs=vp8","video/webm"];function Xe(){if(typeof MediaRecorder>"u")return null;for(let e of Mt)if(MediaRecorder.isTypeSupported(e))return e;return null}function Dt(){return !y()||typeof navigator?.mediaDevices?.getDisplayMedia!="function"?false:Xe()!==null}async function we(e){let{config:t,signal:n}=e;if(!y()||typeof navigator?.mediaDevices?.getDisplayMedia!="function")throw h("recording_failed",new Error("Screen recording is not supported in this browser"));let o=Xe();if(!o)throw h("recording_failed",new Error("No supported video codec found (WebM required)"));if(n?.aborted)throw h("aborted",new Error("Recording aborted before start"));let r;try{r=await navigator.mediaDevices.getDisplayMedia({video:{displaySurface:"browser"},audio:!1});}catch(d){let k=d instanceof DOMException&&d.name==="NotAllowedError"?"Screen share permission denied":"Failed to start screen share";throw h("recording_failed",new Error(k))}if(n?.aborted)throw r.getTracks().forEach(d=>d.stop()),h("aborted",new Error("Recording aborted after permission"));let i=t?.maxDurationMs??xt,a=t?.videoBitsPerSecond??Pt,s=new MediaRecorder(r,{mimeType:o,videoBitsPerSecond:a}),c=[],u=[],b=0,m=null,l=null,w=false,f=()=>{je(),m!==null&&(clearInterval(m),m=null),l!==null&&(clearTimeout(l),l=null),r.getTracks().forEach(d=>d.stop());},p=new Promise((d,k)=>{s.ondataavailable=v=>{v.data.size>0&&c.push(v.data);},s.onstop=()=>{if(w)return;w=true,f();let v=Date.now()-b,C=new Blob(c,{type:$e}),L=URL.createObjectURL(C);d({mime:$e,blobUrl:L,blob:C,durationMs:v,sizeBytes:C.size});},s.onerror=()=>{w||(w=true,f(),k(h("recording_failed",new Error("MediaRecorder error"))));};let _=r.getVideoTracks()[0];if(_&&_.addEventListener("ended",()=>{s.state!=="inactive"&&s.stop();}),n){let v=()=>{w||(w=true,s.state!=="inactive"&&s.stop(),f(),k(h("aborted",new Error("Recording aborted"))));};n.addEventListener("abort",v,{once:true});}});return s.start(Ft),ze(),b=Date.now(),m=setInterval(()=>{let d=Date.now()-b;for(let k of u)k(d);},Ct),l=setTimeout(()=>{!w&&s.state!=="inactive"&&s.stop();},i),{result:p,stop(){!w&&s.state!=="inactive"&&s.stop();},onTick(d){u.push(d);},abort(){w||(w=true,s.state!=="inactive"&&s.stop(),f());}}}var Lt=6e4,Ze="audio/webm",Bt=250,It=["audio/webm;codecs=opus","audio/webm","audio/ogg;codecs=opus"];function We(){if(typeof MediaRecorder>"u")return null;for(let e of It)if(MediaRecorder.isTypeSupported(e))return e;return null}function Nt(){return !y()||typeof navigator?.mediaDevices?.getUserMedia!="function"?false:We()!==null}async function be(e){let{config:t,signal:n}=e;if(!y()||typeof navigator?.mediaDevices?.getUserMedia!="function")throw h("recording_failed",new Error("Microphone recording is not supported in this browser"));let o=We();if(!o)throw h("recording_failed",new Error("No supported audio codec found"));if(n?.aborted)throw h("aborted",new Error("Voice recording aborted before start"));let r;try{r=await navigator.mediaDevices.getUserMedia({audio:!0});}catch(p){let d=p instanceof DOMException&&p.name==="NotAllowedError"?"Microphone permission denied":"Failed to access microphone";throw h("recording_failed",new Error(d))}if(n?.aborted)throw r.getTracks().forEach(p=>p.stop()),h("aborted",new Error("Voice recording aborted after permission"));let i=t?.maxDurationMs??Lt,a=new MediaRecorder(r,{mimeType:o}),s=[],c=[],u=0,b=null,m=null,l=false,w=()=>{b!==null&&(clearInterval(b),b=null),m!==null&&(clearTimeout(m),m=null),r.getTracks().forEach(p=>p.stop());},f=new Promise((p,d)=>{if(a.ondataavailable=k=>{k.data.size>0&&s.push(k.data);},a.onstop=()=>{if(l)return;l=true,w();let k=Date.now()-u,_=new Blob(s,{type:Ze});p({blob:_,mime:Ze,durationMs:k});},a.onerror=()=>{l||(l=true,w(),d(h("recording_failed",new Error("MediaRecorder error"))));},n){let k=()=>{l||(l=true,a.state!=="inactive"&&a.stop(),w(),d(h("aborted",new Error("Voice recording aborted"))));};n.addEventListener("abort",k,{once:true});}});return a.start(1e3),u=Date.now(),b=setInterval(()=>{let p=Date.now()-u;for(let d of c)d(p);},Bt),m=setTimeout(()=>{!l&&a.state!=="inactive"&&a.stop();},i),{result:f,stop(){!l&&a.state!=="inactive"&&a.stop();},onTick(p){c.push(p);},abort(){l||(l=true,a.state!=="inactive"&&a.stop(),w());}}}var Ot=12e3,Ut=2,Vt=500,Ht=2e3,ye="https://blocfeed.com/api/feedback",qt="https://blocfeed.com/api/feedback/voice",Ke=0;function Qe(e,t){return new Promise((n,o)=>{if(t?.aborted){o(new Error("Aborted"));return}let r=setTimeout(n,e),i=()=>{clearTimeout(r),o(new Error("Aborted"));};t?.addEventListener("abort",i,{once:true});})}function zt(e){return e>=500&&e<=599}function jt(e){let[t,n]=e.split(",",2);if(!t||!n)throw new Error("Invalid data URL");let r=/data:(.*?);base64/.exec(t)?.[1]||"application/octet-stream",i=atob(n),a=new Uint8Array(i.length);for(let s=0;s<i.length;s+=1)a[s]=i.charCodeAt(s);return new Blob([a],{type:r})}function $t(e){let t={},n={},o={...e};if(o.screenshots){let r={},i={...o.screenshots};i.element&&(t.element=i.element.dataUrl,r.element={mime:i.element.mime,width:i.element.width,height:i.element.height},i.element={...i.element,dataUrl:""}),i.fullPage&&(t.fullPage=i.fullPage.dataUrl,r.fullPage={mime:i.fullPage.mime,width:i.fullPage.width,height:i.fullPage.height},i.fullPage={...i.fullPage,dataUrl:""}),o.screenshots=i,(r.element||r.fullPage)&&(o.screenshot_intent=r);}return o.video&&(n.blob=o.video.blob,o.video_intent={mime:o.video.mime,durationMs:o.video.durationMs,sizeBytes:o.video.sizeBytes},delete o.video),{lean:o,extracted:t,extractedVideo:n}}async function Ye(e,t,n){let o=jt(t);await fetch(e,{method:"PUT",body:o,headers:{"content-type":o.type},...n?{signal:n}:{}});}async function Xt(e){let{feedbackId:t,extracted:n,screenshots:o,signal:r}=e,i={};n.element&&o?.element&&(i.element={dataUrl:n.element,mime:o.element.mime,width:o.element.width,height:o.element.height}),n.fullPage&&o?.fullPage&&(i.fullPage={dataUrl:n.fullPage,mime:o.fullPage.mime,width:o.fullPage.width,height:o.fullPage.height}),Object.keys(i).length!==0&&await fetch(`${ye}/${t}/screenshots`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(i),...r?{signal:r}:{}});}async function Zt(e){await fetch(`${ye}/${e.feedbackId}/video`,{method:"POST",body:e.blob,headers:{"content-type":e.blob.type},...e.signal?{signal:e.signal}:{}});}async function Ge(e){let{signal:t,transport:n}=e;if(Date.now()-Ke<Ht)return {ok:false,error:h("configuration",new Error("Please wait before submitting again"))};let r=n?.timeoutMs??Ot,i=n?.maxAttempts??Ut,a=n?.backoffMs??Vt,s=!!(e.payload.screenshots?.element?.dataUrl||e.payload.screenshots?.fullPage?.dataUrl),c=!!e.payload.video?.blob,u=s||c,{lean:b,extracted:m,extractedVideo:l}=u?$t(e.payload):{lean:e.payload,extracted:{},extractedVideo:{}},w={...m,...e.screenshotDataUrls};for(let f=1;f<=i;f+=1){let p=new AbortController,d=setTimeout(()=>p.abort(),r),k=()=>p.abort();t&&t.addEventListener("abort",k,{once:true});try{let _=await fetch(ye,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(b),signal:p.signal});if(_.ok){Ke=Date.now();let v;try{v=await _.json();}catch{}if((w.element||w.fullPage)&&v){let F=v.upload_urls;if(F){let H=[];w.element&&F.element&&H.push(Ye(F.element,w.element,t)),w.fullPage&&F.fullPage&&H.push(Ye(F.fullPage,w.fullPage,t));try{await Promise.all(H);}catch{}}else if(v.feedback_id)try{await Xt({feedbackId:v.feedback_id,extracted:w,screenshots:e.payload.screenshots,...t?{signal:t}:{}});}catch{}}if(l.blob&&v){let F=v.upload_urls;if(F?.video)try{await fetch(F.video,{method:"PUT",body:l.blob,headers:{"content-type":l.blob.type},...t?{signal:t}:{}});}catch{}else if(v.feedback_id)try{await Zt({feedbackId:v.feedback_id,blob:l.blob,...t?{signal:t}:{}});}catch{}}let L={ok:!0,status:_.status};return v&&(L.apiResponse=v),L}if(f<i&&zt(_.status)){let v=.85+Math.random()*.3,C=Math.round(a*2**(f-1)*v);await Qe(C,t);continue}return {ok:!1,status:_.status,error:h("api_failed",new Error(`HTTP ${_.status}`))}}catch(_){if(p.signal.aborted||t?.aborted)return {ok:false,error:h("aborted",_)};if(f<i){let v=.85+Math.random()*.3,C=Math.round(a*2**(f-1)*v);await Qe(C,t);continue}return {ok:false,error:h("api_failed",_)}}finally{clearTimeout(d),t&&t.removeEventListener("abort",k);}}return {ok:false,error:h("api_failed",new Error("Failed"))}}async function Je(e){let{blob:t,mime:n,blocfeedId:o,signal:r}=e,i=await fetch(qt,{method:"POST",headers:{"Content-Type":n,"X-Blocfeed-Id":o},body:t,...r?{signal:r}:{}});if(!i.ok){let s=await i.json().catch(()=>({error:"Unknown error"}));throw h("api_failed",new Error(s?.error??`Voice API returned ${i.status}`))}let a=await i.json();return {text:a.text??"",warning:a.warning}}function Ee(e){let t=null,n=null,o=(...r)=>{n=r,t===null&&(t=requestAnimationFrame(()=>{if(t=null,!n)return;let i=n;n=null,e(...i);}));};return o.cancel=()=>{t!==null&&cancelAnimationFrame(t),t=null,n=null;},o}function te(e){return e instanceof Element?!!e.closest("[data-blocfeed-ui]"):false}function ne(e){e.stopPropagation(),e.stopImmediatePropagation?.();}function et(e,t){if(!y())throw new Error("BlocFeed picker can only run in a browser environment.");let n=e.ignoreSelectors,o=e.isSelectable,r={};n&&n.length>0&&(r.ignoreSelectors=n),o&&(r.isSelectable=o);let i=null,a=null,s=(f,p=false)=>{if(!f){i=null,a=null,t.onHover(null);return}let d=se(f.getBoundingClientRect()),k=`${Math.round(d.x)}:${Math.round(d.y)}:${Math.round(d.width)}:${Math.round(d.height)}`;!p&&f===i&&k===a||(i=f,a=k,t.onHover({element:f,rect:d}));},c=Ee(f=>{if(te(f.target))return;let p=document.elementFromPoint(f.clientX,f.clientY),d=de(p,r);s(d);}),u=Ee(()=>{i&&s(i,true);}),b=f=>{te(f.target)||(ne(f),f.pointerType==="mouse"&&f.preventDefault());},m=f=>{te(f.target)||(ne(f),f.pointerType==="mouse"&&f.preventDefault());},l=f=>{if(te(f.target))return;ne(f),f.preventDefault();let p=document.elementFromPoint(f.clientX,f.clientY),d=de(p,r);d&&t.onSelect({element:d,descriptor:Fe(d)});},w=f=>{f.key==="Escape"&&(ne(f),f.preventDefault(),t.onCancel());};return window.addEventListener("pointermove",c,{capture:true,passive:true}),window.addEventListener("pointerdown",b,{capture:true}),window.addEventListener("pointerup",m,{capture:true}),window.addEventListener("click",l,{capture:true}),window.addEventListener("keydown",w,{capture:true}),window.addEventListener("scroll",u,{capture:true,passive:true}),window.addEventListener("resize",u,{passive:true}),{stop(){window.removeEventListener("pointermove",c,{capture:true}),window.removeEventListener("pointerdown",b,{capture:true}),window.removeEventListener("pointerup",m,{capture:true}),window.removeEventListener("click",l,{capture:true}),window.removeEventListener("keydown",w,{capture:true}),window.removeEventListener("scroll",u,{capture:true}),window.removeEventListener("resize",u),c.cancel(),u.cancel(),t.onHover(null);}}}async function ve(e){let{signal:t,transport:n}=e,o={ok:false};try{let r={payload:e.payload,...t?{signal:t}:{},...n?{transport:n}:{}};o.api=await Ge(r),o.ok=!!o.api?.ok;}catch(r){o.api={ok:false,error:h("api_failed",r)},o.ok=false;}return {payload:e.payload,result:o}}var Wt=["[data-blocfeed-ui]","[data-blocfeed-ignore]"];function Kt(e){let t=[...Wt,...e?.ignoreSelectors??[]],n=Array.from(new Set(t));return {...e,ignoreSelectors:n}}function tt(){return {phase:"idle"}}function Qt(e){if(e.ok)return false;let t=e.api;return t?t.status&&t.status>=400&&t.status<500||t.error?.kind==="aborted"||t.error?.kind==="configuration"?false:!t.ok:true}function no(e){let t=e,n=tt(),o=new Set,r=new Set,i=null,a=null,s=null,c=null,u=0,b=null,m=null,l=null,w=null,f=()=>{for(let g of o)g(n);},p=g=>{for(let E of r)E(g);},d=g=>{n=g,f();},k=()=>{u+=1,c?.abort(),c=null;},_=()=>{i?.stop(),i=null,p(null),s!==null&&y()&&(document.documentElement.style.cursor=s,s=null);},v=()=>{m&&(m.abort(),m=null),l&&(URL.revokeObjectURL(l.blobUrl),l=null);},C=()=>{w&&(w.abort(),w=null);},L=()=>{k(),_(),v(),C(),a=null,d(tt());},F=()=>{if(!y())return;_(),a=null;let g=Kt(t.picker);s=document.documentElement.style.cursor,document.documentElement.style.cursor="crosshair",d({phase:"picking"}),i=et(g,{onHover:p,onSelect:({element:E,descriptor:x})=>{a=E,_(),d({phase:"review",selection:x});},onCancel:()=>{L();}});},H=()=>{let g=Ve();if(g.length!==0)for(let E of g)ve({payload:E,...t.transport?{transport:t.transport}:{}}).catch(()=>{he(E);});};if(y()){setTimeout(H,1e3);let g=()=>H();window.addEventListener("online",g),b=()=>window.removeEventListener("online",g);}return {getState:()=>n,subscribe(g){return o.add(g),()=>o.delete(g)},subscribeHover(g){return r.add(g),()=>r.delete(g)},start(){n.phase==="capturing"||n.phase==="submitting"||n.phase==="recording"||n.phase!=="picking"&&F();},stop(){L();},clearSelection(){n.phase==="capturing"||n.phase==="submitting"||n.phase==="recording"||F();},setConfig(g){t=g;},async submit(g,E){if(!y()){let T=h("configuration",new Error("BlocFeed submit can only run in the browser"));return d({phase:"error",lastError:T}),{ok:false}}let x=t.blocfeed_id?.trim?.()??"";if(!x){let M={phase:"error",lastError:h("configuration",new Error("Missing blocfeed_id. Create a project in BlocFeed and pass its blocfeed_id."))};return n.selection&&(M.selection=n.selection),d(M),{ok:false}}if(n.phase==="capturing"||n.phase==="submitting"||n.phase==="recording")return {ok:false};let R=u+1;u=R,c?.abort(),c=new AbortController;let A=c.signal,S=n.selection,D=E?.capture?{...t.capture,...E.capture}:t.capture,B=!!(D?.element||D?.fullPage),z={phase:B?"capturing":"submitting"};S&&(z.selection=S),d(z);try{let T=B?await Oe({selectionElement:a,capture:D,signal:A}):void 0;if(A.aborted||u!==R)return {ok:!1};let M={phase:"submitting"};S&&(M.selection=S),T&&(M.capture=T),d(M);let I={};S&&(I.selection=S),T&&(I.capture=T);let j=await Ue({config:t.metadata,context:I,...t.user?{user:t.user}:{}}),O={version:1,createdAt:new Date().toISOString(),blocfeed_id:x,message:g,metadata:j};E?.category&&(O.category=E.category),t.user&&(O.user=t.user),S&&(O.selection=S),T&&(O.screenshots=T),l&&(O.video=l);let{result:N}=await ve({payload:O,signal:A,...t.transport?{transport:t.transport}:{}});if(A.aborted||u!==R)return N;if(N.ok){l&&(URL.revokeObjectURL(l.blobUrl),l=null);let ae={phase:"success",lastSubmit:N};return S&&(ae.selection=S),T&&(ae.capture=T),d(ae),N}Qt(N)&&he(O);let rt=N.api?.error??h("unknown",new Error("Submission failed")),ie={phase:"error",lastSubmit:N,lastError:rt};return S&&(ie.selection=S),T&&(ie.capture=T),d(ie),N}catch(T){if(A.aborted||u!==R)return {ok:false};let I={phase:"error",lastError:A.aborted?h("aborted",T):h("unknown",T)};return S&&(I.selection=S),d(I),{ok:false}}finally{u===R&&(c=null);}},async startRecording(){if(n.phase!=="review"||!t.recording?.enabled)return;let g=n.selection,E={phase:"recording",recordingElapsedMs:0};g&&(E.selection=g),d(E);try{let x={config:t.recording};c&&(x.signal=c.signal);let R=await we(x);m=R,R.onTick(D=>{if(n.phase==="recording"){let B={phase:"recording",recordingElapsedMs:D};g&&(B.selection=g),d(B);}});let A=await R.result;m=null,l=A;let S={phase:"review",video:A};g&&(S.selection=g),d(S);}catch(x){m=null;let A={phase:"review",lastError:x?.kind?x:h("recording_failed",x)};g&&(A.selection=g),d(A);}},stopRecording(){n.phase!=="recording"||!m||m.stop();},removeVideo(){l&&(URL.revokeObjectURL(l.blobUrl),l=null);let g=n.selection,E={phase:"review"};g&&(E.selection=g),d(E);},async startVoice(){if(n.phase!=="review"){console.warn("[BlocFeed] startVoice: ignored \u2014 phase is",n.phase,'(expected "review")');return}let g=t.voice;if(!g?.enabled){console.warn("[BlocFeed] startVoice: ignored \u2014 voice is not enabled in config");return}C();let E=n.selection,x={phase:"review",voiceRecording:true,voiceElapsedMs:0};E&&(x.selection=E),l&&(x.video=l),d(x);try{let R={config:g};c&&(R.signal=c.signal);let A=await be(R);w=A,A.onTick(I=>{let j={phase:"review",voiceRecording:!0,voiceElapsedMs:I};E&&(j.selection=E),l&&(j.video=l),d(j);});let S=await A.result;w=null;let D={phase:"review",voiceRecording:!1,voiceTranscribing:!0};E&&(D.selection=E),l&&(D.video=l),d(D);let B={blob:S.blob,mime:S.mime,blocfeedId:t.blocfeed_id};c&&(B.signal=c.signal);let{text:Te,warning:z}=await Je(B),T={phase:"review",voiceRecording:!1,voiceTranscribing:!1};E&&(T.selection=E),l&&(T.video=l),d(T);let M={text:Te};return z&&(M.warning=z),M}catch(R){C();let A=R?.kind?R:h("recording_failed",R);console.warn("[BlocFeed] Voice error:",A.message);let S={phase:"review",voiceRecording:false,voiceTranscribing:false,lastError:A};E&&(S.selection=E),l&&(S.video=l),d(S);return}},stopVoice(){w&&w.stop();},__unsafeGetSelectedElement(){return a},destroy(){L(),o.clear(),r.clear(),b?.(),b=null;}}}var Z=[],W=[],nt=20,ot=15,oe=[],K={},Q=null,Y=null,G=null,re=false,Yt=["blocfeed.com","google-analytics.com","googletagmanager.com","analytics.google.com","googleads.g.doubleclick.net","googlesyndication.com","googleadservices.com","google.com/pagead","google.com/ads","facebook.net","facebook.com/tr","connect.facebook.net","graph.facebook.com","api.mixpanel.com","api-js.mixpanel.com","api.amplitude.com","api2.amplitude.com","api.segment.io","cdn.segment.com","vars.hotjar.com","in.hotjar.com","script.hotjar.com","heapanalytics.com","fullstory.com/s/fs.js","rs.fullstory.com","app.posthog.com","us.posthog.com","eu.posthog.com","api-iam.intercom.io","widget.intercom.io","sentry.io/api","browser.sentry-cdn.com","browser-intake-datadoghq.com","clarity.ms","js.hs-analytics.net","api.hubapi.com","forms.hubspot.com","plausible.io/api","client.crisp.chat","js.driftt.com","r.logrocket.com","app.pendo.io","events.launchdarkly.com","app.launchdarkly.com","grammarly.com","gnar.grammarly.com","capi.grammarly.com","api.languagetool.org","api.languagetoolplus.com","fonts.googleapis.com","fonts.gstatic.com","cdn.cookielaw.org","consent.cookiebot.com","js.stripe.com","api.stripe.com/v1/tokens","google.com/recaptcha","hcaptcha.com","bam.nr-data.net","rec.smartlook.com","o2.mouseflow.com","api.luckyorange.com","static.zdassets.com","ekr.zdassets.com"];function Gt(e){if(e instanceof Error)return e.message;if(typeof e=="string")return e;try{return JSON.stringify(e)}catch{return String(e)}}function Jt(e,t){let n=t.map(Gt).join(" "),o=t.find(i=>i instanceof Error),r={level:e,message:n.slice(0,2e3),timestamp:Date.now()};for(o?.stack&&(r.stack=o.stack.slice(0,2e3)),Z.push(r);Z.length>nt;)Z.shift();}function Se(e){let t=e.url.toLowerCase();for(let n of oe)if(t.includes(n))return;for(W.push(e);W.length>ot;)W.shift();}function io(e={}){if(re||!y())return;re=true,nt=e.consoleLimit??20,ot=e.networkLimit??15;let t=e.ignoreUrls;if(t!==void 0?oe=t.map(n=>n.toLowerCase()):oe=[...Yt],e.console!==false){let n=e.consoleLevels??["error","warn"];for(let o of n)K[o]=console[o],console[o]=(...r)=>{Jt(o,r),K[o]?.apply(console,r);};}if(e.network!==false&&typeof window.fetch=="function"){Q=window.fetch;let n=Q;window.fetch=async function(r,i){let a=typeof r=="string"?r:r instanceof URL?r.toString():r.url,s=(i?.method??"GET").toUpperCase(),c=Date.now();try{let u=await n.call(window,r,i);return u.ok||Se({url:a.slice(0,500),method:s,status:u.status,statusText:u.statusText,timestamp:c,durationMs:Date.now()-c}),u}catch(u){throw Se({url:a.slice(0,500),method:s,status:0,statusText:u instanceof Error?u.message:"Network error",timestamp:c,durationMs:Date.now()-c}),u}};}if(e.network!==false&&typeof XMLHttpRequest<"u"){Y=XMLHttpRequest.prototype.open,G=XMLHttpRequest.prototype.send;let n=Y,o=G;XMLHttpRequest.prototype.open=function(r,i,...a){return this.__bf_method=r.toUpperCase(),this.__bf_url=String(i),n.apply(this,[r,i,...a])},XMLHttpRequest.prototype.send=function(...r){let i=this.__bf_method||"GET",a=this.__bf_url||"",s=Date.now();return this.addEventListener("loadend",function(){if(this.status>=400||this.status===0){let c={url:a.slice(0,500),method:i,status:this.status,timestamp:s,durationMs:Date.now()-s};this.statusText&&(c.statusText=this.statusText),Se(c);}}),o.apply(this,r)};}}function ao(){if(re){for(let[e,t]of Object.entries(K))console[e]=t;for(let e of Object.keys(K))delete K[e];Q&&(window.fetch=Q,Q=null),Y&&(XMLHttpRequest.prototype.open=Y,Y=null),G&&(XMLHttpRequest.prototype.send=G,G=null),oe=[],re=false;}}function so(){return {consoleLogs:[...Z],networkErrors:[...W]}}function co(){Z=[],W=[];}var en=[{name:"supabase_service_role_key",pattern:/SUPABASE_SERVICE_ROLE_KEY["'=:\s]+[A-Za-z0-9_.=-]{30,}/},{name:"supabase_db_password",pattern:/SUPABASE_DB_PASSWORD["'=:\s]+[^\s"']{6,}/},{name:"postgres_password",pattern:/POSTGRES_PASSWORD["'=:\s]+[^\s"']{6,}/},{name:"database_url_with_creds",pattern:/(?:postgres|postgresql|mysql|mongodb|redis|amqp):\/\/[^:]+:[^@\s"']+@/},{name:"aws_access_key",pattern:/AKIA[0-9A-Z]{16}/},{name:"aws_secret_key",pattern:/AWS_SECRET_ACCESS_KEY["'=:\s]+[A-Za-z0-9/+=]{30,}/},{name:"aws_session_token",pattern:/AWS_SESSION_TOKEN["'=:\s]+[A-Za-z0-9/+=]{30,}/},{name:"stripe_secret_key",pattern:/sk_live_[a-zA-Z0-9]{10,}/},{name:"stripe_secret_key_test",pattern:/sk_test_[a-zA-Z0-9]{10,}/},{name:"stripe_restricted_key",pattern:/rk_(?:live|test)_[a-zA-Z0-9]{10,}/},{name:"github_pat",pattern:/ghp_[A-Za-z0-9_]{36,}/},{name:"github_oauth",pattern:/gho_[A-Za-z0-9_]{36,}/},{name:"github_user_token",pattern:/ghu_[A-Za-z0-9_]{36,}/},{name:"github_server_token",pattern:/ghs_[A-Za-z0-9_]{36,}/},{name:"github_fine_grained",pattern:/github_pat_[A-Za-z0-9_]{22,}/},{name:"openai_api_key",pattern:/sk-[a-zA-Z0-9]{20,}(?![a-zA-Z0-9_])/},{name:"anthropic_api_key",pattern:/sk-ant-[a-zA-Z0-9\-_]{20,}/},{name:"private_key",pattern:/-----BEGIN (?:RSA |EC |DSA |OPENSSH |PGP )?PRIVATE KEY-----/},{name:"sendgrid_api_key",pattern:/SG\.[a-zA-Z0-9_-]{22,}\.[a-zA-Z0-9_-]{22,}/},{name:"twilio_auth_token",pattern:/TWILIO_AUTH_TOKEN["'=:\s]+[a-f0-9]{32}/},{name:"generic_secret_key",pattern:/[A-Z_]{2,}_SECRET_KEY["'=:\s]+[^\s"']{8,}/},{name:"generic_secret",pattern:/[A-Z_]{2,}_SECRET["'=:\s]+[^\s"']{8,}/},{name:"generic_password",pattern:/[A-Z_]{2,}_PASSWORD["'=:\s]+[^\s"']{6,}/},{name:"generic_private_key_var",pattern:/[A-Z_]{2,}_PRIVATE_KEY["'=:\s]+[^\s"']{8,}/}],V=[],_e=0,ke=false;function tn(e){let t=e.slice(0,80);return t.length<=6?"***":t.slice(0,6)+"***"}function nn(e,t,n,o){if(V.some(a=>a.rule===e&&a.source===t))return;let i={rule:e,source:t,hint:tn(n),timestamp:Date.now()};o&&(i.location=o),V.push(i);}function J(e,t,n,o){for(let{name:r,pattern:i}of n){let a=i.exec(e);a&&nn(r,t,a[0],o);}}function on(e){try{let t=window;if(t.__NEXT_DATA__){let n=JSON.stringify(t.__NEXT_DATA__);J(n,"hydration",e,"__NEXT_DATA__");}if(t.__NUXT__){let n=JSON.stringify(t.__NUXT__);J(n,"hydration",e,"__NUXT__");}}catch{}}function rn(e){try{document.querySelectorAll("script:not([src])").forEach((n,o)=>{let r=n.textContent;r&&r.length>0&&J(r,"scripts",e,`inline-script[${o}]`);});}catch{}}function an(e){try{document.querySelectorAll("meta[content]").forEach(n=>{let o=n.getAttribute("content"),r=n.getAttribute("name")||n.getAttribute("property")||"";o&&o.length>10&&J(o,"meta",e,r?`meta[${r}]`:void 0);});}catch{}}function sn(e){try{document.querySelectorAll("[data-api-key], [data-secret], [data-token], [data-password]").forEach(n=>{let o=n.attributes;for(let r=0;r<o.length;r++){let i=o[r];i.name.startsWith("data-")&&i.value.length>10&&J(i.value,"dom",e,`${n.tagName.toLowerCase()}[${i.name}]`);}});}catch{}}function fo(e={}){if(ke||!y()||(ke=true,e.secretScan===false))return;let t=[...en,...e.customPatterns??[]],n=e.scanTargets??["hydration","scripts","meta","dom"];setTimeout(()=>{_e=Date.now(),n.includes("hydration")&&on(t),n.includes("scripts")&&rn(t),n.includes("meta")&&an(t),n.includes("dom")&&sn(t);let o=e.notify??"both";V.length>0&&(o==="console"||o==="both")&&console.warn(`[BlocFeed Security] Found ${V.length} potential secret(s) exposed in client code:`,V.map(r=>`${r.rule} (${r.source}${r.location?`: ${r.location}`:""})`));},100);}function mo(){return {findings:[...V],scannedAt:_e}}function po(){V=[],_e=0,ke=false;}
2
+ export{y as a,se as b,Le as c,Oe as d,Ue as e,he as f,Ve as g,En as h,vn as i,At as j,Rt as k,Dt as l,we as m,Nt as n,be as o,Je as p,no as q,io as r,ao as s,so as t,co as u,fo as v,mo as w,po as x};
@@ -203,6 +203,12 @@ interface RecordingConfig {
203
203
  /** Video bitrate in bits/s. Default: 2 500 000 (2.5 Mbps) */
204
204
  videoBitsPerSecond?: number;
205
205
  }
206
+ interface VoiceConfig {
207
+ /** Enable voice feedback mic button. Default: false */
208
+ enabled?: boolean;
209
+ /** Maximum recording duration in milliseconds. Default: 60 000 (60 s) */
210
+ maxDurationMs?: number;
211
+ }
206
212
  interface VideoAsset {
207
213
  mime: VideoMime;
208
214
  /** Object URL (blob:) for local preview. Revoked after submission. */
@@ -291,6 +297,8 @@ interface BlocFeedConfig {
291
297
  security?: SecurityConfig;
292
298
  /** Video recording configuration. */
293
299
  recording?: RecordingConfig;
300
+ /** Voice feedback configuration (audio recording + transcription). */
301
+ voice?: VoiceConfig;
294
302
  ui?: {
295
303
  /** z-index for the widget overlay/panel. */
296
304
  zIndex?: number;
@@ -379,6 +387,12 @@ interface BlocFeedState {
379
387
  recordingElapsedMs?: number;
380
388
  lastSubmit?: SubmitResult;
381
389
  lastError?: BlocFeedError;
390
+ /** True while voice recording is active. */
391
+ voiceRecording?: boolean;
392
+ /** Elapsed voice recording time in ms. */
393
+ voiceElapsedMs?: number;
394
+ /** True while audio is being transcribed. */
395
+ voiceTranscribing?: boolean;
382
396
  }
383
397
 
384
398
  type HoverInfo = {
@@ -403,10 +417,15 @@ interface BlocFeedController {
403
417
  startRecording: () => Promise<void>;
404
418
  stopRecording: () => void;
405
419
  removeVideo: () => void;
420
+ startVoice: () => Promise<{
421
+ text: string;
422
+ warning?: string;
423
+ } | undefined>;
424
+ stopVoice: () => void;
406
425
  /** Internal: used by UI to access the live element handle. */
407
426
  __unsafeGetSelectedElement: () => Element | null;
408
427
  destroy: () => void;
409
428
  }
410
429
  declare function createBlocFeedController(config: BlocFeedConfig): BlocFeedController;
411
430
 
412
- export { type VideoMime as A, type BlocFeedConfig as B, type CaptureConfig as C, type DiagnosticsConfig as D, type ElementDescriptor as E, type FeedbackCategory as F, type ClickEvent as G, type HoverListener as H, type ImageAsset as I, type StateListener as J, createBlocFeedController as K, type MaybePromise as M, type NetworkEntry as N, type PickerConfig as P, type RecordingConfig as R, type SubmitResult as S, type ThemeConfig as T, type VideoAsset as V, type WidgetPosition as W, type BlocFeedHandle as a, type BlocFeedState as b, type BlocFeedController as c, type BlocFeedError as d, type BlocFeedStrings as e, type BlocFeedUser as f, type CaptureDiagnostics as g, type CaptureResult as h, type ConsoleEntry as i, type FeedbackApiResponse as j, type FeedbackPayload as k, type MetadataConfig as l, type MetadataContext as m, type Rect as n, type ScreenshotAdapter as o, type ScreenshotAdapterOptions as p, type ScreenshotIntent as q, type ScreenshotMime as r, type SecurityConfig as s, type SecurityFinding as t, type SecuritySnapshot as u, type SessionPhase as v, type TransportConfig as w, type TransportResult as x, type TriggerStyle as y, type VideoIntent as z };
431
+ export { type VideoMime as A, type BlocFeedConfig as B, type CaptureConfig as C, type DiagnosticsConfig as D, type ElementDescriptor as E, type FeedbackCategory as F, type VoiceConfig as G, type ClickEvent as H, type ImageAsset as I, type HoverListener as J, type StateListener as K, createBlocFeedController as L, type MaybePromise as M, type NetworkEntry as N, type PickerConfig as P, type RecordingConfig as R, type SubmitResult as S, type ThemeConfig as T, type VideoAsset as V, type WidgetPosition as W, type BlocFeedHandle as a, type BlocFeedState as b, type BlocFeedController as c, type BlocFeedError as d, type BlocFeedStrings as e, type BlocFeedUser as f, type CaptureDiagnostics as g, type CaptureResult as h, type ConsoleEntry as i, type FeedbackApiResponse as j, type FeedbackPayload as k, type MetadataConfig as l, type MetadataContext as m, type Rect as n, type ScreenshotAdapter as o, type ScreenshotAdapterOptions as p, type ScreenshotIntent as q, type ScreenshotMime as r, type SecurityConfig as s, type SecurityFinding as t, type SecuritySnapshot as u, type SessionPhase as v, type TransportConfig as w, type TransportResult as x, type TriggerStyle as y, type VideoIntent as z };
@@ -203,6 +203,12 @@ interface RecordingConfig {
203
203
  /** Video bitrate in bits/s. Default: 2 500 000 (2.5 Mbps) */
204
204
  videoBitsPerSecond?: number;
205
205
  }
206
+ interface VoiceConfig {
207
+ /** Enable voice feedback mic button. Default: false */
208
+ enabled?: boolean;
209
+ /** Maximum recording duration in milliseconds. Default: 60 000 (60 s) */
210
+ maxDurationMs?: number;
211
+ }
206
212
  interface VideoAsset {
207
213
  mime: VideoMime;
208
214
  /** Object URL (blob:) for local preview. Revoked after submission. */
@@ -291,6 +297,8 @@ interface BlocFeedConfig {
291
297
  security?: SecurityConfig;
292
298
  /** Video recording configuration. */
293
299
  recording?: RecordingConfig;
300
+ /** Voice feedback configuration (audio recording + transcription). */
301
+ voice?: VoiceConfig;
294
302
  ui?: {
295
303
  /** z-index for the widget overlay/panel. */
296
304
  zIndex?: number;
@@ -379,6 +387,12 @@ interface BlocFeedState {
379
387
  recordingElapsedMs?: number;
380
388
  lastSubmit?: SubmitResult;
381
389
  lastError?: BlocFeedError;
390
+ /** True while voice recording is active. */
391
+ voiceRecording?: boolean;
392
+ /** Elapsed voice recording time in ms. */
393
+ voiceElapsedMs?: number;
394
+ /** True while audio is being transcribed. */
395
+ voiceTranscribing?: boolean;
382
396
  }
383
397
 
384
398
  type HoverInfo = {
@@ -403,10 +417,15 @@ interface BlocFeedController {
403
417
  startRecording: () => Promise<void>;
404
418
  stopRecording: () => void;
405
419
  removeVideo: () => void;
420
+ startVoice: () => Promise<{
421
+ text: string;
422
+ warning?: string;
423
+ } | undefined>;
424
+ stopVoice: () => void;
406
425
  /** Internal: used by UI to access the live element handle. */
407
426
  __unsafeGetSelectedElement: () => Element | null;
408
427
  destroy: () => void;
409
428
  }
410
429
  declare function createBlocFeedController(config: BlocFeedConfig): BlocFeedController;
411
430
 
412
- export { type VideoMime as A, type BlocFeedConfig as B, type CaptureConfig as C, type DiagnosticsConfig as D, type ElementDescriptor as E, type FeedbackCategory as F, type ClickEvent as G, type HoverListener as H, type ImageAsset as I, type StateListener as J, createBlocFeedController as K, type MaybePromise as M, type NetworkEntry as N, type PickerConfig as P, type RecordingConfig as R, type SubmitResult as S, type ThemeConfig as T, type VideoAsset as V, type WidgetPosition as W, type BlocFeedHandle as a, type BlocFeedState as b, type BlocFeedController as c, type BlocFeedError as d, type BlocFeedStrings as e, type BlocFeedUser as f, type CaptureDiagnostics as g, type CaptureResult as h, type ConsoleEntry as i, type FeedbackApiResponse as j, type FeedbackPayload as k, type MetadataConfig as l, type MetadataContext as m, type Rect as n, type ScreenshotAdapter as o, type ScreenshotAdapterOptions as p, type ScreenshotIntent as q, type ScreenshotMime as r, type SecurityConfig as s, type SecurityFinding as t, type SecuritySnapshot as u, type SessionPhase as v, type TransportConfig as w, type TransportResult as x, type TriggerStyle as y, type VideoIntent as z };
431
+ export { type VideoMime as A, type BlocFeedConfig as B, type CaptureConfig as C, type DiagnosticsConfig as D, type ElementDescriptor as E, type FeedbackCategory as F, type VoiceConfig as G, type ClickEvent as H, type ImageAsset as I, type HoverListener as J, type StateListener as K, createBlocFeedController as L, type MaybePromise as M, type NetworkEntry as N, type PickerConfig as P, type RecordingConfig as R, type SubmitResult as S, type ThemeConfig as T, type VideoAsset as V, type WidgetPosition as W, type BlocFeedHandle as a, type BlocFeedState as b, type BlocFeedController as c, type BlocFeedError as d, type BlocFeedStrings as e, type BlocFeedUser as f, type CaptureDiagnostics as g, type CaptureResult as h, type ConsoleEntry as i, type FeedbackApiResponse as j, type FeedbackPayload as k, type MetadataConfig as l, type MetadataContext as m, type Rect as n, type ScreenshotAdapter as o, type ScreenshotAdapterOptions as p, type ScreenshotIntent as q, type ScreenshotMime as r, type SecurityConfig as s, type SecurityFinding as t, type SecuritySnapshot as u, type SessionPhase as v, type TransportConfig as w, type TransportResult as x, type TriggerStyle as y, type VideoIntent as z };
package/dist/engine.cjs CHANGED
@@ -1 +1 @@
1
- 'use strict';var chunkEFTA67IW_cjs=require('./chunk-EFTA67IW.cjs');function c(o){if(o?.aborted)throw new Error("Aborted")}async function k(o){return await new Promise((t,e)=>{let r=new Image;r.onload=()=>t({width:r.naturalWidth,height:r.naturalHeight}),r.onerror=()=>e(new Error("Failed to load generated screenshot")),r.src=o;})}async function l(o,t){let{width:e,height:r}=await k(o);return {dataUrl:o,mime:t,width:e,height:r}}function T(o){return {async captureElement(t,e){if(!chunkEFTA67IW_cjs.a())throw new Error("captureElement can only run in the browser");c(e.signal);let r={scale:e.pixelRatio},n=e.mime==="image/jpeg"?await o.domToJpeg(t,{...r,quality:e.quality??.92}):await o.domToPng(t,r);return c(e.signal),await l(n,e.mime)},async captureFullPage(t){if(!chunkEFTA67IW_cjs.a())throw new Error("captureFullPage can only run in the browser");c(t.signal);let e=document.documentElement,r=Math.max(e.scrollWidth,e.clientWidth),n=Math.max(e.scrollHeight,e.clientHeight),a=Math.min(1,t.maxDimension/Math.max(r,n)),s={width:Math.max(1,Math.round(r*a)),height:Math.max(1,Math.round(n*a)),scale:t.pixelRatio},i=t.mime==="image/jpeg"?await o.domToJpeg(e,{...s,quality:t.quality??.92}):await o.domToPng(e,s);return c(t.signal),await l(i,t.mime)}}}function q(o){let[t,e]=o.split(",",2);if(!t||!e)throw new Error("Invalid data URL");let n=/data:(.*?);base64/.exec(t)?.[1]||"application/octet-stream",a=atob(e),s=new Uint8Array(a.length);for(let i=0;i<a.length;i+=1)s[i]=a.charCodeAt(i);return new Blob([s],{type:n})}Object.defineProperty(exports,"clearClickEvents",{enumerable:true,get:function(){return chunkEFTA67IW_cjs.k}});Object.defineProperty(exports,"clearDiagnostics",{enumerable:true,get:function(){return chunkEFTA67IW_cjs.r}});Object.defineProperty(exports,"clearQueue",{enumerable:true,get:function(){return chunkEFTA67IW_cjs.h}});Object.defineProperty(exports,"clearSecurityFindings",{enumerable:true,get:function(){return chunkEFTA67IW_cjs.u}});Object.defineProperty(exports,"collectMetadata",{enumerable:true,get:function(){return chunkEFTA67IW_cjs.e}});Object.defineProperty(exports,"createBlocFeedController",{enumerable:true,get:function(){return chunkEFTA67IW_cjs.n}});Object.defineProperty(exports,"createHtmlToImageAdapter",{enumerable:true,get:function(){return chunkEFTA67IW_cjs.c}});Object.defineProperty(exports,"dequeueAll",{enumerable:true,get:function(){return chunkEFTA67IW_cjs.g}});Object.defineProperty(exports,"drainClickEvents",{enumerable:true,get:function(){return chunkEFTA67IW_cjs.j}});Object.defineProperty(exports,"drainDiagnostics",{enumerable:true,get:function(){return chunkEFTA67IW_cjs.q}});Object.defineProperty(exports,"enqueue",{enumerable:true,get:function(){return chunkEFTA67IW_cjs.f}});Object.defineProperty(exports,"getQueueSize",{enumerable:true,get:function(){return chunkEFTA67IW_cjs.i}});Object.defineProperty(exports,"getSecurityFindings",{enumerable:true,get:function(){return chunkEFTA67IW_cjs.t}});Object.defineProperty(exports,"installDiagnostics",{enumerable:true,get:function(){return chunkEFTA67IW_cjs.o}});Object.defineProperty(exports,"isRecordingSupported",{enumerable:true,get:function(){return chunkEFTA67IW_cjs.l}});Object.defineProperty(exports,"runCapture",{enumerable:true,get:function(){return chunkEFTA67IW_cjs.d}});Object.defineProperty(exports,"runSecretScan",{enumerable:true,get:function(){return chunkEFTA67IW_cjs.s}});Object.defineProperty(exports,"startRecording",{enumerable:true,get:function(){return chunkEFTA67IW_cjs.m}});Object.defineProperty(exports,"uninstallDiagnostics",{enumerable:true,get:function(){return chunkEFTA67IW_cjs.p}});exports.createModernScreenshotAdapter=T;exports.dataUrlToBlob=q;
1
+ 'use strict';var chunkCU24MLER_cjs=require('./chunk-CU24MLER.cjs');function s(o){if(o?.aborted)throw new Error("Aborted")}async function D(o){return await new Promise((t,e)=>{let r=new Image;r.onload=()=>t({width:r.naturalWidth,height:r.naturalHeight}),r.onerror=()=>e(new Error("Failed to load generated screenshot")),r.src=o;})}async function l(o,t){let{width:e,height:r}=await D(o);return {dataUrl:o,mime:t,width:e,height:r}}function v(o){return {async captureElement(t,e){if(!chunkCU24MLER_cjs.a())throw new Error("captureElement can only run in the browser");s(e.signal);let r={scale:e.pixelRatio},n=e.mime==="image/jpeg"?await o.domToJpeg(t,{...r,quality:e.quality??.92}):await o.domToPng(t,r);return s(e.signal),await l(n,e.mime)},async captureFullPage(t){if(!chunkCU24MLER_cjs.a())throw new Error("captureFullPage can only run in the browser");s(t.signal);let e=document.documentElement,r=Math.max(e.scrollWidth,e.clientWidth),n=Math.max(e.scrollHeight,e.clientHeight),i=Math.min(1,t.maxDimension/Math.max(r,n)),c={width:Math.max(1,Math.round(r*i)),height:Math.max(1,Math.round(n*i)),scale:t.pixelRatio},a=t.mime==="image/jpeg"?await o.domToJpeg(e,{...c,quality:t.quality??.92}):await o.domToPng(e,c);return s(t.signal),await l(a,t.mime)}}}function H(o){let[t,e]=o.split(",",2);if(!t||!e)throw new Error("Invalid data URL");let n=/data:(.*?);base64/.exec(t)?.[1]||"application/octet-stream",i=atob(e),c=new Uint8Array(i.length);for(let a=0;a<i.length;a+=1)c[a]=i.charCodeAt(a);return new Blob([c],{type:n})}Object.defineProperty(exports,"clearClickEvents",{enumerable:true,get:function(){return chunkCU24MLER_cjs.k}});Object.defineProperty(exports,"clearDiagnostics",{enumerable:true,get:function(){return chunkCU24MLER_cjs.u}});Object.defineProperty(exports,"clearQueue",{enumerable:true,get:function(){return chunkCU24MLER_cjs.h}});Object.defineProperty(exports,"clearSecurityFindings",{enumerable:true,get:function(){return chunkCU24MLER_cjs.x}});Object.defineProperty(exports,"collectMetadata",{enumerable:true,get:function(){return chunkCU24MLER_cjs.e}});Object.defineProperty(exports,"createBlocFeedController",{enumerable:true,get:function(){return chunkCU24MLER_cjs.q}});Object.defineProperty(exports,"createHtmlToImageAdapter",{enumerable:true,get:function(){return chunkCU24MLER_cjs.c}});Object.defineProperty(exports,"dequeueAll",{enumerable:true,get:function(){return chunkCU24MLER_cjs.g}});Object.defineProperty(exports,"drainClickEvents",{enumerable:true,get:function(){return chunkCU24MLER_cjs.j}});Object.defineProperty(exports,"drainDiagnostics",{enumerable:true,get:function(){return chunkCU24MLER_cjs.t}});Object.defineProperty(exports,"enqueue",{enumerable:true,get:function(){return chunkCU24MLER_cjs.f}});Object.defineProperty(exports,"getQueueSize",{enumerable:true,get:function(){return chunkCU24MLER_cjs.i}});Object.defineProperty(exports,"getSecurityFindings",{enumerable:true,get:function(){return chunkCU24MLER_cjs.w}});Object.defineProperty(exports,"installDiagnostics",{enumerable:true,get:function(){return chunkCU24MLER_cjs.r}});Object.defineProperty(exports,"isRecordingSupported",{enumerable:true,get:function(){return chunkCU24MLER_cjs.l}});Object.defineProperty(exports,"isVoiceSupported",{enumerable:true,get:function(){return chunkCU24MLER_cjs.n}});Object.defineProperty(exports,"runCapture",{enumerable:true,get:function(){return chunkCU24MLER_cjs.d}});Object.defineProperty(exports,"runSecretScan",{enumerable:true,get:function(){return chunkCU24MLER_cjs.v}});Object.defineProperty(exports,"startRecording",{enumerable:true,get:function(){return chunkCU24MLER_cjs.m}});Object.defineProperty(exports,"startVoiceRecording",{enumerable:true,get:function(){return chunkCU24MLER_cjs.o}});Object.defineProperty(exports,"transcribeAudio",{enumerable:true,get:function(){return chunkCU24MLER_cjs.p}});Object.defineProperty(exports,"uninstallDiagnostics",{enumerable:true,get:function(){return chunkCU24MLER_cjs.s}});exports.createModernScreenshotAdapter=v;exports.dataUrlToBlob=H;
package/dist/engine.d.cts CHANGED
@@ -1,5 +1,5 @@
1
- import { o as ScreenshotAdapter, C as CaptureConfig, h as CaptureResult, V as VideoAsset, R as RecordingConfig, G as ClickEvent, l as MetadataConfig, m as MetadataContext, f as BlocFeedUser, k as FeedbackPayload, u as SecuritySnapshot, s as SecurityConfig } from './controller-C6M-ge3K.cjs';
2
- export { B as BlocFeedConfig, c as BlocFeedController, d as BlocFeedError, a as BlocFeedHandle, b as BlocFeedState, e as BlocFeedStrings, i as ConsoleEntry, D as DiagnosticsConfig, E as ElementDescriptor, j as FeedbackApiResponse, F as FeedbackCategory, H as HoverListener, I as ImageAsset, N as NetworkEntry, n as Rect, p as ScreenshotAdapterOptions, q as ScreenshotIntent, r as ScreenshotMime, t as SecurityFinding, v as SessionPhase, J as StateListener, S as SubmitResult, T as ThemeConfig, w as TransportConfig, y as TriggerStyle, z as VideoIntent, A as VideoMime, W as WidgetPosition, K as createBlocFeedController } from './controller-C6M-ge3K.cjs';
1
+ import { o as ScreenshotAdapter, C as CaptureConfig, h as CaptureResult, V as VideoAsset, R as RecordingConfig, H as ClickEvent, G as VoiceConfig, l as MetadataConfig, m as MetadataContext, f as BlocFeedUser, k as FeedbackPayload, u as SecuritySnapshot, s as SecurityConfig } from './controller-97Hd8v8C.cjs';
2
+ export { B as BlocFeedConfig, c as BlocFeedController, d as BlocFeedError, a as BlocFeedHandle, b as BlocFeedState, e as BlocFeedStrings, i as ConsoleEntry, D as DiagnosticsConfig, E as ElementDescriptor, j as FeedbackApiResponse, F as FeedbackCategory, J as HoverListener, I as ImageAsset, N as NetworkEntry, n as Rect, p as ScreenshotAdapterOptions, q as ScreenshotIntent, r as ScreenshotMime, t as SecurityFinding, v as SessionPhase, K as StateListener, S as SubmitResult, T as ThemeConfig, w as TransportConfig, y as TriggerStyle, z as VideoIntent, A as VideoMime, W as WidgetPosition, L as createBlocFeedController } from './controller-97Hd8v8C.cjs';
3
3
 
4
4
  declare function createHtmlToImageAdapter(): ScreenshotAdapter;
5
5
 
@@ -68,6 +68,47 @@ declare function startRecording(params: {
68
68
  declare function drainClickEvents(): ClickEvent[];
69
69
  declare function clearClickEvents(): void;
70
70
 
71
+ interface VoiceSession {
72
+ /** Resolves with the final audio Blob when recording finishes. */
73
+ result: Promise<{
74
+ blob: Blob;
75
+ mime: string;
76
+ durationMs: number;
77
+ }>;
78
+ /** Stop recording early (user clicks stop). */
79
+ stop: () => void;
80
+ /** Subscribe to elapsed-time ticks (~250 ms interval). */
81
+ onTick: (cb: (elapsedMs: number) => void) => void;
82
+ /** Abort and clean up. */
83
+ abort: () => void;
84
+ }
85
+ /**
86
+ * Returns true if the current browser supports audio recording
87
+ * (getUserMedia + MediaRecorder with WebM/Ogg audio).
88
+ */
89
+ declare function isVoiceSupported(): boolean;
90
+ /**
91
+ * Start a voice recording session using the microphone.
92
+ */
93
+ declare function startVoiceRecording(params: {
94
+ config?: VoiceConfig;
95
+ signal?: AbortSignal;
96
+ }): Promise<VoiceSession>;
97
+
98
+ /**
99
+ * Send audio blob to the voice transcription endpoint.
100
+ * Returns the transcribed text or throws on failure.
101
+ */
102
+ declare function transcribeAudio(params: {
103
+ blob: Blob;
104
+ mime: string;
105
+ blocfeedId: string;
106
+ signal?: AbortSignal;
107
+ }): Promise<{
108
+ text: string;
109
+ warning?: string;
110
+ }>;
111
+
71
112
  declare function collectMetadata(params: {
72
113
  config: MetadataConfig | undefined;
73
114
  context: MetadataContext;
@@ -162,4 +203,4 @@ declare function clearSecurityFindings(): void;
162
203
 
163
204
  declare function dataUrlToBlob(dataUrl: string): Blob;
164
205
 
165
- export { BlocFeedUser, CaptureResult, ClickEvent, FeedbackPayload, type ModernScreenshotModule, RecordingConfig, type RecordingSession, ScreenshotAdapter, SecurityConfig, SecuritySnapshot, VideoAsset, clearClickEvents, clearDiagnostics, clearQueue, clearSecurityFindings, collectMetadata, createHtmlToImageAdapter, createModernScreenshotAdapter, dataUrlToBlob, dequeueAll, drainClickEvents, drainDiagnostics, enqueue, getQueueSize, getSecurityFindings, installDiagnostics, isRecordingSupported, runCapture, runSecretScan, startRecording, uninstallDiagnostics };
206
+ export { BlocFeedUser, CaptureResult, ClickEvent, FeedbackPayload, type ModernScreenshotModule, RecordingConfig, type RecordingSession, ScreenshotAdapter, SecurityConfig, SecuritySnapshot, VideoAsset, VoiceConfig, type VoiceSession, clearClickEvents, clearDiagnostics, clearQueue, clearSecurityFindings, collectMetadata, createHtmlToImageAdapter, createModernScreenshotAdapter, dataUrlToBlob, dequeueAll, drainClickEvents, drainDiagnostics, enqueue, getQueueSize, getSecurityFindings, installDiagnostics, isRecordingSupported, isVoiceSupported, runCapture, runSecretScan, startRecording, startVoiceRecording, transcribeAudio, uninstallDiagnostics };