blocfeed 0.7.3 → 0.8.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 +34 -0
- package/README.md +92 -1
- package/dist/chunk-KJRV7PRA.js +2 -0
- package/dist/chunk-ORVW2RTZ.cjs +2 -0
- package/dist/{controller-D0bplVji.d.cts → controller-BnTq9F8J.d.cts} +50 -3
- package/dist/{controller-D0bplVji.d.ts → controller-BnTq9F8J.d.ts} +50 -3
- package/dist/engine.cjs +1 -1
- package/dist/engine.d.cts +30 -3
- package/dist/engine.d.ts +30 -3
- package/dist/engine.js +1 -1
- package/dist/main.cjs +107 -4
- package/dist/main.d.cts +5 -2
- package/dist/main.d.ts +5 -2
- package/dist/main.js +107 -4
- package/package.json +4 -2
- package/dist/chunk-2BPYL6WQ.js +0 -2
- package/dist/chunk-FOZ6KYBJ.cjs +0 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,39 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.8.0 — 2026-02-24
|
|
4
|
+
|
|
5
|
+
### New features
|
|
6
|
+
|
|
7
|
+
- **Video recording** — Users can record a short screen capture clip alongside their feedback to demonstrate bug reproduction steps. Uses the browser's Screen Capture API (`getDisplayMedia`) with WebM output via `MediaRecorder`.
|
|
8
|
+
- Enabled via `config.recording.enabled: true` (off by default)
|
|
9
|
+
- Configurable max duration (`maxDurationMs`, default 30s) with auto-stop
|
|
10
|
+
- Configurable bitrate (`videoBitsPerSecond`, default 2.5 Mbps, ~9 MB for 30s)
|
|
11
|
+
- Codec negotiation: VP9 → VP8 → plain WebM
|
|
12
|
+
- Record button appears in review phase; pulsing red dot + timer during recording
|
|
13
|
+
- Video preview with playback controls before submission
|
|
14
|
+
- Direct upload to object storage via presigned URL (no server proxy)
|
|
15
|
+
- Auto-hidden on unsupported browsers (Safari, older browsers without `getDisplayMedia` + WebM `MediaRecorder`)
|
|
16
|
+
- **Programmatic recording API** — `startRecording()` and `stopRecording()` on `BlocFeedHandle` ref and `useBlocFeed()` hook
|
|
17
|
+
|
|
18
|
+
### Improvements
|
|
19
|
+
|
|
20
|
+
- New types exported: `RecordingConfig`, `VideoAsset`, `VideoIntent`, `VideoMime`.
|
|
21
|
+
- Engine exports: `startRecording`, `isRecordingSupported`, `RecordingSession` for headless usage.
|
|
22
|
+
- `BlocFeedStrings` extended with `recordButton`, `stopRecordButton`, `recordingText`, `recordingDeniedText`, `videoPreviewLabel`, `removeVideoButton`.
|
|
23
|
+
- Offline queue now strips video blobs (in addition to screenshots) before `localStorage` serialization.
|
|
24
|
+
- `SessionPhase` extended with `"recording"` phase.
|
|
25
|
+
- `BlocFeedError.kind` extended with `"recording_failed"`.
|
|
26
|
+
|
|
27
|
+
### Platform changes (blocfeed-frontend)
|
|
28
|
+
|
|
29
|
+
- New SQL migration `scripts/032_video_recording.sql` adds `video_url`, `video_mime`, `video_duration_ms`, `video_size_bytes`, `video_uploaded_at` columns with partial index.
|
|
30
|
+
- `POST /api/feedback` now accepts `video_intent`, inserts video metadata, and returns `upload_urls.video` presigned URL.
|
|
31
|
+
- New `POST /api/feedback/:id/video` fallback endpoint for uploading video blobs when presigned URLs are unavailable.
|
|
32
|
+
- Dashboard feedback detail panel shows a Video tab with `<video>` player when a recording is attached.
|
|
33
|
+
- Fixed `createPresignedUploadUrl` default content type from `"audio/webm"` to `"application/octet-stream"`.
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
3
37
|
## 0.7.3 — 2026-02-23
|
|
4
38
|
|
|
5
39
|
### Improvements
|
package/README.md
CHANGED
|
@@ -15,6 +15,7 @@ Drop-in in-app feedback widget for **Next.js** and **React**:
|
|
|
15
15
|
- **Dark / Light mode** — `"dark"`, `"light"`, or `"auto"` (follows system preference)
|
|
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
|
+
- **Video recording** — record a short screen capture clip (via `getDisplayMedia`) to show bug reproduction steps
|
|
18
19
|
- **Secret leak detection** — scans client-side code for exposed API keys, database credentials, and tokens
|
|
19
20
|
|
|
20
21
|
Docs live in `docs/` (start at `docs/index.md`). Architecture pointers are in `ARCHITECTURE.md`.
|
|
@@ -132,6 +133,13 @@ All configuration is passed via the `config` prop on `<BlocFeedWidget>` or `<Blo
|
|
|
132
133
|
// ignoreUrls: [], // override default blocked domains (see docs below)
|
|
133
134
|
},
|
|
134
135
|
|
|
136
|
+
// Video recording (screen capture)
|
|
137
|
+
recording: {
|
|
138
|
+
enabled: true,
|
|
139
|
+
maxDurationMs: 30_000,
|
|
140
|
+
videoBitsPerSecond: 2_500_000,
|
|
141
|
+
},
|
|
142
|
+
|
|
135
143
|
// Screenshot defaults
|
|
136
144
|
capture: {
|
|
137
145
|
element: true,
|
|
@@ -601,7 +609,7 @@ When a submission fails due to a network error, the payload is automatically sav
|
|
|
601
609
|
- On the next page load (1s after controller initialization)
|
|
602
610
|
- When the browser comes back online (`online` event)
|
|
603
611
|
|
|
604
|
-
Screenshots are stripped from queued payloads to stay within `localStorage` limits. The queue holds up to 50 items (FIFO eviction).
|
|
612
|
+
Screenshots and video recordings are stripped from queued payloads to stay within `localStorage` limits. The queue holds up to 50 items (FIFO eviction).
|
|
605
613
|
|
|
606
614
|
## Picking rules (ignore / filter)
|
|
607
615
|
|
|
@@ -652,6 +660,85 @@ Screenshots are uploaded efficiently using a two-phase flow:
|
|
|
652
660
|
|
|
653
661
|
Both **element** and **full-page** screenshots are supported and stored on the platform.
|
|
654
662
|
|
|
663
|
+
## Video Recording
|
|
664
|
+
|
|
665
|
+
Let users record a short screen capture clip alongside their feedback. Uses the browser's Screen Capture API (`getDisplayMedia`) to record the current tab as WebM video.
|
|
666
|
+
|
|
667
|
+
```tsx
|
|
668
|
+
<BlocFeedWidget
|
|
669
|
+
blocfeed_id="bf_..."
|
|
670
|
+
config={{
|
|
671
|
+
recording: {
|
|
672
|
+
enabled: true, // default: false
|
|
673
|
+
maxDurationMs: 30_000, // default: 30s — auto-stops at this limit
|
|
674
|
+
videoBitsPerSecond: 2_500_000, // default: 2.5 Mbps (~9 MB for 30s)
|
|
675
|
+
},
|
|
676
|
+
}}
|
|
677
|
+
/>
|
|
678
|
+
```
|
|
679
|
+
|
|
680
|
+
### `RecordingConfig` options
|
|
681
|
+
|
|
682
|
+
| Option | Type | Default | Description |
|
|
683
|
+
|--------|------|---------|-------------|
|
|
684
|
+
| `enabled` | `boolean` | `false` | Show the Record button in the review phase |
|
|
685
|
+
| `maxDurationMs` | `number` | `30000` | Maximum recording duration in ms. Auto-stops when reached |
|
|
686
|
+
| `mime` | `"video/webm"` | `"video/webm"` | Output format (WebM is the only supported format) |
|
|
687
|
+
| `videoBitsPerSecond` | `number` | `2500000` | Video bitrate. Lower = smaller files, faster uploads |
|
|
688
|
+
|
|
689
|
+
### Bitrate & file size guide
|
|
690
|
+
|
|
691
|
+
| `videoBitsPerSecond` | 30s file size | Upload time (10 Mbps) |
|
|
692
|
+
|---|---|---|
|
|
693
|
+
| `1_000_000` (1 Mbps) | ~3.75 MB | ~3s |
|
|
694
|
+
| `2_500_000` (2.5 Mbps) | ~9.4 MB | ~7s |
|
|
695
|
+
| `5_000_000` (5 Mbps) | ~18.8 MB | ~15s |
|
|
696
|
+
|
|
697
|
+
Lower bitrates are fine for bug reproduction clips. 2.5 Mbps (default) gives crisp screen recordings.
|
|
698
|
+
|
|
699
|
+
### How it works
|
|
700
|
+
|
|
701
|
+
1. A **Record** button appears in the review phase (after element selection)
|
|
702
|
+
2. Clicking it triggers the browser's screen sharing permission prompt
|
|
703
|
+
3. A pulsing red dot + elapsed timer shows during recording
|
|
704
|
+
4. Recording stops when the user clicks **Stop**, the max duration is reached, or the browser's "Stop sharing" button is clicked
|
|
705
|
+
5. A video preview with playback controls appears — the user can remove it or submit
|
|
706
|
+
6. On submit, the video is uploaded directly to object storage via a presigned URL
|
|
707
|
+
|
|
708
|
+
### Browser support
|
|
709
|
+
|
|
710
|
+
Video recording requires `getDisplayMedia` and `MediaRecorder` with WebM support. The Record button is **automatically hidden** on unsupported browsers.
|
|
711
|
+
|
|
712
|
+
| Browser | Supported |
|
|
713
|
+
|---------|-----------|
|
|
714
|
+
| Chrome / Edge | Yes |
|
|
715
|
+
| Firefox | Yes |
|
|
716
|
+
| Safari 16+ | No (WebM not supported by MediaRecorder) |
|
|
717
|
+
|
|
718
|
+
### Programmatic API
|
|
719
|
+
|
|
720
|
+
```tsx
|
|
721
|
+
const ref = useRef<BlocFeedHandle>(null);
|
|
722
|
+
|
|
723
|
+
ref.current?.startRecording(); // start recording (must be in review phase)
|
|
724
|
+
ref.current?.stopRecording(); // stop recording
|
|
725
|
+
```
|
|
726
|
+
|
|
727
|
+
### Localization
|
|
728
|
+
|
|
729
|
+
Override recording-related UI text via `config.ui.strings`:
|
|
730
|
+
|
|
731
|
+
```tsx
|
|
732
|
+
strings: {
|
|
733
|
+
recordButton: "Record",
|
|
734
|
+
stopRecordButton: "Stop",
|
|
735
|
+
recordingText: "Recording…",
|
|
736
|
+
recordingDeniedText: "Screen recording permission was denied.",
|
|
737
|
+
videoPreviewLabel: "Video preview",
|
|
738
|
+
removeVideoButton: "Remove video",
|
|
739
|
+
}
|
|
740
|
+
```
|
|
741
|
+
|
|
655
742
|
### Known limitations
|
|
656
743
|
|
|
657
744
|
The default screenshot engine (`html-to-image`) has known issues with:
|
|
@@ -761,6 +848,10 @@ import type {
|
|
|
761
848
|
TransportConfig,
|
|
762
849
|
TriggerStyle,
|
|
763
850
|
WidgetPosition,
|
|
851
|
+
RecordingConfig,
|
|
852
|
+
VideoAsset,
|
|
853
|
+
VideoIntent,
|
|
854
|
+
VideoMime,
|
|
764
855
|
SecurityConfig,
|
|
765
856
|
SecurityFinding,
|
|
766
857
|
SecuritySnapshot,
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
function E(){return typeof window<"u"&&typeof document<"u"}function he(e){let t=globalThis.CSS;return typeof t?.escape=="function"?t.escape(e):e.replace(/[^a-zA-Z0-9_-]/g,n=>{let r=n.codePointAt(0);return r===void 0?"":`\\${r.toString(16)} `})}function te(e){return {x:e.x,y:e.y,width:e.width,height:e.height}}function $e(e){return {x:e.x+window.scrollX,y:e.y+window.scrollY,width:e.width,height:e.height}}function je(e){return e.replace(/\s+/g," ").trim()}function Ve(e,t=140){let n=e.textContent;if(!n)return;let r=je(n);if(r)return r.length<=t?r:`${r.slice(0,t-1)}\u2026`}function Xe(e){let t=1;for(let n=e.previousElementSibling;n;n=n.previousElementSibling)n.tagName===e.tagName&&(t+=1);return t}var Ee=["data-testid","data-test-id","data-test","data-qa","data-cy"],we="data-blocfeed-component";function Ze(e){let t=e.closest(`[${we}]`);if(!t)return;let r=t.getAttribute(we)?.trim();return r||void 0}function We(e){for(let t of Ee){let n=e.closest(`[${t}]`);if(!n)continue;let o=n.getAttribute(t)?.trim();if(o)return o}}function be(e){try{let t=e,n=Object.getOwnPropertyNames(t);for(let r of n)if(r.startsWith("__reactFiber$")||r.startsWith("__reactInternalInstance$")){let o=t[r];if(o&&typeof o=="object")return o}}catch{}return null}function B(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 q(e){if(e){for(let t of e)if(typeof t.name=="string"&&t.name&&!B(t.name))return t.name}}function A(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 r=t.render;if(typeof r=="function"){let i=r;if(typeof i.displayName=="string"&&i.displayName)return i.displayName;if(typeof i.name=="string"&&i.name)return i.name}let o=t.type;return A(o)}}}function Ke(e){let t=be(e);if(!t)return;let n=q(t._debugInfo);if(n)return n;let r=t._debugOwner!==void 0;if(r){let s=t._debugOwner;for(let c=0;s&&c<50;c+=1){let h=q(s._debugInfo);if(h)return h;let p=A(s.type)??A(s.elementType);if(p&&!B(p))return p;s=s._debugOwner;}}if(t._owner!==void 0&&t._owner!==t._debugOwner){let s=t._owner;for(let c=0;s&&c<50;c+=1){let h=q(s._debugInfo);if(h)return h;let p=A(s.type)??A(s.elementType);if(p&&!B(p))return p;s=s._owner;}}let i=t,l=r?80:25;for(let s=0;i&&s<l;s+=1){let c=q(i._debugInfo);if(c)return c;let h=A(i.type)??A(i.elementType);if(h&&!B(h))return h;i=i.return;}let a=e.parentElement;for(let s=0;a&&s<15;s+=1){let c=be(a);if(c){let h=q(c._debugInfo);if(h)return h;let p=A(c.type)??A(c.elementType);if(p&&!B(p))return p;if(c._debugOwner){let m=A(c._debugOwner.type)??A(c._debugOwner.elementType);if(m&&!B(m))return m}if(c._owner&&c._owner!==c._debugOwner){let m=A(c._owner.type)??A(c._owner.elementType);if(m&&!B(m))return m}}a=a.parentElement;}}function Qe(e){let t=e.tagName.toLowerCase(),n=e.getAttribute("id");if(n)return `#${he(n)}`;for(let r of Ee){let o=e.getAttribute(r);if(o)return `${t}[${r}="${he(o)}"]`}return `${t}:nth-of-type(${Xe(e)})`}function Ye(e,t=10){let n=[],r=e;for(;r&&n.length<t;){let o=Qe(r);if(n.unshift(o),o.startsWith("#"))break;r=r.parentElement;}return n.join(" > ")}function ye(e,t){if(!t||t.length===0)return false;for(let n of t)if(e.closest(n))return true;return false}function ne(e,t){if(!e||ye(e,t.ignoreSelectors))return null;let n=e;for(;n;){if(ye(n,t.ignoreSelectors))return null;if(t.isSelectable?.(n)??Ge(n))return n;n=n.parentElement;}return null}function Ge(e){let t=e.tagName;return !(t==="HTML"||t==="BODY")}function Se(e){let t=e.getBoundingClientRect(),n={selector:Ye(e),tagName:e.tagName.toLowerCase(),rect:te(t),pageRect:$e(t)},r=e.getAttribute("id");r&&(n.id=r);let o=e.className;typeof o=="string"&&o.trim()&&(n.className=o);let i=Ve(e);i&&(n.textSnippet=i);let l=Ze(e)??Ke(e);l&&(n.componentName=l);let a=We(e);return a&&(n.testId=a),n}var re=null;async function ke(){return re||(re=import('html-to-image')),re}async function Je(e){return await new Promise((t,n)=>{let r=new Image;r.onload=()=>t({width:r.naturalWidth,height:r.naturalHeight}),r.onerror=()=>n(new Error("Failed to load generated screenshot")),r.src=e;})}async function _e(e,t){let{width:n,height:r}=await Je(e);return {dataUrl:e,mime:t,width:n,height:r}}function O(e){if(e?.aborted)throw new Error("Aborted")}function ve(){return {async captureElement(e,t){if(!E())throw new Error("captureElement can only run in the browser");O(t.signal);let n=await ke();O(t.signal);let r=t.mime==="image/jpeg"?await n.toJpeg(e,{quality:t.quality??.92,pixelRatio:t.pixelRatio}):await n.toPng(e,{pixelRatio:t.pixelRatio});return O(t.signal),await _e(r,t.mime)},async captureFullPage(e){if(!E())throw new Error("captureFullPage can only run in the browser");O(e.signal);let t=document.documentElement,n=Math.max(t.scrollWidth,t.clientWidth),r=Math.max(t.scrollHeight,t.clientHeight),o=Math.min(1,e.maxDimension/Math.max(n,r)),i=Math.max(1,Math.round(n*o)),l=Math.max(1,Math.round(r*o)),a=await ke();O(e.signal);let s=e.mime==="image/jpeg"?await a.toJpeg(t,{width:i,height:l,quality:e.quality??.92,pixelRatio:e.pixelRatio}):await a.toPng(t,{width:i,height:l,pixelRatio:e.pixelRatio});return O(e.signal),await _e(s,e.mime)}}}function et(e){if(e instanceof Error)return e.message;if(typeof e=="string")return e;try{return JSON.stringify(e)}catch{return "Unknown error"}}function w(e,t,n){let r={kind:e,message:et(t)};return n&&(r.detail=n),r}var tt=12e3,nt=2048,rt=.92;function Re(){return Date.now()}function ot(e){return new Promise((t,n)=>{let r=()=>n(new Error("Aborted"));if(e.aborted){r();return}e.addEventListener("abort",r,{once:true});})}async function Ae(e,t,n){let r=new Promise((i,l)=>{let a=setTimeout(()=>l(new Error("Timeout")),t);typeof a.unref=="function"&&a.unref();}),o=[e,r];return n&&o.push(ot(n)),await Promise.race(o)}function it(e){if(!E())return 1;let t=window.devicePixelRatio||1,n=e?.pixelRatio??Math.min(t,2);return Math.max(.1,n)}function at(e){return !!(e?.element||e?.fullPage)}function Te(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 Pe(e){let{selectionElement:t,capture:n,signal:r}=e;if(!E()||!at(n))return;let o=Re(),i=[],l=n?.timeoutMs??tt,a=n?.maxDimension??nt,s=n?.mime??"image/png",c=n?.quality??rt,h=n?.adapter??ve(),p={},m=it(n);if(n?.element&&t)try{let f=t.getBoundingClientRect(),g=Math.min(1,a/Math.max(f.width,f.height)),k=Math.min(m,m*g),v=await Ae(Promise.resolve(h.captureElement(t,{...Te({mime:s,quality:c,pixelRatio:k,maxDimension:a,includeQuality:s==="image/jpeg",...r?{signal:r}:{}})})),l,r);p.element=v;}catch(f){if(r?.aborted)throw f;i.push(w("capture_failed",f,{target:"element"}));}if(n?.fullPage)try{let f=await Ae(Promise.resolve(h.captureFullPage(Te({mime:s,quality:c,pixelRatio:m,maxDimension:a,includeQuality:s==="image/jpeg",...r?{signal:r}:{}}))),l,r);p.fullPage=f;}catch(f){if(r?.aborted)throw f;i.push(w("capture_failed",f,{target:"fullPage"}));}let b=Re(),u={startedAt:o,finishedAt:b,durationMs:Math.max(0,b-o)};return i.length>0&&(u.errors=i),{...p,diagnostics:u}}function st(){try{return Intl.DateTimeFormat().resolvedOptions().timeZone}catch{return}}function ct(){return E()?{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:st()}:{}}function lt(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 xe(e){let{config:t,context:n,user:r}=e;if(t?.enabled===false)return {};let o={...ct(),...lt(r)},i=t?.enrich;if(!i)return o;try{let l=await i(n);return {...o,...l}}catch(l){let a=w("unknown",l);return {...o,blocfeedMetadataError:a.message}}}var oe="blocfeed-queue",ut=50;function ie(){if(!E())return [];try{let e=localStorage.getItem(oe);if(!e)return [];let t=JSON.parse(e);return Array.isArray(t)?t:[]}catch{return []}}function ae(e){if(E())try{e.length===0?localStorage.removeItem(oe):localStorage.setItem(oe,JSON.stringify(e));}catch{}}function dt(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 se(e){let t=ie(),n=dt(e);for(n.metadata={...n.metadata,_queued:true},t.push({payload:n,timestamp:Date.now()});t.length>ut;)t.shift();ae(t);}function Fe(){let e=ie();return e.length===0?[]:(ae([]),e.map(t=>t.payload))}function Jt(){ae([]);}function en(){return ie().length}var ft=3e4,mt=25e5,Ce="video/webm",pt=250,gt=1e3,ht=["video/webm;codecs=vp9","video/webm;codecs=vp8","video/webm"];function Me(){if(typeof MediaRecorder>"u")return null;for(let e of ht)if(MediaRecorder.isTypeSupported(e))return e;return null}function wt(){return !E()||typeof navigator?.mediaDevices?.getDisplayMedia!="function"?false:Me()!==null}async function ce(e){let{config:t,signal:n}=e;if(!E()||typeof navigator?.mediaDevices?.getDisplayMedia!="function")throw w("recording_failed",new Error("Screen recording is not supported in this browser"));let r=Me();if(!r)throw w("recording_failed",new Error("No supported video codec found (WebM required)"));if(n?.aborted)throw w("aborted",new Error("Recording aborted before start"));let o;try{o=await navigator.mediaDevices.getDisplayMedia({video:{displaySurface:"browser"},audio:!1});}catch(g){let k=g instanceof DOMException&&g.name==="NotAllowedError"?"Screen share permission denied":"Failed to start screen share";throw w("recording_failed",new Error(k))}if(n?.aborted)throw o.getTracks().forEach(g=>g.stop()),w("aborted",new Error("Recording aborted after permission"));let i=t?.maxDurationMs??ft,l=t?.videoBitsPerSecond??mt,a=new MediaRecorder(o,{mimeType:r,videoBitsPerSecond:l}),s=[],c=[],h=0,p=null,m=null,b=false,u=()=>{p!==null&&(clearInterval(p),p=null),m!==null&&(clearTimeout(m),m=null),o.getTracks().forEach(g=>g.stop());},f=new Promise((g,k)=>{a.ondataavailable=y=>{y.data.size>0&&s.push(y.data);},a.onstop=()=>{if(b)return;b=true,u();let y=Date.now()-h,P=new Blob(s,{type:Ce}),M=URL.createObjectURL(P);g({mime:Ce,blobUrl:M,blob:P,durationMs:y,sizeBytes:P.size});},a.onerror=()=>{b||(b=true,u(),k(w("recording_failed",new Error("MediaRecorder error"))));};let v=o.getVideoTracks()[0];if(v&&v.addEventListener("ended",()=>{a.state!=="inactive"&&a.stop();}),n){let y=()=>{b||(b=true,a.state!=="inactive"&&a.stop(),u(),k(w("aborted",new Error("Recording aborted"))));};n.addEventListener("abort",y,{once:true});}});return a.start(gt),h=Date.now(),p=setInterval(()=>{let g=Date.now()-h;for(let k of c)k(g);},pt),m=setTimeout(()=>{!b&&a.state!=="inactive"&&a.stop();},i),{result:f,stop(){!b&&a.state!=="inactive"&&a.stop();},onTick(g){c.push(g);},abort(){b||(b=true,a.state!=="inactive"&&a.stop(),u());}}}function le(e){let t=null,n=null,r=(...o)=>{n=o,t===null&&(t=requestAnimationFrame(()=>{if(t=null,!n)return;let i=n;n=null,e(...i);}));};return r.cancel=()=>{t!==null&&cancelAnimationFrame(t),t=null,n=null;},r}function K(e){return e instanceof Element?!!e.closest("[data-blocfeed-ui]"):false}function Q(e){e.stopPropagation(),e.stopImmediatePropagation?.();}function Le(e,t){if(!E())throw new Error("BlocFeed picker can only run in a browser environment.");let n=e.ignoreSelectors,r=e.isSelectable,o={};n&&n.length>0&&(o.ignoreSelectors=n),r&&(o.isSelectable=r);let i=null,l=null,a=(u,f=false)=>{if(!u){i=null,l=null,t.onHover(null);return}let g=te(u.getBoundingClientRect()),k=`${Math.round(g.x)}:${Math.round(g.y)}:${Math.round(g.width)}:${Math.round(g.height)}`;!f&&u===i&&k===l||(i=u,l=k,t.onHover({element:u,rect:g}));},s=le(u=>{if(K(u.target))return;let f=document.elementFromPoint(u.clientX,u.clientY),g=ne(f,o);a(g);}),c=le(()=>{i&&a(i,true);}),h=u=>{K(u.target)||(Q(u),u.pointerType==="mouse"&&u.preventDefault());},p=u=>{K(u.target)||(Q(u),u.pointerType==="mouse"&&u.preventDefault());},m=u=>{if(K(u.target))return;Q(u),u.preventDefault();let f=document.elementFromPoint(u.clientX,u.clientY),g=ne(f,o);g&&t.onSelect({element:g,descriptor:Se(g)});},b=u=>{u.key==="Escape"&&(Q(u),u.preventDefault(),t.onCancel());};return window.addEventListener("pointermove",s,{capture:true,passive:true}),window.addEventListener("pointerdown",h,{capture:true}),window.addEventListener("pointerup",p,{capture:true}),window.addEventListener("click",m,{capture:true}),window.addEventListener("keydown",b,{capture:true}),window.addEventListener("scroll",c,{capture:true,passive:true}),window.addEventListener("resize",c,{passive:true}),{stop(){window.removeEventListener("pointermove",s,{capture:true}),window.removeEventListener("pointerdown",h,{capture:true}),window.removeEventListener("pointerup",p,{capture:true}),window.removeEventListener("click",m,{capture:true}),window.removeEventListener("keydown",b,{capture:true}),window.removeEventListener("scroll",c,{capture:true}),window.removeEventListener("resize",c),s.cancel(),c.cancel(),t.onHover(null);}}}var bt=12e3,yt=2,Et=500,St=2e3,ue="https://blocfeed.com/api/feedback",De=0;function Be(e,t){return new Promise((n,r)=>{if(t?.aborted){r(new Error("Aborted"));return}let o=setTimeout(n,e),i=()=>{clearTimeout(o),r(new Error("Aborted"));};t?.addEventListener("abort",i,{once:true});})}function kt(e){return e>=500&&e<=599}function _t(e){let[t,n]=e.split(",",2);if(!t||!n)throw new Error("Invalid data URL");let o=/data:(.*?);base64/.exec(t)?.[1]||"application/octet-stream",i=atob(n),l=new Uint8Array(i.length);for(let a=0;a<i.length;a+=1)l[a]=i.charCodeAt(a);return new Blob([l],{type:o})}function vt(e){let t={},n={},r={...e};if(r.screenshots){let o={},i={...r.screenshots};i.element&&(t.element=i.element.dataUrl,o.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,o.fullPage={mime:i.fullPage.mime,width:i.fullPage.width,height:i.fullPage.height},i.fullPage={...i.fullPage,dataUrl:""}),r.screenshots=i,(o.element||o.fullPage)&&(r.screenshot_intent=o);}return r.video&&(n.blob=r.video.blob,r.video_intent={mime:r.video.mime,durationMs:r.video.durationMs,sizeBytes:r.video.sizeBytes},delete r.video),{lean:r,extracted:t,extractedVideo:n}}async function Ie(e,t,n){let r=_t(t);await fetch(e,{method:"PUT",body:r,headers:{"content-type":r.type},...n?{signal:n}:{}});}async function Rt(e){let{feedbackId:t,extracted:n,screenshots:r,signal:o}=e,i={};n.element&&r?.element&&(i.element={dataUrl:n.element,mime:r.element.mime,width:r.element.width,height:r.element.height}),n.fullPage&&r?.fullPage&&(i.fullPage={dataUrl:n.fullPage,mime:r.fullPage.mime,width:r.fullPage.width,height:r.fullPage.height}),Object.keys(i).length!==0&&await fetch(`${ue}/${t}/screenshots`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(i),...o?{signal:o}:{}});}async function At(e){await fetch(`${ue}/${e.feedbackId}/video`,{method:"POST",body:e.blob,headers:{"content-type":e.blob.type},...e.signal?{signal:e.signal}:{}});}async function Ne(e){let{signal:t,transport:n}=e;if(Date.now()-De<St)return {ok:false,error:w("configuration",new Error("Please wait before submitting again"))};let o=n?.timeoutMs??bt,i=n?.maxAttempts??yt,l=n?.backoffMs??Et,a=!!(e.payload.screenshots?.element?.dataUrl||e.payload.screenshots?.fullPage?.dataUrl),s=!!e.payload.video?.blob,c=a||s,{lean:h,extracted:p,extractedVideo:m}=c?vt(e.payload):{lean:e.payload,extracted:{},extractedVideo:{}},b={...p,...e.screenshotDataUrls};for(let u=1;u<=i;u+=1){let f=new AbortController,g=setTimeout(()=>f.abort(),o),k=()=>f.abort();t&&t.addEventListener("abort",k,{once:true});try{let v=await fetch(ue,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(h),signal:f.signal});if(v.ok){De=Date.now();let y;try{y=await v.json();}catch{}if((b.element||b.fullPage)&&y){let d=y.upload_urls;if(d){let S=[];b.element&&d.element&&S.push(Ie(d.element,b.element,t)),b.fullPage&&d.fullPage&&S.push(Ie(d.fullPage,b.fullPage,t));try{await Promise.all(S);}catch{}}else if(y.feedback_id)try{await Rt({feedbackId:y.feedback_id,extracted:b,screenshots:e.payload.screenshots,...t?{signal:t}:{}});}catch{}}if(m.blob&&y){let d=y.upload_urls;if(d?.video)try{await fetch(d.video,{method:"PUT",body:m.blob,headers:{"content-type":m.blob.type},...t?{signal:t}:{}});}catch{}else if(y.feedback_id)try{await At({feedbackId:y.feedback_id,blob:m.blob,...t?{signal:t}:{}});}catch{}}let M={ok:!0,status:v.status};return y&&(M.apiResponse=y),M}if(u<i&&kt(v.status)){let y=.85+Math.random()*.3,P=Math.round(l*2**(u-1)*y);await Be(P,t);continue}return {ok:!1,status:v.status,error:w("api_failed",new Error(`HTTP ${v.status}`))}}catch(v){if(f.signal.aborted||t?.aborted)return {ok:false,error:w("aborted",v)};if(u<i){let y=.85+Math.random()*.3,P=Math.round(l*2**(u-1)*y);await Be(P,t);continue}return {ok:false,error:w("api_failed",v)}}finally{clearTimeout(g),t&&t.removeEventListener("abort",k);}}return {ok:false,error:w("api_failed",new Error("Failed"))}}async function de(e){let{signal:t,transport:n}=e,r={ok:false};try{let o={payload:e.payload,...t?{signal:t}:{},...n?{transport:n}:{}};r.api=await Ne(o),r.ok=!!r.api?.ok;}catch(o){r.api={ok:false,error:w("api_failed",o)},r.ok=false;}return {payload:e.payload,result:r}}var Tt=["[data-blocfeed-ui]","[data-blocfeed-ignore]"];function Pt(e){let t=[...Tt,...e?.ignoreSelectors??[]],n=Array.from(new Set(t));return {...e,ignoreSelectors:n}}function Oe(){return {phase:"idle"}}function xt(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 Rn(e){let t=e,n=Oe(),r=new Set,o=new Set,i=null,l=null,a=null,s=null,c=0,h=null,p=null,m=null,b=()=>{for(let d of r)d(n);},u=d=>{for(let S of o)S(d);},f=d=>{n=d,b();},g=()=>{c+=1,s?.abort(),s=null;},k=()=>{i?.stop(),i=null,u(null),a!==null&&E()&&(document.documentElement.style.cursor=a,a=null);},v=()=>{p&&(p.abort(),p=null),m&&(URL.revokeObjectURL(m.blobUrl),m=null);},y=()=>{g(),k(),v(),l=null,f(Oe());},P=()=>{if(!E())return;k(),l=null;let d=Pt(t.picker);a=document.documentElement.style.cursor,document.documentElement.style.cursor="crosshair",f({phase:"picking"}),i=Le(d,{onHover:u,onSelect:({element:S,descriptor:x})=>{l=S,k(),f({phase:"review",selection:x});},onCancel:()=>{y();}});},M=()=>{let d=Fe();if(d.length!==0)for(let S of d)de({payload:S,...t.transport?{transport:t.transport}:{}}).catch(()=>{se(S);});};if(E()){setTimeout(M,1e3);let d=()=>M();window.addEventListener("online",d),h=()=>window.removeEventListener("online",d);}return {getState:()=>n,subscribe(d){return r.add(d),()=>r.delete(d)},subscribeHover(d){return o.add(d),()=>o.delete(d)},start(){n.phase==="capturing"||n.phase==="submitting"||n.phase==="recording"||n.phase!=="picking"&&P();},stop(){y();},clearSelection(){n.phase==="capturing"||n.phase==="submitting"||n.phase==="recording"||P();},setConfig(d){t=d;},async submit(d,S){if(!E()){let R=w("configuration",new Error("BlocFeed submit can only run in the browser"));return f({phase:"error",lastError:R}),{ok:false}}let x=t.blocfeed_id?.trim?.()??"";if(!x){let L={phase:"error",lastError:w("configuration",new Error("Missing blocfeed_id. Create a project in BlocFeed and pass its blocfeed_id."))};return n.selection&&(L.selection=n.selection),f(L),{ok:false}}if(n.phase==="capturing"||n.phase==="submitting"||n.phase==="recording")return {ok:false};let F=c+1;c=F,s?.abort(),s=new AbortController;let T=s.signal,_=n.selection,U=S?.capture?{...t.capture,...S.capture}:t.capture,H=!!(U?.element||U?.fullPage),ge={phase:H?"capturing":"submitting"};_&&(ge.selection=_),f(ge);try{let R=H?await Pe({selectionElement:l,capture:U,signal:T}):void 0;if(T.aborted||c!==F)return {ok:!1};let L={phase:"submitting"};_&&(L.selection=_),R&&(L.capture=R),f(L);let N={};_&&(N.selection=_),R&&(N.capture=R);let qe=await xe({config:t.metadata,context:N,...t.user?{user:t.user}:{}}),D={version:1,createdAt:new Date().toISOString(),blocfeed_id:x,message:d,metadata:qe};S?.category&&(D.category=S.category),t.user&&(D.user=t.user),_&&(D.selection=_),R&&(D.screenshots=R),m&&(D.video=m);let{result:C}=await de({payload:D,signal:T,...t.transport?{transport:t.transport}:{}});if(T.aborted||c!==F)return C;if(C.ok){m&&(URL.revokeObjectURL(m.blobUrl),m=null);let ee={phase:"success",lastSubmit:C};return _&&(ee.selection=_),R&&(ee.capture=R),f(ee),C}xt(C)&&se(D);let ze=C.api?.error??w("unknown",new Error("Submission failed")),J={phase:"error",lastSubmit:C,lastError:ze};return _&&(J.selection=_),R&&(J.capture=R),f(J),C}catch(R){if(T.aborted||c!==F)return {ok:false};let N={phase:"error",lastError:T.aborted?w("aborted",R):w("unknown",R)};return _&&(N.selection=_),f(N),{ok:false}}finally{c===F&&(s=null);}},async startRecording(){if(n.phase!=="review"||!t.recording?.enabled)return;let d=n.selection,S={phase:"recording",recordingElapsedMs:0};d&&(S.selection=d),f(S);try{let x={config:t.recording};s&&(x.signal=s.signal);let F=await ce(x);p=F,F.onTick(U=>{if(n.phase==="recording"){let H={phase:"recording",recordingElapsedMs:U};d&&(H.selection=d),f(H);}});let T=await F.result;p=null,m=T;let _={phase:"review",video:T};d&&(_.selection=d),f(_);}catch(x){p=null;let T={phase:"review",lastError:x?.kind?x:w("recording_failed",x)};d&&(T.selection=d),f(T);}},stopRecording(){n.phase!=="recording"||!p||p.stop();},removeVideo(){m&&(URL.revokeObjectURL(m.blobUrl),m=null);let d=n.selection,S={phase:"review"};d&&(S.selection=d),f(S);},__unsafeGetSelectedElement(){return l},destroy(){y(),r.clear(),o.clear(),h?.(),h=null;}}}var z=[],$=[],Ue=20,He=15,Y=[],j={},V=null,X=null,Z=null,G=false,Ft=["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 Ct(e){if(e instanceof Error)return e.message;if(typeof e=="string")return e;try{return JSON.stringify(e)}catch{return String(e)}}function Mt(e,t){let n=t.map(Ct).join(" "),r=t.find(i=>i instanceof Error),o={level:e,message:n.slice(0,2e3),timestamp:Date.now()};for(r?.stack&&(o.stack=r.stack.slice(0,2e3)),z.push(o);z.length>Ue;)z.shift();}function fe(e){let t=e.url.toLowerCase();for(let n of Y)if(t.includes(n))return;for($.push(e);$.length>He;)$.shift();}function Pn(e={}){if(G||!E())return;G=true,Ue=e.consoleLimit??20,He=e.networkLimit??15;let t=e.ignoreUrls;if(t!==void 0?Y=t.map(n=>n.toLowerCase()):Y=[...Ft],e.console!==false){let n=e.consoleLevels??["error","warn"];for(let r of n)j[r]=console[r],console[r]=(...o)=>{Mt(r,o),j[r]?.apply(console,o);};}if(e.network!==false&&typeof window.fetch=="function"){V=window.fetch;let n=V;window.fetch=async function(o,i){let l=typeof o=="string"?o:o instanceof URL?o.toString():o.url,a=(i?.method??"GET").toUpperCase(),s=Date.now();try{let c=await n.call(window,o,i);return c.ok||fe({url:l.slice(0,500),method:a,status:c.status,statusText:c.statusText,timestamp:s,durationMs:Date.now()-s}),c}catch(c){throw fe({url:l.slice(0,500),method:a,status:0,statusText:c instanceof Error?c.message:"Network error",timestamp:s,durationMs:Date.now()-s}),c}};}if(e.network!==false&&typeof XMLHttpRequest<"u"){X=XMLHttpRequest.prototype.open,Z=XMLHttpRequest.prototype.send;let n=X,r=Z;XMLHttpRequest.prototype.open=function(o,i,...l){return this.__bf_method=o.toUpperCase(),this.__bf_url=String(i),n.apply(this,[o,i,...l])},XMLHttpRequest.prototype.send=function(...o){let i=this.__bf_method||"GET",l=this.__bf_url||"",a=Date.now();return this.addEventListener("loadend",function(){if(this.status>=400||this.status===0){let s={url:l.slice(0,500),method:i,status:this.status,timestamp:a,durationMs:Date.now()-a};this.statusText&&(s.statusText=this.statusText),fe(s);}}),r.apply(this,o)};}}function xn(){if(G){for(let[e,t]of Object.entries(j))console[e]=t;for(let e of Object.keys(j))delete j[e];V&&(window.fetch=V,V=null),X&&(XMLHttpRequest.prototype.open=X,X=null),Z&&(XMLHttpRequest.prototype.send=Z,Z=null),Y=[],G=false;}}function Fn(){return {consoleLogs:[...z],networkErrors:[...$]}}function Cn(){z=[],$=[];}var Lt=[{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,}/}],I=[],pe=0,me=false;function Dt(e){let t=e.slice(0,80);return t.length<=6?"***":t.slice(0,6)+"***"}function Bt(e,t,n,r){if(I.some(l=>l.rule===e&&l.source===t))return;let i={rule:e,source:t,hint:Dt(n),timestamp:Date.now()};r&&(i.location=r),I.push(i);}function W(e,t,n,r){for(let{name:o,pattern:i}of n){let l=i.exec(e);l&&Bt(o,t,l[0],r);}}function It(e){try{let t=window;if(t.__NEXT_DATA__){let n=JSON.stringify(t.__NEXT_DATA__);W(n,"hydration",e,"__NEXT_DATA__");}if(t.__NUXT__){let n=JSON.stringify(t.__NUXT__);W(n,"hydration",e,"__NUXT__");}}catch{}}function Nt(e){try{document.querySelectorAll("script:not([src])").forEach((n,r)=>{let o=n.textContent;o&&o.length>0&&W(o,"scripts",e,`inline-script[${r}]`);});}catch{}}function Ot(e){try{document.querySelectorAll("meta[content]").forEach(n=>{let r=n.getAttribute("content"),o=n.getAttribute("name")||n.getAttribute("property")||"";r&&r.length>10&&W(r,"meta",e,o?`meta[${o}]`:void 0);});}catch{}}function Ut(e){try{document.querySelectorAll("[data-api-key], [data-secret], [data-token], [data-password]").forEach(n=>{let r=n.attributes;for(let o=0;o<r.length;o++){let i=r[o];i.name.startsWith("data-")&&i.value.length>10&&W(i.value,"dom",e,`${n.tagName.toLowerCase()}[${i.name}]`);}});}catch{}}function Dn(e={}){if(me||!E()||(me=true,e.secretScan===false))return;let t=[...Lt,...e.customPatterns??[]],n=e.scanTargets??["hydration","scripts","meta","dom"];setTimeout(()=>{pe=Date.now(),n.includes("hydration")&&It(t),n.includes("scripts")&&Nt(t),n.includes("meta")&&Ot(t),n.includes("dom")&&Ut(t);let r=e.notify??"both";I.length>0&&(r==="console"||r==="both")&&console.warn(`[BlocFeed Security] Found ${I.length} potential secret(s) exposed in client code:`,I.map(o=>`${o.rule} (${o.source}${o.location?`: ${o.location}`:""})`));},100);}function Bn(){return {findings:[...I],scannedAt:pe}}function In(){I=[],pe=0,me=false;}
|
|
2
|
+
export{E as a,te as b,ve as c,Pe as d,xe as e,se as f,Fe as g,Jt as h,en as i,wt as j,ce as k,Rn as l,Pn as m,xn as n,Fn as o,Cn as p,Dn as q,Bn as r,In as s};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
'use strict';function E(){return typeof window<"u"&&typeof document<"u"}function he(e){let t=globalThis.CSS;return typeof t?.escape=="function"?t.escape(e):e.replace(/[^a-zA-Z0-9_-]/g,n=>{let r=n.codePointAt(0);return r===void 0?"":`\\${r.toString(16)} `})}function te(e){return {x:e.x,y:e.y,width:e.width,height:e.height}}function $e(e){return {x:e.x+window.scrollX,y:e.y+window.scrollY,width:e.width,height:e.height}}function je(e){return e.replace(/\s+/g," ").trim()}function Ve(e,t=140){let n=e.textContent;if(!n)return;let r=je(n);if(r)return r.length<=t?r:`${r.slice(0,t-1)}\u2026`}function Xe(e){let t=1;for(let n=e.previousElementSibling;n;n=n.previousElementSibling)n.tagName===e.tagName&&(t+=1);return t}var Ee=["data-testid","data-test-id","data-test","data-qa","data-cy"],we="data-blocfeed-component";function Ze(e){let t=e.closest(`[${we}]`);if(!t)return;let r=t.getAttribute(we)?.trim();return r||void 0}function We(e){for(let t of Ee){let n=e.closest(`[${t}]`);if(!n)continue;let o=n.getAttribute(t)?.trim();if(o)return o}}function be(e){try{let t=e,n=Object.getOwnPropertyNames(t);for(let r of n)if(r.startsWith("__reactFiber$")||r.startsWith("__reactInternalInstance$")){let o=t[r];if(o&&typeof o=="object")return o}}catch{}return null}function B(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 q(e){if(e){for(let t of e)if(typeof t.name=="string"&&t.name&&!B(t.name))return t.name}}function A(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 r=t.render;if(typeof r=="function"){let i=r;if(typeof i.displayName=="string"&&i.displayName)return i.displayName;if(typeof i.name=="string"&&i.name)return i.name}let o=t.type;return A(o)}}}function Ke(e){let t=be(e);if(!t)return;let n=q(t._debugInfo);if(n)return n;let r=t._debugOwner!==void 0;if(r){let s=t._debugOwner;for(let c=0;s&&c<50;c+=1){let h=q(s._debugInfo);if(h)return h;let p=A(s.type)??A(s.elementType);if(p&&!B(p))return p;s=s._debugOwner;}}if(t._owner!==void 0&&t._owner!==t._debugOwner){let s=t._owner;for(let c=0;s&&c<50;c+=1){let h=q(s._debugInfo);if(h)return h;let p=A(s.type)??A(s.elementType);if(p&&!B(p))return p;s=s._owner;}}let i=t,l=r?80:25;for(let s=0;i&&s<l;s+=1){let c=q(i._debugInfo);if(c)return c;let h=A(i.type)??A(i.elementType);if(h&&!B(h))return h;i=i.return;}let a=e.parentElement;for(let s=0;a&&s<15;s+=1){let c=be(a);if(c){let h=q(c._debugInfo);if(h)return h;let p=A(c.type)??A(c.elementType);if(p&&!B(p))return p;if(c._debugOwner){let m=A(c._debugOwner.type)??A(c._debugOwner.elementType);if(m&&!B(m))return m}if(c._owner&&c._owner!==c._debugOwner){let m=A(c._owner.type)??A(c._owner.elementType);if(m&&!B(m))return m}}a=a.parentElement;}}function Qe(e){let t=e.tagName.toLowerCase(),n=e.getAttribute("id");if(n)return `#${he(n)}`;for(let r of Ee){let o=e.getAttribute(r);if(o)return `${t}[${r}="${he(o)}"]`}return `${t}:nth-of-type(${Xe(e)})`}function Ye(e,t=10){let n=[],r=e;for(;r&&n.length<t;){let o=Qe(r);if(n.unshift(o),o.startsWith("#"))break;r=r.parentElement;}return n.join(" > ")}function ye(e,t){if(!t||t.length===0)return false;for(let n of t)if(e.closest(n))return true;return false}function ne(e,t){if(!e||ye(e,t.ignoreSelectors))return null;let n=e;for(;n;){if(ye(n,t.ignoreSelectors))return null;if(t.isSelectable?.(n)??Ge(n))return n;n=n.parentElement;}return null}function Ge(e){let t=e.tagName;return !(t==="HTML"||t==="BODY")}function Se(e){let t=e.getBoundingClientRect(),n={selector:Ye(e),tagName:e.tagName.toLowerCase(),rect:te(t),pageRect:$e(t)},r=e.getAttribute("id");r&&(n.id=r);let o=e.className;typeof o=="string"&&o.trim()&&(n.className=o);let i=Ve(e);i&&(n.textSnippet=i);let l=Ze(e)??Ke(e);l&&(n.componentName=l);let a=We(e);return a&&(n.testId=a),n}var re=null;async function ke(){return re||(re=import('html-to-image')),re}async function Je(e){return await new Promise((t,n)=>{let r=new Image;r.onload=()=>t({width:r.naturalWidth,height:r.naturalHeight}),r.onerror=()=>n(new Error("Failed to load generated screenshot")),r.src=e;})}async function _e(e,t){let{width:n,height:r}=await Je(e);return {dataUrl:e,mime:t,width:n,height:r}}function O(e){if(e?.aborted)throw new Error("Aborted")}function ve(){return {async captureElement(e,t){if(!E())throw new Error("captureElement can only run in the browser");O(t.signal);let n=await ke();O(t.signal);let r=t.mime==="image/jpeg"?await n.toJpeg(e,{quality:t.quality??.92,pixelRatio:t.pixelRatio}):await n.toPng(e,{pixelRatio:t.pixelRatio});return O(t.signal),await _e(r,t.mime)},async captureFullPage(e){if(!E())throw new Error("captureFullPage can only run in the browser");O(e.signal);let t=document.documentElement,n=Math.max(t.scrollWidth,t.clientWidth),r=Math.max(t.scrollHeight,t.clientHeight),o=Math.min(1,e.maxDimension/Math.max(n,r)),i=Math.max(1,Math.round(n*o)),l=Math.max(1,Math.round(r*o)),a=await ke();O(e.signal);let s=e.mime==="image/jpeg"?await a.toJpeg(t,{width:i,height:l,quality:e.quality??.92,pixelRatio:e.pixelRatio}):await a.toPng(t,{width:i,height:l,pixelRatio:e.pixelRatio});return O(e.signal),await _e(s,e.mime)}}}function et(e){if(e instanceof Error)return e.message;if(typeof e=="string")return e;try{return JSON.stringify(e)}catch{return "Unknown error"}}function w(e,t,n){let r={kind:e,message:et(t)};return n&&(r.detail=n),r}var tt=12e3,nt=2048,rt=.92;function Re(){return Date.now()}function ot(e){return new Promise((t,n)=>{let r=()=>n(new Error("Aborted"));if(e.aborted){r();return}e.addEventListener("abort",r,{once:true});})}async function Ae(e,t,n){let r=new Promise((i,l)=>{let a=setTimeout(()=>l(new Error("Timeout")),t);typeof a.unref=="function"&&a.unref();}),o=[e,r];return n&&o.push(ot(n)),await Promise.race(o)}function it(e){if(!E())return 1;let t=window.devicePixelRatio||1,n=e?.pixelRatio??Math.min(t,2);return Math.max(.1,n)}function at(e){return !!(e?.element||e?.fullPage)}function Te(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 Pe(e){let{selectionElement:t,capture:n,signal:r}=e;if(!E()||!at(n))return;let o=Re(),i=[],l=n?.timeoutMs??tt,a=n?.maxDimension??nt,s=n?.mime??"image/png",c=n?.quality??rt,h=n?.adapter??ve(),p={},m=it(n);if(n?.element&&t)try{let f=t.getBoundingClientRect(),g=Math.min(1,a/Math.max(f.width,f.height)),k=Math.min(m,m*g),v=await Ae(Promise.resolve(h.captureElement(t,{...Te({mime:s,quality:c,pixelRatio:k,maxDimension:a,includeQuality:s==="image/jpeg",...r?{signal:r}:{}})})),l,r);p.element=v;}catch(f){if(r?.aborted)throw f;i.push(w("capture_failed",f,{target:"element"}));}if(n?.fullPage)try{let f=await Ae(Promise.resolve(h.captureFullPage(Te({mime:s,quality:c,pixelRatio:m,maxDimension:a,includeQuality:s==="image/jpeg",...r?{signal:r}:{}}))),l,r);p.fullPage=f;}catch(f){if(r?.aborted)throw f;i.push(w("capture_failed",f,{target:"fullPage"}));}let b=Re(),u={startedAt:o,finishedAt:b,durationMs:Math.max(0,b-o)};return i.length>0&&(u.errors=i),{...p,diagnostics:u}}function st(){try{return Intl.DateTimeFormat().resolvedOptions().timeZone}catch{return}}function ct(){return E()?{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:st()}:{}}function lt(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 xe(e){let{config:t,context:n,user:r}=e;if(t?.enabled===false)return {};let o={...ct(),...lt(r)},i=t?.enrich;if(!i)return o;try{let l=await i(n);return {...o,...l}}catch(l){let a=w("unknown",l);return {...o,blocfeedMetadataError:a.message}}}var oe="blocfeed-queue",ut=50;function ie(){if(!E())return [];try{let e=localStorage.getItem(oe);if(!e)return [];let t=JSON.parse(e);return Array.isArray(t)?t:[]}catch{return []}}function ae(e){if(E())try{e.length===0?localStorage.removeItem(oe):localStorage.setItem(oe,JSON.stringify(e));}catch{}}function dt(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 se(e){let t=ie(),n=dt(e);for(n.metadata={...n.metadata,_queued:true},t.push({payload:n,timestamp:Date.now()});t.length>ut;)t.shift();ae(t);}function Fe(){let e=ie();return e.length===0?[]:(ae([]),e.map(t=>t.payload))}function Jt(){ae([]);}function en(){return ie().length}var ft=3e4,mt=25e5,Ce="video/webm",pt=250,gt=1e3,ht=["video/webm;codecs=vp9","video/webm;codecs=vp8","video/webm"];function Me(){if(typeof MediaRecorder>"u")return null;for(let e of ht)if(MediaRecorder.isTypeSupported(e))return e;return null}function wt(){return !E()||typeof navigator?.mediaDevices?.getDisplayMedia!="function"?false:Me()!==null}async function ce(e){let{config:t,signal:n}=e;if(!E()||typeof navigator?.mediaDevices?.getDisplayMedia!="function")throw w("recording_failed",new Error("Screen recording is not supported in this browser"));let r=Me();if(!r)throw w("recording_failed",new Error("No supported video codec found (WebM required)"));if(n?.aborted)throw w("aborted",new Error("Recording aborted before start"));let o;try{o=await navigator.mediaDevices.getDisplayMedia({video:{displaySurface:"browser"},audio:!1});}catch(g){let k=g instanceof DOMException&&g.name==="NotAllowedError"?"Screen share permission denied":"Failed to start screen share";throw w("recording_failed",new Error(k))}if(n?.aborted)throw o.getTracks().forEach(g=>g.stop()),w("aborted",new Error("Recording aborted after permission"));let i=t?.maxDurationMs??ft,l=t?.videoBitsPerSecond??mt,a=new MediaRecorder(o,{mimeType:r,videoBitsPerSecond:l}),s=[],c=[],h=0,p=null,m=null,b=false,u=()=>{p!==null&&(clearInterval(p),p=null),m!==null&&(clearTimeout(m),m=null),o.getTracks().forEach(g=>g.stop());},f=new Promise((g,k)=>{a.ondataavailable=y=>{y.data.size>0&&s.push(y.data);},a.onstop=()=>{if(b)return;b=true,u();let y=Date.now()-h,P=new Blob(s,{type:Ce}),M=URL.createObjectURL(P);g({mime:Ce,blobUrl:M,blob:P,durationMs:y,sizeBytes:P.size});},a.onerror=()=>{b||(b=true,u(),k(w("recording_failed",new Error("MediaRecorder error"))));};let v=o.getVideoTracks()[0];if(v&&v.addEventListener("ended",()=>{a.state!=="inactive"&&a.stop();}),n){let y=()=>{b||(b=true,a.state!=="inactive"&&a.stop(),u(),k(w("aborted",new Error("Recording aborted"))));};n.addEventListener("abort",y,{once:true});}});return a.start(gt),h=Date.now(),p=setInterval(()=>{let g=Date.now()-h;for(let k of c)k(g);},pt),m=setTimeout(()=>{!b&&a.state!=="inactive"&&a.stop();},i),{result:f,stop(){!b&&a.state!=="inactive"&&a.stop();},onTick(g){c.push(g);},abort(){b||(b=true,a.state!=="inactive"&&a.stop(),u());}}}function le(e){let t=null,n=null,r=(...o)=>{n=o,t===null&&(t=requestAnimationFrame(()=>{if(t=null,!n)return;let i=n;n=null,e(...i);}));};return r.cancel=()=>{t!==null&&cancelAnimationFrame(t),t=null,n=null;},r}function K(e){return e instanceof Element?!!e.closest("[data-blocfeed-ui]"):false}function Q(e){e.stopPropagation(),e.stopImmediatePropagation?.();}function Le(e,t){if(!E())throw new Error("BlocFeed picker can only run in a browser environment.");let n=e.ignoreSelectors,r=e.isSelectable,o={};n&&n.length>0&&(o.ignoreSelectors=n),r&&(o.isSelectable=r);let i=null,l=null,a=(u,f=false)=>{if(!u){i=null,l=null,t.onHover(null);return}let g=te(u.getBoundingClientRect()),k=`${Math.round(g.x)}:${Math.round(g.y)}:${Math.round(g.width)}:${Math.round(g.height)}`;!f&&u===i&&k===l||(i=u,l=k,t.onHover({element:u,rect:g}));},s=le(u=>{if(K(u.target))return;let f=document.elementFromPoint(u.clientX,u.clientY),g=ne(f,o);a(g);}),c=le(()=>{i&&a(i,true);}),h=u=>{K(u.target)||(Q(u),u.pointerType==="mouse"&&u.preventDefault());},p=u=>{K(u.target)||(Q(u),u.pointerType==="mouse"&&u.preventDefault());},m=u=>{if(K(u.target))return;Q(u),u.preventDefault();let f=document.elementFromPoint(u.clientX,u.clientY),g=ne(f,o);g&&t.onSelect({element:g,descriptor:Se(g)});},b=u=>{u.key==="Escape"&&(Q(u),u.preventDefault(),t.onCancel());};return window.addEventListener("pointermove",s,{capture:true,passive:true}),window.addEventListener("pointerdown",h,{capture:true}),window.addEventListener("pointerup",p,{capture:true}),window.addEventListener("click",m,{capture:true}),window.addEventListener("keydown",b,{capture:true}),window.addEventListener("scroll",c,{capture:true,passive:true}),window.addEventListener("resize",c,{passive:true}),{stop(){window.removeEventListener("pointermove",s,{capture:true}),window.removeEventListener("pointerdown",h,{capture:true}),window.removeEventListener("pointerup",p,{capture:true}),window.removeEventListener("click",m,{capture:true}),window.removeEventListener("keydown",b,{capture:true}),window.removeEventListener("scroll",c,{capture:true}),window.removeEventListener("resize",c),s.cancel(),c.cancel(),t.onHover(null);}}}var bt=12e3,yt=2,Et=500,St=2e3,ue="https://blocfeed.com/api/feedback",De=0;function Be(e,t){return new Promise((n,r)=>{if(t?.aborted){r(new Error("Aborted"));return}let o=setTimeout(n,e),i=()=>{clearTimeout(o),r(new Error("Aborted"));};t?.addEventListener("abort",i,{once:true});})}function kt(e){return e>=500&&e<=599}function _t(e){let[t,n]=e.split(",",2);if(!t||!n)throw new Error("Invalid data URL");let o=/data:(.*?);base64/.exec(t)?.[1]||"application/octet-stream",i=atob(n),l=new Uint8Array(i.length);for(let a=0;a<i.length;a+=1)l[a]=i.charCodeAt(a);return new Blob([l],{type:o})}function vt(e){let t={},n={},r={...e};if(r.screenshots){let o={},i={...r.screenshots};i.element&&(t.element=i.element.dataUrl,o.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,o.fullPage={mime:i.fullPage.mime,width:i.fullPage.width,height:i.fullPage.height},i.fullPage={...i.fullPage,dataUrl:""}),r.screenshots=i,(o.element||o.fullPage)&&(r.screenshot_intent=o);}return r.video&&(n.blob=r.video.blob,r.video_intent={mime:r.video.mime,durationMs:r.video.durationMs,sizeBytes:r.video.sizeBytes},delete r.video),{lean:r,extracted:t,extractedVideo:n}}async function Ie(e,t,n){let r=_t(t);await fetch(e,{method:"PUT",body:r,headers:{"content-type":r.type},...n?{signal:n}:{}});}async function Rt(e){let{feedbackId:t,extracted:n,screenshots:r,signal:o}=e,i={};n.element&&r?.element&&(i.element={dataUrl:n.element,mime:r.element.mime,width:r.element.width,height:r.element.height}),n.fullPage&&r?.fullPage&&(i.fullPage={dataUrl:n.fullPage,mime:r.fullPage.mime,width:r.fullPage.width,height:r.fullPage.height}),Object.keys(i).length!==0&&await fetch(`${ue}/${t}/screenshots`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(i),...o?{signal:o}:{}});}async function At(e){await fetch(`${ue}/${e.feedbackId}/video`,{method:"POST",body:e.blob,headers:{"content-type":e.blob.type},...e.signal?{signal:e.signal}:{}});}async function Ne(e){let{signal:t,transport:n}=e;if(Date.now()-De<St)return {ok:false,error:w("configuration",new Error("Please wait before submitting again"))};let o=n?.timeoutMs??bt,i=n?.maxAttempts??yt,l=n?.backoffMs??Et,a=!!(e.payload.screenshots?.element?.dataUrl||e.payload.screenshots?.fullPage?.dataUrl),s=!!e.payload.video?.blob,c=a||s,{lean:h,extracted:p,extractedVideo:m}=c?vt(e.payload):{lean:e.payload,extracted:{},extractedVideo:{}},b={...p,...e.screenshotDataUrls};for(let u=1;u<=i;u+=1){let f=new AbortController,g=setTimeout(()=>f.abort(),o),k=()=>f.abort();t&&t.addEventListener("abort",k,{once:true});try{let v=await fetch(ue,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(h),signal:f.signal});if(v.ok){De=Date.now();let y;try{y=await v.json();}catch{}if((b.element||b.fullPage)&&y){let d=y.upload_urls;if(d){let S=[];b.element&&d.element&&S.push(Ie(d.element,b.element,t)),b.fullPage&&d.fullPage&&S.push(Ie(d.fullPage,b.fullPage,t));try{await Promise.all(S);}catch{}}else if(y.feedback_id)try{await Rt({feedbackId:y.feedback_id,extracted:b,screenshots:e.payload.screenshots,...t?{signal:t}:{}});}catch{}}if(m.blob&&y){let d=y.upload_urls;if(d?.video)try{await fetch(d.video,{method:"PUT",body:m.blob,headers:{"content-type":m.blob.type},...t?{signal:t}:{}});}catch{}else if(y.feedback_id)try{await At({feedbackId:y.feedback_id,blob:m.blob,...t?{signal:t}:{}});}catch{}}let M={ok:!0,status:v.status};return y&&(M.apiResponse=y),M}if(u<i&&kt(v.status)){let y=.85+Math.random()*.3,P=Math.round(l*2**(u-1)*y);await Be(P,t);continue}return {ok:!1,status:v.status,error:w("api_failed",new Error(`HTTP ${v.status}`))}}catch(v){if(f.signal.aborted||t?.aborted)return {ok:false,error:w("aborted",v)};if(u<i){let y=.85+Math.random()*.3,P=Math.round(l*2**(u-1)*y);await Be(P,t);continue}return {ok:false,error:w("api_failed",v)}}finally{clearTimeout(g),t&&t.removeEventListener("abort",k);}}return {ok:false,error:w("api_failed",new Error("Failed"))}}async function de(e){let{signal:t,transport:n}=e,r={ok:false};try{let o={payload:e.payload,...t?{signal:t}:{},...n?{transport:n}:{}};r.api=await Ne(o),r.ok=!!r.api?.ok;}catch(o){r.api={ok:false,error:w("api_failed",o)},r.ok=false;}return {payload:e.payload,result:r}}var Tt=["[data-blocfeed-ui]","[data-blocfeed-ignore]"];function Pt(e){let t=[...Tt,...e?.ignoreSelectors??[]],n=Array.from(new Set(t));return {...e,ignoreSelectors:n}}function Oe(){return {phase:"idle"}}function xt(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 Rn(e){let t=e,n=Oe(),r=new Set,o=new Set,i=null,l=null,a=null,s=null,c=0,h=null,p=null,m=null,b=()=>{for(let d of r)d(n);},u=d=>{for(let S of o)S(d);},f=d=>{n=d,b();},g=()=>{c+=1,s?.abort(),s=null;},k=()=>{i?.stop(),i=null,u(null),a!==null&&E()&&(document.documentElement.style.cursor=a,a=null);},v=()=>{p&&(p.abort(),p=null),m&&(URL.revokeObjectURL(m.blobUrl),m=null);},y=()=>{g(),k(),v(),l=null,f(Oe());},P=()=>{if(!E())return;k(),l=null;let d=Pt(t.picker);a=document.documentElement.style.cursor,document.documentElement.style.cursor="crosshair",f({phase:"picking"}),i=Le(d,{onHover:u,onSelect:({element:S,descriptor:x})=>{l=S,k(),f({phase:"review",selection:x});},onCancel:()=>{y();}});},M=()=>{let d=Fe();if(d.length!==0)for(let S of d)de({payload:S,...t.transport?{transport:t.transport}:{}}).catch(()=>{se(S);});};if(E()){setTimeout(M,1e3);let d=()=>M();window.addEventListener("online",d),h=()=>window.removeEventListener("online",d);}return {getState:()=>n,subscribe(d){return r.add(d),()=>r.delete(d)},subscribeHover(d){return o.add(d),()=>o.delete(d)},start(){n.phase==="capturing"||n.phase==="submitting"||n.phase==="recording"||n.phase!=="picking"&&P();},stop(){y();},clearSelection(){n.phase==="capturing"||n.phase==="submitting"||n.phase==="recording"||P();},setConfig(d){t=d;},async submit(d,S){if(!E()){let R=w("configuration",new Error("BlocFeed submit can only run in the browser"));return f({phase:"error",lastError:R}),{ok:false}}let x=t.blocfeed_id?.trim?.()??"";if(!x){let L={phase:"error",lastError:w("configuration",new Error("Missing blocfeed_id. Create a project in BlocFeed and pass its blocfeed_id."))};return n.selection&&(L.selection=n.selection),f(L),{ok:false}}if(n.phase==="capturing"||n.phase==="submitting"||n.phase==="recording")return {ok:false};let F=c+1;c=F,s?.abort(),s=new AbortController;let T=s.signal,_=n.selection,U=S?.capture?{...t.capture,...S.capture}:t.capture,H=!!(U?.element||U?.fullPage),ge={phase:H?"capturing":"submitting"};_&&(ge.selection=_),f(ge);try{let R=H?await Pe({selectionElement:l,capture:U,signal:T}):void 0;if(T.aborted||c!==F)return {ok:!1};let L={phase:"submitting"};_&&(L.selection=_),R&&(L.capture=R),f(L);let N={};_&&(N.selection=_),R&&(N.capture=R);let qe=await xe({config:t.metadata,context:N,...t.user?{user:t.user}:{}}),D={version:1,createdAt:new Date().toISOString(),blocfeed_id:x,message:d,metadata:qe};S?.category&&(D.category=S.category),t.user&&(D.user=t.user),_&&(D.selection=_),R&&(D.screenshots=R),m&&(D.video=m);let{result:C}=await de({payload:D,signal:T,...t.transport?{transport:t.transport}:{}});if(T.aborted||c!==F)return C;if(C.ok){m&&(URL.revokeObjectURL(m.blobUrl),m=null);let ee={phase:"success",lastSubmit:C};return _&&(ee.selection=_),R&&(ee.capture=R),f(ee),C}xt(C)&&se(D);let ze=C.api?.error??w("unknown",new Error("Submission failed")),J={phase:"error",lastSubmit:C,lastError:ze};return _&&(J.selection=_),R&&(J.capture=R),f(J),C}catch(R){if(T.aborted||c!==F)return {ok:false};let N={phase:"error",lastError:T.aborted?w("aborted",R):w("unknown",R)};return _&&(N.selection=_),f(N),{ok:false}}finally{c===F&&(s=null);}},async startRecording(){if(n.phase!=="review"||!t.recording?.enabled)return;let d=n.selection,S={phase:"recording",recordingElapsedMs:0};d&&(S.selection=d),f(S);try{let x={config:t.recording};s&&(x.signal=s.signal);let F=await ce(x);p=F,F.onTick(U=>{if(n.phase==="recording"){let H={phase:"recording",recordingElapsedMs:U};d&&(H.selection=d),f(H);}});let T=await F.result;p=null,m=T;let _={phase:"review",video:T};d&&(_.selection=d),f(_);}catch(x){p=null;let T={phase:"review",lastError:x?.kind?x:w("recording_failed",x)};d&&(T.selection=d),f(T);}},stopRecording(){n.phase!=="recording"||!p||p.stop();},removeVideo(){m&&(URL.revokeObjectURL(m.blobUrl),m=null);let d=n.selection,S={phase:"review"};d&&(S.selection=d),f(S);},__unsafeGetSelectedElement(){return l},destroy(){y(),r.clear(),o.clear(),h?.(),h=null;}}}var z=[],$=[],Ue=20,He=15,Y=[],j={},V=null,X=null,Z=null,G=false,Ft=["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 Ct(e){if(e instanceof Error)return e.message;if(typeof e=="string")return e;try{return JSON.stringify(e)}catch{return String(e)}}function Mt(e,t){let n=t.map(Ct).join(" "),r=t.find(i=>i instanceof Error),o={level:e,message:n.slice(0,2e3),timestamp:Date.now()};for(r?.stack&&(o.stack=r.stack.slice(0,2e3)),z.push(o);z.length>Ue;)z.shift();}function fe(e){let t=e.url.toLowerCase();for(let n of Y)if(t.includes(n))return;for($.push(e);$.length>He;)$.shift();}function Pn(e={}){if(G||!E())return;G=true,Ue=e.consoleLimit??20,He=e.networkLimit??15;let t=e.ignoreUrls;if(t!==void 0?Y=t.map(n=>n.toLowerCase()):Y=[...Ft],e.console!==false){let n=e.consoleLevels??["error","warn"];for(let r of n)j[r]=console[r],console[r]=(...o)=>{Mt(r,o),j[r]?.apply(console,o);};}if(e.network!==false&&typeof window.fetch=="function"){V=window.fetch;let n=V;window.fetch=async function(o,i){let l=typeof o=="string"?o:o instanceof URL?o.toString():o.url,a=(i?.method??"GET").toUpperCase(),s=Date.now();try{let c=await n.call(window,o,i);return c.ok||fe({url:l.slice(0,500),method:a,status:c.status,statusText:c.statusText,timestamp:s,durationMs:Date.now()-s}),c}catch(c){throw fe({url:l.slice(0,500),method:a,status:0,statusText:c instanceof Error?c.message:"Network error",timestamp:s,durationMs:Date.now()-s}),c}};}if(e.network!==false&&typeof XMLHttpRequest<"u"){X=XMLHttpRequest.prototype.open,Z=XMLHttpRequest.prototype.send;let n=X,r=Z;XMLHttpRequest.prototype.open=function(o,i,...l){return this.__bf_method=o.toUpperCase(),this.__bf_url=String(i),n.apply(this,[o,i,...l])},XMLHttpRequest.prototype.send=function(...o){let i=this.__bf_method||"GET",l=this.__bf_url||"",a=Date.now();return this.addEventListener("loadend",function(){if(this.status>=400||this.status===0){let s={url:l.slice(0,500),method:i,status:this.status,timestamp:a,durationMs:Date.now()-a};this.statusText&&(s.statusText=this.statusText),fe(s);}}),r.apply(this,o)};}}function xn(){if(G){for(let[e,t]of Object.entries(j))console[e]=t;for(let e of Object.keys(j))delete j[e];V&&(window.fetch=V,V=null),X&&(XMLHttpRequest.prototype.open=X,X=null),Z&&(XMLHttpRequest.prototype.send=Z,Z=null),Y=[],G=false;}}function Fn(){return {consoleLogs:[...z],networkErrors:[...$]}}function Cn(){z=[],$=[];}var Lt=[{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,}/}],I=[],pe=0,me=false;function Dt(e){let t=e.slice(0,80);return t.length<=6?"***":t.slice(0,6)+"***"}function Bt(e,t,n,r){if(I.some(l=>l.rule===e&&l.source===t))return;let i={rule:e,source:t,hint:Dt(n),timestamp:Date.now()};r&&(i.location=r),I.push(i);}function W(e,t,n,r){for(let{name:o,pattern:i}of n){let l=i.exec(e);l&&Bt(o,t,l[0],r);}}function It(e){try{let t=window;if(t.__NEXT_DATA__){let n=JSON.stringify(t.__NEXT_DATA__);W(n,"hydration",e,"__NEXT_DATA__");}if(t.__NUXT__){let n=JSON.stringify(t.__NUXT__);W(n,"hydration",e,"__NUXT__");}}catch{}}function Nt(e){try{document.querySelectorAll("script:not([src])").forEach((n,r)=>{let o=n.textContent;o&&o.length>0&&W(o,"scripts",e,`inline-script[${r}]`);});}catch{}}function Ot(e){try{document.querySelectorAll("meta[content]").forEach(n=>{let r=n.getAttribute("content"),o=n.getAttribute("name")||n.getAttribute("property")||"";r&&r.length>10&&W(r,"meta",e,o?`meta[${o}]`:void 0);});}catch{}}function Ut(e){try{document.querySelectorAll("[data-api-key], [data-secret], [data-token], [data-password]").forEach(n=>{let r=n.attributes;for(let o=0;o<r.length;o++){let i=r[o];i.name.startsWith("data-")&&i.value.length>10&&W(i.value,"dom",e,`${n.tagName.toLowerCase()}[${i.name}]`);}});}catch{}}function Dn(e={}){if(me||!E()||(me=true,e.secretScan===false))return;let t=[...Lt,...e.customPatterns??[]],n=e.scanTargets??["hydration","scripts","meta","dom"];setTimeout(()=>{pe=Date.now(),n.includes("hydration")&&It(t),n.includes("scripts")&&Nt(t),n.includes("meta")&&Ot(t),n.includes("dom")&&Ut(t);let r=e.notify??"both";I.length>0&&(r==="console"||r==="both")&&console.warn(`[BlocFeed Security] Found ${I.length} potential secret(s) exposed in client code:`,I.map(o=>`${o.rule} (${o.source}${o.location?`: ${o.location}`:""})`));},100);}function Bn(){return {findings:[...I],scannedAt:pe}}function In(){I=[],pe=0,me=false;}
|
|
2
|
+
exports.a=E;exports.b=te;exports.c=ve;exports.d=Pe;exports.e=xe;exports.f=se;exports.g=Fe;exports.h=Jt;exports.i=en;exports.j=wt;exports.k=ce;exports.l=Rn;exports.m=Pn;exports.n=xn;exports.o=Fn;exports.p=Cn;exports.q=Dn;exports.r=Bn;exports.s=In;
|
|
@@ -38,6 +38,8 @@ interface BlocFeedHandle {
|
|
|
38
38
|
open: () => void;
|
|
39
39
|
close: () => void;
|
|
40
40
|
submit: (message: string) => Promise<SubmitResult>;
|
|
41
|
+
startRecording: () => Promise<void>;
|
|
42
|
+
stopRecording: () => void;
|
|
41
43
|
isOpen: boolean;
|
|
42
44
|
}
|
|
43
45
|
interface BlocFeedStrings {
|
|
@@ -59,6 +61,12 @@ interface BlocFeedStrings {
|
|
|
59
61
|
categoryFeature?: string;
|
|
60
62
|
categoryUx?: string;
|
|
61
63
|
categoryGeneral?: string;
|
|
64
|
+
recordButton?: string;
|
|
65
|
+
stopRecordButton?: string;
|
|
66
|
+
recordingText?: string;
|
|
67
|
+
recordingDeniedText?: string;
|
|
68
|
+
videoPreviewLabel?: string;
|
|
69
|
+
removeVideoButton?: string;
|
|
62
70
|
}
|
|
63
71
|
interface ConsoleEntry {
|
|
64
72
|
level: "error" | "warn" | "log";
|
|
@@ -162,7 +170,7 @@ interface CaptureResult {
|
|
|
162
170
|
diagnostics: CaptureDiagnostics;
|
|
163
171
|
}
|
|
164
172
|
interface BlocFeedError {
|
|
165
|
-
kind: "unknown" | "configuration" | "capture_failed" | "api_failed" | "aborted";
|
|
173
|
+
kind: "unknown" | "configuration" | "capture_failed" | "recording_failed" | "api_failed" | "aborted";
|
|
166
174
|
message: string;
|
|
167
175
|
detail?: Record<string, unknown>;
|
|
168
176
|
}
|
|
@@ -184,6 +192,33 @@ interface ScreenshotAdapter {
|
|
|
184
192
|
captureElement: (element: Element, options: ScreenshotAdapterOptions) => MaybePromise<ImageAsset>;
|
|
185
193
|
captureFullPage: (options: ScreenshotAdapterOptions) => MaybePromise<ImageAsset>;
|
|
186
194
|
}
|
|
195
|
+
type VideoMime = "video/webm";
|
|
196
|
+
interface RecordingConfig {
|
|
197
|
+
/** Enable video recording button in the widget. Default: false */
|
|
198
|
+
enabled?: boolean;
|
|
199
|
+
/** Maximum recording duration in milliseconds. Default: 30 000 (30 s) */
|
|
200
|
+
maxDurationMs?: number;
|
|
201
|
+
/** Video MIME type. Default: "video/webm" */
|
|
202
|
+
mime?: VideoMime;
|
|
203
|
+
/** Video bitrate in bits/s. Default: 2 500 000 (2.5 Mbps) */
|
|
204
|
+
videoBitsPerSecond?: number;
|
|
205
|
+
}
|
|
206
|
+
interface VideoAsset {
|
|
207
|
+
mime: VideoMime;
|
|
208
|
+
/** Object URL (blob:) for local preview. Revoked after submission. */
|
|
209
|
+
blobUrl: string;
|
|
210
|
+
/** The raw Blob — held in memory until uploaded. */
|
|
211
|
+
blob: Blob;
|
|
212
|
+
/** Duration of the recording in milliseconds. */
|
|
213
|
+
durationMs: number;
|
|
214
|
+
/** Byte size of the video blob. */
|
|
215
|
+
sizeBytes: number;
|
|
216
|
+
}
|
|
217
|
+
interface VideoIntent {
|
|
218
|
+
mime: string;
|
|
219
|
+
durationMs: number;
|
|
220
|
+
sizeBytes: number;
|
|
221
|
+
}
|
|
187
222
|
interface CaptureConfig {
|
|
188
223
|
element?: boolean;
|
|
189
224
|
fullPage?: boolean;
|
|
@@ -242,6 +277,8 @@ interface BlocFeedConfig {
|
|
|
242
277
|
diagnostics?: DiagnosticsConfig;
|
|
243
278
|
/** Client-side secret leak detection. */
|
|
244
279
|
security?: SecurityConfig;
|
|
280
|
+
/** Video recording configuration. */
|
|
281
|
+
recording?: RecordingConfig;
|
|
245
282
|
ui?: {
|
|
246
283
|
/** z-index for the widget overlay/panel. */
|
|
247
284
|
zIndex?: number;
|
|
@@ -293,6 +330,10 @@ interface FeedbackPayload {
|
|
|
293
330
|
screenshots?: CaptureResult;
|
|
294
331
|
/** Lightweight screenshot metadata sent instead of base64 dataUrls. */
|
|
295
332
|
screenshot_intent?: ScreenshotIntent;
|
|
333
|
+
/** Video recording asset (blob held separately during transport). */
|
|
334
|
+
video?: VideoAsset;
|
|
335
|
+
/** Lightweight video metadata sent instead of the blob. */
|
|
336
|
+
video_intent?: VideoIntent;
|
|
296
337
|
/** First-class user identity. */
|
|
297
338
|
user?: BlocFeedUser;
|
|
298
339
|
metadata: Record<string, unknown>;
|
|
@@ -303,6 +344,7 @@ interface FeedbackApiResponse {
|
|
|
303
344
|
upload_urls?: {
|
|
304
345
|
element?: string;
|
|
305
346
|
fullPage?: string;
|
|
347
|
+
video?: string;
|
|
306
348
|
};
|
|
307
349
|
}
|
|
308
350
|
interface TransportResult {
|
|
@@ -314,11 +356,13 @@ interface SubmitResult {
|
|
|
314
356
|
ok: boolean;
|
|
315
357
|
api?: TransportResult;
|
|
316
358
|
}
|
|
317
|
-
type SessionPhase = "idle" | "picking" | "review" | "capturing" | "submitting" | "success" | "error";
|
|
359
|
+
type SessionPhase = "idle" | "picking" | "review" | "recording" | "capturing" | "submitting" | "success" | "error";
|
|
318
360
|
interface BlocFeedState {
|
|
319
361
|
phase: SessionPhase;
|
|
320
362
|
selection?: ElementDescriptor;
|
|
321
363
|
capture?: CaptureResult;
|
|
364
|
+
video?: VideoAsset;
|
|
365
|
+
recordingElapsedMs?: number;
|
|
322
366
|
lastSubmit?: SubmitResult;
|
|
323
367
|
lastError?: BlocFeedError;
|
|
324
368
|
}
|
|
@@ -342,10 +386,13 @@ interface BlocFeedController {
|
|
|
342
386
|
capture?: CaptureConfig;
|
|
343
387
|
category?: FeedbackCategory;
|
|
344
388
|
}) => Promise<SubmitResult>;
|
|
389
|
+
startRecording: () => Promise<void>;
|
|
390
|
+
stopRecording: () => void;
|
|
391
|
+
removeVideo: () => void;
|
|
345
392
|
/** Internal: used by UI to access the live element handle. */
|
|
346
393
|
__unsafeGetSelectedElement: () => Element | null;
|
|
347
394
|
destroy: () => void;
|
|
348
395
|
}
|
|
349
396
|
declare function createBlocFeedController(config: BlocFeedConfig): BlocFeedController;
|
|
350
397
|
|
|
351
|
-
export { type BlocFeedConfig as B, type CaptureConfig as C, type DiagnosticsConfig as D, type ElementDescriptor as E, type FeedbackCategory as F, type HoverListener as H, type ImageAsset as I, type MaybePromise as M, type NetworkEntry as N, type PickerConfig as P, type
|
|
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 StateListener as G, type HoverListener as H, type ImageAsset as I, createBlocFeedController as J, 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 };
|
|
@@ -38,6 +38,8 @@ interface BlocFeedHandle {
|
|
|
38
38
|
open: () => void;
|
|
39
39
|
close: () => void;
|
|
40
40
|
submit: (message: string) => Promise<SubmitResult>;
|
|
41
|
+
startRecording: () => Promise<void>;
|
|
42
|
+
stopRecording: () => void;
|
|
41
43
|
isOpen: boolean;
|
|
42
44
|
}
|
|
43
45
|
interface BlocFeedStrings {
|
|
@@ -59,6 +61,12 @@ interface BlocFeedStrings {
|
|
|
59
61
|
categoryFeature?: string;
|
|
60
62
|
categoryUx?: string;
|
|
61
63
|
categoryGeneral?: string;
|
|
64
|
+
recordButton?: string;
|
|
65
|
+
stopRecordButton?: string;
|
|
66
|
+
recordingText?: string;
|
|
67
|
+
recordingDeniedText?: string;
|
|
68
|
+
videoPreviewLabel?: string;
|
|
69
|
+
removeVideoButton?: string;
|
|
62
70
|
}
|
|
63
71
|
interface ConsoleEntry {
|
|
64
72
|
level: "error" | "warn" | "log";
|
|
@@ -162,7 +170,7 @@ interface CaptureResult {
|
|
|
162
170
|
diagnostics: CaptureDiagnostics;
|
|
163
171
|
}
|
|
164
172
|
interface BlocFeedError {
|
|
165
|
-
kind: "unknown" | "configuration" | "capture_failed" | "api_failed" | "aborted";
|
|
173
|
+
kind: "unknown" | "configuration" | "capture_failed" | "recording_failed" | "api_failed" | "aborted";
|
|
166
174
|
message: string;
|
|
167
175
|
detail?: Record<string, unknown>;
|
|
168
176
|
}
|
|
@@ -184,6 +192,33 @@ interface ScreenshotAdapter {
|
|
|
184
192
|
captureElement: (element: Element, options: ScreenshotAdapterOptions) => MaybePromise<ImageAsset>;
|
|
185
193
|
captureFullPage: (options: ScreenshotAdapterOptions) => MaybePromise<ImageAsset>;
|
|
186
194
|
}
|
|
195
|
+
type VideoMime = "video/webm";
|
|
196
|
+
interface RecordingConfig {
|
|
197
|
+
/** Enable video recording button in the widget. Default: false */
|
|
198
|
+
enabled?: boolean;
|
|
199
|
+
/** Maximum recording duration in milliseconds. Default: 30 000 (30 s) */
|
|
200
|
+
maxDurationMs?: number;
|
|
201
|
+
/** Video MIME type. Default: "video/webm" */
|
|
202
|
+
mime?: VideoMime;
|
|
203
|
+
/** Video bitrate in bits/s. Default: 2 500 000 (2.5 Mbps) */
|
|
204
|
+
videoBitsPerSecond?: number;
|
|
205
|
+
}
|
|
206
|
+
interface VideoAsset {
|
|
207
|
+
mime: VideoMime;
|
|
208
|
+
/** Object URL (blob:) for local preview. Revoked after submission. */
|
|
209
|
+
blobUrl: string;
|
|
210
|
+
/** The raw Blob — held in memory until uploaded. */
|
|
211
|
+
blob: Blob;
|
|
212
|
+
/** Duration of the recording in milliseconds. */
|
|
213
|
+
durationMs: number;
|
|
214
|
+
/** Byte size of the video blob. */
|
|
215
|
+
sizeBytes: number;
|
|
216
|
+
}
|
|
217
|
+
interface VideoIntent {
|
|
218
|
+
mime: string;
|
|
219
|
+
durationMs: number;
|
|
220
|
+
sizeBytes: number;
|
|
221
|
+
}
|
|
187
222
|
interface CaptureConfig {
|
|
188
223
|
element?: boolean;
|
|
189
224
|
fullPage?: boolean;
|
|
@@ -242,6 +277,8 @@ interface BlocFeedConfig {
|
|
|
242
277
|
diagnostics?: DiagnosticsConfig;
|
|
243
278
|
/** Client-side secret leak detection. */
|
|
244
279
|
security?: SecurityConfig;
|
|
280
|
+
/** Video recording configuration. */
|
|
281
|
+
recording?: RecordingConfig;
|
|
245
282
|
ui?: {
|
|
246
283
|
/** z-index for the widget overlay/panel. */
|
|
247
284
|
zIndex?: number;
|
|
@@ -293,6 +330,10 @@ interface FeedbackPayload {
|
|
|
293
330
|
screenshots?: CaptureResult;
|
|
294
331
|
/** Lightweight screenshot metadata sent instead of base64 dataUrls. */
|
|
295
332
|
screenshot_intent?: ScreenshotIntent;
|
|
333
|
+
/** Video recording asset (blob held separately during transport). */
|
|
334
|
+
video?: VideoAsset;
|
|
335
|
+
/** Lightweight video metadata sent instead of the blob. */
|
|
336
|
+
video_intent?: VideoIntent;
|
|
296
337
|
/** First-class user identity. */
|
|
297
338
|
user?: BlocFeedUser;
|
|
298
339
|
metadata: Record<string, unknown>;
|
|
@@ -303,6 +344,7 @@ interface FeedbackApiResponse {
|
|
|
303
344
|
upload_urls?: {
|
|
304
345
|
element?: string;
|
|
305
346
|
fullPage?: string;
|
|
347
|
+
video?: string;
|
|
306
348
|
};
|
|
307
349
|
}
|
|
308
350
|
interface TransportResult {
|
|
@@ -314,11 +356,13 @@ interface SubmitResult {
|
|
|
314
356
|
ok: boolean;
|
|
315
357
|
api?: TransportResult;
|
|
316
358
|
}
|
|
317
|
-
type SessionPhase = "idle" | "picking" | "review" | "capturing" | "submitting" | "success" | "error";
|
|
359
|
+
type SessionPhase = "idle" | "picking" | "review" | "recording" | "capturing" | "submitting" | "success" | "error";
|
|
318
360
|
interface BlocFeedState {
|
|
319
361
|
phase: SessionPhase;
|
|
320
362
|
selection?: ElementDescriptor;
|
|
321
363
|
capture?: CaptureResult;
|
|
364
|
+
video?: VideoAsset;
|
|
365
|
+
recordingElapsedMs?: number;
|
|
322
366
|
lastSubmit?: SubmitResult;
|
|
323
367
|
lastError?: BlocFeedError;
|
|
324
368
|
}
|
|
@@ -342,10 +386,13 @@ interface BlocFeedController {
|
|
|
342
386
|
capture?: CaptureConfig;
|
|
343
387
|
category?: FeedbackCategory;
|
|
344
388
|
}) => Promise<SubmitResult>;
|
|
389
|
+
startRecording: () => Promise<void>;
|
|
390
|
+
stopRecording: () => void;
|
|
391
|
+
removeVideo: () => void;
|
|
345
392
|
/** Internal: used by UI to access the live element handle. */
|
|
346
393
|
__unsafeGetSelectedElement: () => Element | null;
|
|
347
394
|
destroy: () => void;
|
|
348
395
|
}
|
|
349
396
|
declare function createBlocFeedController(config: BlocFeedConfig): BlocFeedController;
|
|
350
397
|
|
|
351
|
-
export { type BlocFeedConfig as B, type CaptureConfig as C, type DiagnosticsConfig as D, type ElementDescriptor as E, type FeedbackCategory as F, type HoverListener as H, type ImageAsset as I, type MaybePromise as M, type NetworkEntry as N, type PickerConfig as P, type
|
|
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 StateListener as G, type HoverListener as H, type ImageAsset as I, createBlocFeedController as J, 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 chunkORVW2RTZ_cjs=require('./chunk-ORVW2RTZ.cjs');function c(o){if(o?.aborted)throw new Error("Aborted")}async function P(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 P(o);return {dataUrl:o,mime:t,width:e,height:r}}function B(o){return {async captureElement(t,e){if(!chunkORVW2RTZ_cjs.a())throw new Error("captureElement can only run in the browser");c(e.signal);let r={scale:e.pixelRatio},n=e.mime==="image/jpeg"?await o.domToJpeg(t,{...r,quality:e.quality??.92}):await o.domToPng(t,r);return c(e.signal),await l(n,e.mime)},async captureFullPage(t){if(!chunkORVW2RTZ_cjs.a())throw new Error("captureFullPage can only run in the browser");c(t.signal);let e=document.documentElement,r=Math.max(e.scrollWidth,e.clientWidth),n=Math.max(e.scrollHeight,e.clientHeight),a=Math.min(1,t.maxDimension/Math.max(r,n)),s={width:Math.max(1,Math.round(r*a)),height:Math.max(1,Math.round(n*a)),scale:t.pixelRatio},i=t.mime==="image/jpeg"?await o.domToJpeg(e,{...s,quality:t.quality??.92}):await o.domToPng(e,s);return c(t.signal),await l(i,t.mime)}}}function k(o){let[t,e]=o.split(",",2);if(!t||!e)throw new Error("Invalid data URL");let n=/data:(.*?);base64/.exec(t)?.[1]||"application/octet-stream",a=atob(e),s=new Uint8Array(a.length);for(let i=0;i<a.length;i+=1)s[i]=a.charCodeAt(i);return new Blob([s],{type:n})}Object.defineProperty(exports,"clearDiagnostics",{enumerable:true,get:function(){return chunkORVW2RTZ_cjs.p}});Object.defineProperty(exports,"clearQueue",{enumerable:true,get:function(){return chunkORVW2RTZ_cjs.h}});Object.defineProperty(exports,"clearSecurityFindings",{enumerable:true,get:function(){return chunkORVW2RTZ_cjs.s}});Object.defineProperty(exports,"collectMetadata",{enumerable:true,get:function(){return chunkORVW2RTZ_cjs.e}});Object.defineProperty(exports,"createBlocFeedController",{enumerable:true,get:function(){return chunkORVW2RTZ_cjs.l}});Object.defineProperty(exports,"createHtmlToImageAdapter",{enumerable:true,get:function(){return chunkORVW2RTZ_cjs.c}});Object.defineProperty(exports,"dequeueAll",{enumerable:true,get:function(){return chunkORVW2RTZ_cjs.g}});Object.defineProperty(exports,"drainDiagnostics",{enumerable:true,get:function(){return chunkORVW2RTZ_cjs.o}});Object.defineProperty(exports,"enqueue",{enumerable:true,get:function(){return chunkORVW2RTZ_cjs.f}});Object.defineProperty(exports,"getQueueSize",{enumerable:true,get:function(){return chunkORVW2RTZ_cjs.i}});Object.defineProperty(exports,"getSecurityFindings",{enumerable:true,get:function(){return chunkORVW2RTZ_cjs.r}});Object.defineProperty(exports,"installDiagnostics",{enumerable:true,get:function(){return chunkORVW2RTZ_cjs.m}});Object.defineProperty(exports,"isRecordingSupported",{enumerable:true,get:function(){return chunkORVW2RTZ_cjs.j}});Object.defineProperty(exports,"runCapture",{enumerable:true,get:function(){return chunkORVW2RTZ_cjs.d}});Object.defineProperty(exports,"runSecretScan",{enumerable:true,get:function(){return chunkORVW2RTZ_cjs.q}});Object.defineProperty(exports,"startRecording",{enumerable:true,get:function(){return chunkORVW2RTZ_cjs.k}});Object.defineProperty(exports,"uninstallDiagnostics",{enumerable:true,get:function(){return chunkORVW2RTZ_cjs.n}});exports.createModernScreenshotAdapter=B;exports.dataUrlToBlob=k;
|