blocfeed 0.8.1 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +57 -0
- package/README.md +140 -0
- package/dist/chunk-D626QU52.cjs +2 -0
- package/dist/chunk-X6GNKKUZ.js +2 -0
- package/dist/{controller-BnTq9F8J.d.cts → controller-97Hd8v8C.d.cts} +34 -1
- package/dist/{controller-BnTq9F8J.d.ts → controller-97Hd8v8C.d.ts} +34 -1
- package/dist/engine.cjs +1 -1
- package/dist/engine.d.cts +47 -3
- package/dist/engine.d.ts +47 -3
- package/dist/engine.js +1 -1
- package/dist/main.cjs +42 -3
- package/dist/main.d.cts +2 -2
- package/dist/main.d.ts +2 -2
- package/dist/main.js +42 -3
- package/package.json +6 -3
- package/dist/chunk-KJRV7PRA.js +0 -2
- package/dist/chunk-ORVW2RTZ.cjs +0 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,62 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.10.0 — 2026-03-03
|
|
4
|
+
|
|
5
|
+
### New features
|
|
6
|
+
|
|
7
|
+
- **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.
|
|
8
|
+
- Enabled via `config.voice.enabled: true` (off by default)
|
|
9
|
+
- Configurable max duration (`maxDurationMs`, default 60s) with auto-stop
|
|
10
|
+
- Audio codec negotiation: WebM/Opus → WebM → Ogg/Opus
|
|
11
|
+
- Voice button (mic icon) appears in the review phase; pulsing red dot + timer during recording
|
|
12
|
+
- "Transcribing..." spinner while audio is sent to the server
|
|
13
|
+
- Graceful error handling: microphone denied, transcription failed, no speech detected
|
|
14
|
+
- Auto-hidden on unsupported browsers (no `getUserMedia` or `MediaRecorder`)
|
|
15
|
+
- Requires project-level opt-in via dashboard toggle (`voice_feedback_enabled`)
|
|
16
|
+
|
|
17
|
+
### Improvements
|
|
18
|
+
|
|
19
|
+
- New types exported: `VoiceConfig`.
|
|
20
|
+
- Engine exports: `isVoiceSupported`, `startVoiceRecording`, `transcribeAudio`, `VoiceSession` for headless usage.
|
|
21
|
+
- `BlocFeedStrings` extended with `voiceButton`, `voiceStopButton`, `voiceRecordingText`, `voiceTranscribingText`, `voiceDeniedText`, `voiceErrorText`, `voiceEmptyText`.
|
|
22
|
+
- `BlocFeedState` extended with `voiceRecording`, `voiceElapsedMs`, `voiceTranscribing` fields.
|
|
23
|
+
- `BlocFeedController` extended with `startVoice()` and `stopVoice()` methods.
|
|
24
|
+
|
|
25
|
+
### Platform changes (blocfeed-frontend)
|
|
26
|
+
|
|
27
|
+
- New SQL migration `scripts/040_voice_feedback.sql` adds `voice_feedback_enabled BOOLEAN NOT NULL DEFAULT FALSE` column to projects.
|
|
28
|
+
- 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.
|
|
29
|
+
- Dashboard Settings page adds a "Voice Feedback" toggle card under General tab.
|
|
30
|
+
- New server action `updateVoiceFeedbackEnabled` for toggling the feature per project.
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## 0.9.0 — 2026-02-25
|
|
35
|
+
|
|
36
|
+
### New features
|
|
37
|
+
|
|
38
|
+
- **Click tracking during video recording** — Every user click between recording start and stop is automatically captured and included with the feedback. Each click event records:
|
|
39
|
+
- **Timestamp** (ms offset from recording start, synced to video timeline)
|
|
40
|
+
- **Page path** (`window.location.pathname`)
|
|
41
|
+
- **Element tag** (e.g. `<button>`, `<a>`, `<div>`)
|
|
42
|
+
- **Text snippet** (truncated text content of the clicked element)
|
|
43
|
+
- **Component name** (React component name via `data-blocfeed-component` or React fiber introspection)
|
|
44
|
+
- No configuration needed — click tracking is automatic when `recording.enabled` is `true`.
|
|
45
|
+
|
|
46
|
+
### Improvements
|
|
47
|
+
|
|
48
|
+
- New types exported: `ClickEvent`.
|
|
49
|
+
- Engine exports: `drainClickEvents`, `clearClickEvents` for headless usage.
|
|
50
|
+
- `FeedbackPayload` extended with optional `click_events` field.
|
|
51
|
+
|
|
52
|
+
### Platform changes (blocfeed-frontend)
|
|
53
|
+
|
|
54
|
+
- New SQL migration `scripts/034_click_events.sql` adds `meta_click_events` JSONB column.
|
|
55
|
+
- `POST /api/feedback` now extracts and persists click events from `metadata._clickEvents`.
|
|
56
|
+
- Dashboard Video tab shows a **click timeline** below the video player. Each entry displays the timestamp, element tag, component name, text snippet, and page path. Clicking a timeline entry **seeks the video** to that moment.
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
3
60
|
## 0.8.1 — 2026-02-24
|
|
4
61
|
|
|
5
62
|
### Bug fixes
|
package/README.md
CHANGED
|
@@ -16,6 +16,8 @@ Drop-in in-app feedback widget for **Next.js** and **React**:
|
|
|
16
16
|
- **Conditional display** — show/hide widget by route pattern or custom predicate
|
|
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
|
+
- **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
|
|
19
21
|
- **Secret leak detection** — scans client-side code for exposed API keys, database credentials, and tokens
|
|
20
22
|
|
|
21
23
|
Docs live in `docs/` (start at `docs/index.md`). Architecture pointers are in `ARCHITECTURE.md`.
|
|
@@ -140,6 +142,12 @@ All configuration is passed via the `config` prop on `<BlocFeedWidget>` or `<Blo
|
|
|
140
142
|
videoBitsPerSecond: 2_500_000,
|
|
141
143
|
},
|
|
142
144
|
|
|
145
|
+
// Voice feedback (mic recording + transcription)
|
|
146
|
+
voice: {
|
|
147
|
+
enabled: true,
|
|
148
|
+
maxDurationMs: 60_000,
|
|
149
|
+
},
|
|
150
|
+
|
|
143
151
|
// Screenshot defaults
|
|
144
152
|
capture: {
|
|
145
153
|
element: true,
|
|
@@ -294,6 +302,20 @@ config={{
|
|
|
294
302
|
}}
|
|
295
303
|
```
|
|
296
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
|
+
|
|
297
319
|
All fields are optional — only override the ones you need. Category labels are also localizable:
|
|
298
320
|
|
|
299
321
|
```tsx
|
|
@@ -739,6 +761,122 @@ strings: {
|
|
|
739
761
|
}
|
|
740
762
|
```
|
|
741
763
|
|
|
764
|
+
### Click tracking during recording
|
|
765
|
+
|
|
766
|
+
When video recording is active, BlocFeed automatically captures every user click on the page. Each click event records:
|
|
767
|
+
|
|
768
|
+
| Field | Description |
|
|
769
|
+
|-------|-------------|
|
|
770
|
+
| `timestampMs` | Milliseconds from recording start (synced to video timeline) |
|
|
771
|
+
| `path` | `window.location.pathname` at the time of click |
|
|
772
|
+
| `tagName` | HTML tag of the clicked element (e.g. `button`, `a`) |
|
|
773
|
+
| `textSnippet` | Truncated text content of the clicked element |
|
|
774
|
+
| `componentName` | React component name (via `data-blocfeed-component` or fiber introspection) |
|
|
775
|
+
|
|
776
|
+
Click events are included in the payload as `metadata._clickEvents` and displayed as a **click timeline** in the dashboard Video tab. Clicking a timeline entry seeks the video to that moment.
|
|
777
|
+
|
|
778
|
+
No extra configuration is needed — click tracking is automatic when `recording.enabled` is `true`. Up to 200 click events are captured per recording session. Clicks on the BlocFeed widget itself are excluded.
|
|
779
|
+
|
|
780
|
+
#### Headless click tracking
|
|
781
|
+
|
|
782
|
+
```ts
|
|
783
|
+
import { drainClickEvents, clearClickEvents } from "blocfeed/engine";
|
|
784
|
+
|
|
785
|
+
const events = drainClickEvents(); // returns ClickEvent[]
|
|
786
|
+
clearClickEvents(); // reset the buffer
|
|
787
|
+
```
|
|
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
|
+
|
|
742
880
|
### Known limitations
|
|
743
881
|
|
|
744
882
|
The default screenshot engine (`html-to-image`) has known issues with:
|
|
@@ -855,6 +993,8 @@ import type {
|
|
|
855
993
|
SecurityConfig,
|
|
856
994
|
SecurityFinding,
|
|
857
995
|
SecuritySnapshot,
|
|
996
|
+
ClickEvent,
|
|
997
|
+
VoiceConfig,
|
|
858
998
|
// ... and more
|
|
859
999
|
} from "blocfeed";
|
|
860
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 w=$(c._debugInfo);if(w)return w;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 w=$(c._debugInfo);if(w)return w;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 w=P(i.type)??P(i.elementType);if(w&&!U(w))return w;i=i.return;}let s=e.parentElement;for(let c=0;s&&c<15;c+=1){let u=xe(s);if(u){let w=$(u._debugInfo);if(w)return w;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 Me(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 Fe(){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 Fe();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 Fe();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 Ie(){return Date.now()}function bt(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 Be(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(bt(n)),await Promise.race(r)}function wt(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=Ie(),i=[],a=n?.timeoutMs??pt,s=n?.maxDimension??gt,c=n?.mime??"image/png",u=n?.quality??ht,w=n?.adapter??Le(),m={},l=wt(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 Be(Promise.resolve(w.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 Be(Promise.resolve(w.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 b=Ie(),f={startedAt:r,finishedAt:b,durationMs:Math.max(0,b-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,Mt=1e3,Ft=["video/webm;codecs=vp9","video/webm;codecs=vp8","video/webm"];function Xe(){if(typeof MediaRecorder>"u")return null;for(let e of Ft)if(MediaRecorder.isTypeSupported(e))return e;return null}function Dt(){return !y()||typeof navigator?.mediaDevices?.getDisplayMedia!="function"?false:Xe()!==null}async function be(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=[],w=0,m=null,l=null,b=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(b)return;b=true,f();let v=Date.now()-w,C=new Blob(c,{type:$e}),L=URL.createObjectURL(C);d({mime:$e,blobUrl:L,blob:C,durationMs:v,sizeBytes:C.size});},s.onerror=()=>{b||(b=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=()=>{b||(b=true,s.state!=="inactive"&&s.stop(),f(),k(h("aborted",new Error("Recording aborted"))));};n.addEventListener("abort",v,{once:true});}});return s.start(Mt),ze(),w=Date.now(),m=setInterval(()=>{let d=Date.now()-w;for(let k of u)k(d);},Ct),l=setTimeout(()=>{!b&&s.state!=="inactive"&&s.stop();},i),{result:p,stop(){!b&&s.state!=="inactive"&&s.stop();},onTick(d){u.push(d);},abort(){b||(b=true,s.state!=="inactive"&&s.stop(),f());}}}var Lt=6e4,Ze="audio/webm",It=250,Bt=["audio/webm;codecs=opus","audio/webm","audio/ogg;codecs=opus"];function We(){if(typeof MediaRecorder>"u")return null;for(let e of Bt)if(MediaRecorder.isTypeSupported(e))return e;return null}function Nt(){return !y()||typeof navigator?.mediaDevices?.getUserMedia!="function"?false:We()!==null}async function we(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,w=null,m=null,l=false,b=()=>{w!==null&&(clearInterval(w),w=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,b();let k=Date.now()-u,_=new Blob(s,{type:Ze});p({blob:_,mime:Ze,durationMs:k});},a.onerror=()=>{l||(l=true,b(),d(h("recording_failed",new Error("MediaRecorder error"))));},n){let k=()=>{l||(l=true,a.state!=="inactive"&&a.stop(),b(),d(h("aborted",new Error("Voice recording aborted"))));};n.addEventListener("abort",k,{once:true});}});return a.start(1e3),u=Date.now(),w=setInterval(()=>{let p=Date.now()-u;for(let d of c)d(p);},It),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(),b());}}}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:w,extracted:m,extractedVideo:l}=u?$t(e.payload):{lean:e.payload,extracted:{},extractedVideo:{}},b={...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(w),signal:p.signal});if(_.ok){Ke=Date.now();let v;try{v=await _.json();}catch{}if((b.element||b.fullPage)&&v){let M=v.upload_urls;if(M){let H=[];b.element&&M.element&&H.push(Ye(M.element,b.element,t)),b.fullPage&&M.fullPage&&H.push(Ye(M.fullPage,b.fullPage,t));try{await Promise.all(H);}catch{}}else if(v.feedback_id)try{await Xt({feedbackId:v.feedback_id,extracted:b,screenshots:e.payload.screenshots,...t?{signal:t}:{}});}catch{}}if(l.blob&&v){let M=v.upload_urls;if(M?.video)try{await fetch(M.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);}),w=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:Me(d)});},b=f=>{f.key==="Escape"&&(ne(f),f.preventDefault(),t.onCancel());};return window.addEventListener("pointermove",c,{capture:true,passive:true}),window.addEventListener("pointerdown",w,{capture:true}),window.addEventListener("pointerup",m,{capture:true}),window.addEventListener("click",l,{capture:true}),window.addEventListener("keydown",b,{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",w,{capture:true}),window.removeEventListener("pointerup",m,{capture:true}),window.removeEventListener("click",l,{capture:true}),window.removeEventListener("keydown",b,{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,w=null,m=null,l=null,b=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=()=>{b&&(b.abort(),b=null);},L=()=>{k(),_(),v(),C(),a=null,d(tt());},M=()=>{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),w=()=>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"&&M();},stop(){L();},clearSelection(){n.phase==="capturing"||n.phase==="submitting"||n.phase==="recording"||M();},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 F={phase:"error",lastError:h("configuration",new Error("Missing blocfeed_id. Create a project in BlocFeed and pass its blocfeed_id."))};return n.selection&&(F.selection=n.selection),d(F),{ok:false}}if(n.phase==="capturing"||n.phase==="submitting"||n.phase==="recording")return {ok:false};let A=u+1;u=A,c?.abort(),c=new AbortController;let R=c.signal,S=n.selection,D=E?.capture?{...t.capture,...E.capture}:t.capture,I=!!(D?.element||D?.fullPage),z={phase:I?"capturing":"submitting"};S&&(z.selection=S),d(z);try{let T=I?await Oe({selectionElement:a,capture:D,signal:R}):void 0;if(R.aborted||u!==A)return {ok:!1};let F={phase:"submitting"};S&&(F.selection=S),T&&(F.capture=T),d(F);let B={};S&&(B.selection=S),T&&(B.capture=T);let j=await Ue({config:t.metadata,context:B,...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:R,...t.transport?{transport:t.transport}:{}});if(R.aborted||u!==A)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(R.aborted||u!==A)return {ok:false};let B={phase:"error",lastError:R.aborted?h("aborted",T):h("unknown",T)};return S&&(B.selection=S),d(B),{ok:false}}finally{u===A&&(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 A=await be(x);m=A,A.onTick(D=>{if(n.phase==="recording"){let I={phase:"recording",recordingElapsedMs:D};g&&(I.selection=g),d(I);}});let R=await A.result;m=null,l=R;let S={phase:"review",video:R};g&&(S.selection=g),d(S);}catch(x){m=null;let R={phase:"review",lastError:x?.kind?x:h("recording_failed",x)};g&&(R.selection=g),d(R);}},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")return;let g=t.voice;if(!g?.enabled)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 A={config:g};c&&(A.signal=c.signal);let R=await we(A);b=R,R.onTick(B=>{let j={phase:"review",voiceRecording:!0,voiceElapsedMs:B};E&&(j.selection=E),l&&(j.video=l),d(j);});let S=await R.result;b=null;let D={phase:"review",voiceRecording:!1,voiceTranscribing:!0};E&&(D.selection=E),l&&(D.video=l),d(D);let I={blob:S.blob,mime:S.mime,blocfeedId:t.blocfeed_id};c&&(I.signal=c.signal);let{text:Te,warning:z}=await Je(I),T={phase:"review",voiceRecording:!1,voiceTranscribing:!1};E&&(T.selection=E),l&&(T.video=l),d(T);let F={text:Te};return z&&(F.warning=z),F}catch(A){C();let S={phase:"review",voiceRecording:false,voiceTranscribing:false,lastError:A?.kind?A:h("recording_failed",A)};E&&(S.selection=E),l&&(S.video=l),d(S);return}},stopVoice(){b&&b.stop();},__unsafeGetSelectedElement(){return a},destroy(){L(),o.clear(),r.clear(),w?.(),w=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=be;exports.n=Nt;exports.o=we;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 w=$(c._debugInfo);if(w)return w;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 w=$(c._debugInfo);if(w)return w;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 w=P(i.type)??P(i.elementType);if(w&&!U(w))return w;i=i.return;}let s=e.parentElement;for(let c=0;s&&c<15;c+=1){let u=xe(s);if(u){let w=$(u._debugInfo);if(w)return w;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 Me(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 Fe(){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 Fe();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 Fe();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 Ie(){return Date.now()}function bt(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 Be(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(bt(n)),await Promise.race(r)}function wt(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=Ie(),i=[],a=n?.timeoutMs??pt,s=n?.maxDimension??gt,c=n?.mime??"image/png",u=n?.quality??ht,w=n?.adapter??Le(),m={},l=wt(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 Be(Promise.resolve(w.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 Be(Promise.resolve(w.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 b=Ie(),f={startedAt:r,finishedAt:b,durationMs:Math.max(0,b-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,Mt=1e3,Ft=["video/webm;codecs=vp9","video/webm;codecs=vp8","video/webm"];function Xe(){if(typeof MediaRecorder>"u")return null;for(let e of Ft)if(MediaRecorder.isTypeSupported(e))return e;return null}function Dt(){return !y()||typeof navigator?.mediaDevices?.getDisplayMedia!="function"?false:Xe()!==null}async function be(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=[],w=0,m=null,l=null,b=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(b)return;b=true,f();let v=Date.now()-w,C=new Blob(c,{type:$e}),L=URL.createObjectURL(C);d({mime:$e,blobUrl:L,blob:C,durationMs:v,sizeBytes:C.size});},s.onerror=()=>{b||(b=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=()=>{b||(b=true,s.state!=="inactive"&&s.stop(),f(),k(h("aborted",new Error("Recording aborted"))));};n.addEventListener("abort",v,{once:true});}});return s.start(Mt),ze(),w=Date.now(),m=setInterval(()=>{let d=Date.now()-w;for(let k of u)k(d);},Ct),l=setTimeout(()=>{!b&&s.state!=="inactive"&&s.stop();},i),{result:p,stop(){!b&&s.state!=="inactive"&&s.stop();},onTick(d){u.push(d);},abort(){b||(b=true,s.state!=="inactive"&&s.stop(),f());}}}var Lt=6e4,Ze="audio/webm",It=250,Bt=["audio/webm;codecs=opus","audio/webm","audio/ogg;codecs=opus"];function We(){if(typeof MediaRecorder>"u")return null;for(let e of Bt)if(MediaRecorder.isTypeSupported(e))return e;return null}function Nt(){return !y()||typeof navigator?.mediaDevices?.getUserMedia!="function"?false:We()!==null}async function we(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,w=null,m=null,l=false,b=()=>{w!==null&&(clearInterval(w),w=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,b();let k=Date.now()-u,_=new Blob(s,{type:Ze});p({blob:_,mime:Ze,durationMs:k});},a.onerror=()=>{l||(l=true,b(),d(h("recording_failed",new Error("MediaRecorder error"))));},n){let k=()=>{l||(l=true,a.state!=="inactive"&&a.stop(),b(),d(h("aborted",new Error("Voice recording aborted"))));};n.addEventListener("abort",k,{once:true});}});return a.start(1e3),u=Date.now(),w=setInterval(()=>{let p=Date.now()-u;for(let d of c)d(p);},It),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(),b());}}}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:w,extracted:m,extractedVideo:l}=u?$t(e.payload):{lean:e.payload,extracted:{},extractedVideo:{}},b={...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(w),signal:p.signal});if(_.ok){Ke=Date.now();let v;try{v=await _.json();}catch{}if((b.element||b.fullPage)&&v){let M=v.upload_urls;if(M){let H=[];b.element&&M.element&&H.push(Ye(M.element,b.element,t)),b.fullPage&&M.fullPage&&H.push(Ye(M.fullPage,b.fullPage,t));try{await Promise.all(H);}catch{}}else if(v.feedback_id)try{await Xt({feedbackId:v.feedback_id,extracted:b,screenshots:e.payload.screenshots,...t?{signal:t}:{}});}catch{}}if(l.blob&&v){let M=v.upload_urls;if(M?.video)try{await fetch(M.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);}),w=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:Me(d)});},b=f=>{f.key==="Escape"&&(ne(f),f.preventDefault(),t.onCancel());};return window.addEventListener("pointermove",c,{capture:true,passive:true}),window.addEventListener("pointerdown",w,{capture:true}),window.addEventListener("pointerup",m,{capture:true}),window.addEventListener("click",l,{capture:true}),window.addEventListener("keydown",b,{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",w,{capture:true}),window.removeEventListener("pointerup",m,{capture:true}),window.removeEventListener("click",l,{capture:true}),window.removeEventListener("keydown",b,{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,w=null,m=null,l=null,b=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=()=>{b&&(b.abort(),b=null);},L=()=>{k(),_(),v(),C(),a=null,d(tt());},M=()=>{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),w=()=>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"&&M();},stop(){L();},clearSelection(){n.phase==="capturing"||n.phase==="submitting"||n.phase==="recording"||M();},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 F={phase:"error",lastError:h("configuration",new Error("Missing blocfeed_id. Create a project in BlocFeed and pass its blocfeed_id."))};return n.selection&&(F.selection=n.selection),d(F),{ok:false}}if(n.phase==="capturing"||n.phase==="submitting"||n.phase==="recording")return {ok:false};let A=u+1;u=A,c?.abort(),c=new AbortController;let R=c.signal,S=n.selection,D=E?.capture?{...t.capture,...E.capture}:t.capture,I=!!(D?.element||D?.fullPage),z={phase:I?"capturing":"submitting"};S&&(z.selection=S),d(z);try{let T=I?await Oe({selectionElement:a,capture:D,signal:R}):void 0;if(R.aborted||u!==A)return {ok:!1};let F={phase:"submitting"};S&&(F.selection=S),T&&(F.capture=T),d(F);let B={};S&&(B.selection=S),T&&(B.capture=T);let j=await Ue({config:t.metadata,context:B,...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:R,...t.transport?{transport:t.transport}:{}});if(R.aborted||u!==A)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(R.aborted||u!==A)return {ok:false};let B={phase:"error",lastError:R.aborted?h("aborted",T):h("unknown",T)};return S&&(B.selection=S),d(B),{ok:false}}finally{u===A&&(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 A=await be(x);m=A,A.onTick(D=>{if(n.phase==="recording"){let I={phase:"recording",recordingElapsedMs:D};g&&(I.selection=g),d(I);}});let R=await A.result;m=null,l=R;let S={phase:"review",video:R};g&&(S.selection=g),d(S);}catch(x){m=null;let R={phase:"review",lastError:x?.kind?x:h("recording_failed",x)};g&&(R.selection=g),d(R);}},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")return;let g=t.voice;if(!g?.enabled)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 A={config:g};c&&(A.signal=c.signal);let R=await we(A);b=R,R.onTick(B=>{let j={phase:"review",voiceRecording:!0,voiceElapsedMs:B};E&&(j.selection=E),l&&(j.video=l),d(j);});let S=await R.result;b=null;let D={phase:"review",voiceRecording:!1,voiceTranscribing:!0};E&&(D.selection=E),l&&(D.video=l),d(D);let I={blob:S.blob,mime:S.mime,blocfeedId:t.blocfeed_id};c&&(I.signal=c.signal);let{text:Te,warning:z}=await Je(I),T={phase:"review",voiceRecording:!1,voiceTranscribing:!1};E&&(T.selection=E),l&&(T.video=l),d(T);let F={text:Te};return z&&(F.warning=z),F}catch(A){C();let S={phase:"review",voiceRecording:false,voiceTranscribing:false,lastError:A?.kind?A:h("recording_failed",A)};E&&(S.selection=E),l&&(S.video=l),d(S);return}},stopVoice(){b&&b.stop();},__unsafeGetSelectedElement(){return a},destroy(){L(),o.clear(),r.clear(),w?.(),w=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,be as m,Nt as n,we 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. */
|
|
@@ -219,6 +225,18 @@ interface VideoIntent {
|
|
|
219
225
|
durationMs: number;
|
|
220
226
|
sizeBytes: number;
|
|
221
227
|
}
|
|
228
|
+
interface ClickEvent {
|
|
229
|
+
/** Ms offset from recording start — maps to video timeline. */
|
|
230
|
+
timestampMs: number;
|
|
231
|
+
/** window.location.pathname at click time. */
|
|
232
|
+
path: string;
|
|
233
|
+
/** HTML tag name (lowercase). */
|
|
234
|
+
tagName: string;
|
|
235
|
+
/** Text content (truncated). */
|
|
236
|
+
textSnippet?: string;
|
|
237
|
+
/** React/annotated component name. */
|
|
238
|
+
componentName?: string;
|
|
239
|
+
}
|
|
222
240
|
interface CaptureConfig {
|
|
223
241
|
element?: boolean;
|
|
224
242
|
fullPage?: boolean;
|
|
@@ -279,6 +297,8 @@ interface BlocFeedConfig {
|
|
|
279
297
|
security?: SecurityConfig;
|
|
280
298
|
/** Video recording configuration. */
|
|
281
299
|
recording?: RecordingConfig;
|
|
300
|
+
/** Voice feedback configuration (audio recording + transcription). */
|
|
301
|
+
voice?: VoiceConfig;
|
|
282
302
|
ui?: {
|
|
283
303
|
/** z-index for the widget overlay/panel. */
|
|
284
304
|
zIndex?: number;
|
|
@@ -334,6 +354,8 @@ interface FeedbackPayload {
|
|
|
334
354
|
video?: VideoAsset;
|
|
335
355
|
/** Lightweight video metadata sent instead of the blob. */
|
|
336
356
|
video_intent?: VideoIntent;
|
|
357
|
+
/** Click events captured during video recording. */
|
|
358
|
+
click_events?: ClickEvent[];
|
|
337
359
|
/** First-class user identity. */
|
|
338
360
|
user?: BlocFeedUser;
|
|
339
361
|
metadata: Record<string, unknown>;
|
|
@@ -365,6 +387,12 @@ interface BlocFeedState {
|
|
|
365
387
|
recordingElapsedMs?: number;
|
|
366
388
|
lastSubmit?: SubmitResult;
|
|
367
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;
|
|
368
396
|
}
|
|
369
397
|
|
|
370
398
|
type HoverInfo = {
|
|
@@ -389,10 +417,15 @@ interface BlocFeedController {
|
|
|
389
417
|
startRecording: () => Promise<void>;
|
|
390
418
|
stopRecording: () => void;
|
|
391
419
|
removeVideo: () => void;
|
|
420
|
+
startVoice: () => Promise<{
|
|
421
|
+
text: string;
|
|
422
|
+
warning?: string;
|
|
423
|
+
} | undefined>;
|
|
424
|
+
stopVoice: () => void;
|
|
392
425
|
/** Internal: used by UI to access the live element handle. */
|
|
393
426
|
__unsafeGetSelectedElement: () => Element | null;
|
|
394
427
|
destroy: () => void;
|
|
395
428
|
}
|
|
396
429
|
declare function createBlocFeedController(config: BlocFeedConfig): BlocFeedController;
|
|
397
430
|
|
|
398
|
-
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
|
|
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. */
|
|
@@ -219,6 +225,18 @@ interface VideoIntent {
|
|
|
219
225
|
durationMs: number;
|
|
220
226
|
sizeBytes: number;
|
|
221
227
|
}
|
|
228
|
+
interface ClickEvent {
|
|
229
|
+
/** Ms offset from recording start — maps to video timeline. */
|
|
230
|
+
timestampMs: number;
|
|
231
|
+
/** window.location.pathname at click time. */
|
|
232
|
+
path: string;
|
|
233
|
+
/** HTML tag name (lowercase). */
|
|
234
|
+
tagName: string;
|
|
235
|
+
/** Text content (truncated). */
|
|
236
|
+
textSnippet?: string;
|
|
237
|
+
/** React/annotated component name. */
|
|
238
|
+
componentName?: string;
|
|
239
|
+
}
|
|
222
240
|
interface CaptureConfig {
|
|
223
241
|
element?: boolean;
|
|
224
242
|
fullPage?: boolean;
|
|
@@ -279,6 +297,8 @@ interface BlocFeedConfig {
|
|
|
279
297
|
security?: SecurityConfig;
|
|
280
298
|
/** Video recording configuration. */
|
|
281
299
|
recording?: RecordingConfig;
|
|
300
|
+
/** Voice feedback configuration (audio recording + transcription). */
|
|
301
|
+
voice?: VoiceConfig;
|
|
282
302
|
ui?: {
|
|
283
303
|
/** z-index for the widget overlay/panel. */
|
|
284
304
|
zIndex?: number;
|
|
@@ -334,6 +354,8 @@ interface FeedbackPayload {
|
|
|
334
354
|
video?: VideoAsset;
|
|
335
355
|
/** Lightweight video metadata sent instead of the blob. */
|
|
336
356
|
video_intent?: VideoIntent;
|
|
357
|
+
/** Click events captured during video recording. */
|
|
358
|
+
click_events?: ClickEvent[];
|
|
337
359
|
/** First-class user identity. */
|
|
338
360
|
user?: BlocFeedUser;
|
|
339
361
|
metadata: Record<string, unknown>;
|
|
@@ -365,6 +387,12 @@ interface BlocFeedState {
|
|
|
365
387
|
recordingElapsedMs?: number;
|
|
366
388
|
lastSubmit?: SubmitResult;
|
|
367
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;
|
|
368
396
|
}
|
|
369
397
|
|
|
370
398
|
type HoverInfo = {
|
|
@@ -389,10 +417,15 @@ interface BlocFeedController {
|
|
|
389
417
|
startRecording: () => Promise<void>;
|
|
390
418
|
stopRecording: () => void;
|
|
391
419
|
removeVideo: () => void;
|
|
420
|
+
startVoice: () => Promise<{
|
|
421
|
+
text: string;
|
|
422
|
+
warning?: string;
|
|
423
|
+
} | undefined>;
|
|
424
|
+
stopVoice: () => void;
|
|
392
425
|
/** Internal: used by UI to access the live element handle. */
|
|
393
426
|
__unsafeGetSelectedElement: () => Element | null;
|
|
394
427
|
destroy: () => void;
|
|
395
428
|
}
|
|
396
429
|
declare function createBlocFeedController(config: BlocFeedConfig): BlocFeedController;
|
|
397
430
|
|
|
398
|
-
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
|
|
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
|
|
1
|
+
'use strict';var chunkD626QU52_cjs=require('./chunk-D626QU52.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(!chunkD626QU52_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(!chunkD626QU52_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 chunkD626QU52_cjs.k}});Object.defineProperty(exports,"clearDiagnostics",{enumerable:true,get:function(){return chunkD626QU52_cjs.u}});Object.defineProperty(exports,"clearQueue",{enumerable:true,get:function(){return chunkD626QU52_cjs.h}});Object.defineProperty(exports,"clearSecurityFindings",{enumerable:true,get:function(){return chunkD626QU52_cjs.x}});Object.defineProperty(exports,"collectMetadata",{enumerable:true,get:function(){return chunkD626QU52_cjs.e}});Object.defineProperty(exports,"createBlocFeedController",{enumerable:true,get:function(){return chunkD626QU52_cjs.q}});Object.defineProperty(exports,"createHtmlToImageAdapter",{enumerable:true,get:function(){return chunkD626QU52_cjs.c}});Object.defineProperty(exports,"dequeueAll",{enumerable:true,get:function(){return chunkD626QU52_cjs.g}});Object.defineProperty(exports,"drainClickEvents",{enumerable:true,get:function(){return chunkD626QU52_cjs.j}});Object.defineProperty(exports,"drainDiagnostics",{enumerable:true,get:function(){return chunkD626QU52_cjs.t}});Object.defineProperty(exports,"enqueue",{enumerable:true,get:function(){return chunkD626QU52_cjs.f}});Object.defineProperty(exports,"getQueueSize",{enumerable:true,get:function(){return chunkD626QU52_cjs.i}});Object.defineProperty(exports,"getSecurityFindings",{enumerable:true,get:function(){return chunkD626QU52_cjs.w}});Object.defineProperty(exports,"installDiagnostics",{enumerable:true,get:function(){return chunkD626QU52_cjs.r}});Object.defineProperty(exports,"isRecordingSupported",{enumerable:true,get:function(){return chunkD626QU52_cjs.l}});Object.defineProperty(exports,"isVoiceSupported",{enumerable:true,get:function(){return chunkD626QU52_cjs.n}});Object.defineProperty(exports,"runCapture",{enumerable:true,get:function(){return chunkD626QU52_cjs.d}});Object.defineProperty(exports,"runSecretScan",{enumerable:true,get:function(){return chunkD626QU52_cjs.v}});Object.defineProperty(exports,"startRecording",{enumerable:true,get:function(){return chunkD626QU52_cjs.m}});Object.defineProperty(exports,"startVoiceRecording",{enumerable:true,get:function(){return chunkD626QU52_cjs.o}});Object.defineProperty(exports,"transcribeAudio",{enumerable:true,get:function(){return chunkD626QU52_cjs.p}});Object.defineProperty(exports,"uninstallDiagnostics",{enumerable:true,get:function(){return chunkD626QU52_cjs.s}});exports.createModernScreenshotAdapter=v;exports.dataUrlToBlob=H;
|