@checkflow/sdk 1.1.1 → 1.1.2
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/README.md +63 -219
- package/dist/chunk-CD33QAA6.mjs +131 -0
- package/dist/chunk-CQ56DMFR.mjs +83 -0
- package/dist/highlighter-D_wZWHlS.d.mts +71 -0
- package/dist/highlighter-D_wZWHlS.d.ts +71 -0
- package/dist/highlighter-W4XDALRE.mjs +8 -0
- package/dist/index.d.mts +41 -0
- package/dist/index.d.ts +38 -20
- package/dist/index.js +607 -17221
- package/dist/index.mjs +411 -0
- package/dist/react.d.mts +28 -0
- package/dist/react.d.ts +28 -0
- package/dist/react.js +743 -0
- package/dist/react.mjs +51 -0
- package/dist/screenshot-CUMBPE2T.mjs +12 -0
- package/dist/vue.d.mts +26 -0
- package/dist/vue.d.ts +26 -0
- package/dist/vue.js +744 -0
- package/dist/vue.mjs +53 -0
- package/package.json +38 -53
- package/dist/analytics-tracker.d.ts +0 -112
- package/dist/annotation/editor.d.ts +0 -72
- package/dist/annotation/index.d.ts +0 -9
- package/dist/annotation/styles.d.ts +0 -6
- package/dist/annotation/toolbar.d.ts +0 -32
- package/dist/annotation/types.d.ts +0 -85
- package/dist/api-client.d.ts +0 -76
- package/dist/checkflow.css +0 -1
- package/dist/checkflow.d.ts +0 -112
- package/dist/context-capture.d.ts +0 -42
- package/dist/error-capture.d.ts +0 -60
- package/dist/index.esm.js +0 -17210
- package/dist/index.esm.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/optimized-recorder.d.ts +0 -94
- package/dist/privacy/detector.d.ts +0 -56
- package/dist/privacy/index.d.ts +0 -8
- package/dist/privacy/masker.d.ts +0 -43
- package/dist/privacy/types.d.ts +0 -54
- package/dist/react/index.d.ts +0 -77
- package/dist/session-recording-rrweb.d.ts +0 -100
- package/dist/session-recording.d.ts +0 -74
- package/dist/types.d.ts +0 -299
- package/dist/vue/index.d.ts +0 -55
- package/dist/widget/Widget.d.ts +0 -98
- package/dist/widget/index.d.ts +0 -2
package/README.md
CHANGED
|
@@ -1,265 +1,109 @@
|
|
|
1
1
|
# @checkflow/sdk
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
[](https://www.npmjs.com/package/@checkflow/sdk)
|
|
6
|
-
[](https://opensource.org/licenses/MIT)
|
|
3
|
+
Lightweight bug reporter & feedback SDK for Checkflow — with screenshot capture, element highlighting, and framework integrations.
|
|
7
4
|
|
|
8
5
|
## Features
|
|
9
6
|
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
- 📱 **Responsive** - Works on desktop, tablet, and mobile
|
|
7
|
+
- **Floating widget** — customizable feedback button with form
|
|
8
|
+
- **Screenshot capture** — automatic page screenshots via html2canvas
|
|
9
|
+
- **Element highlighting** — click-to-highlight elements for visual bug reports
|
|
10
|
+
- **Context collection** — URL, viewport, browser, OS, locale, timezone
|
|
11
|
+
- **Console & error capture** — intercepts console.log/warn/error + JS errors
|
|
12
|
+
- **Performance metrics** — FCP, DOM load, page load times
|
|
13
|
+
- **Framework support** — React/Next.js, Vue/Nuxt, vanilla JS
|
|
18
14
|
|
|
19
15
|
## Installation
|
|
20
16
|
|
|
21
17
|
```bash
|
|
22
18
|
npm install @checkflow/sdk
|
|
23
|
-
# or
|
|
24
|
-
yarn add @checkflow/sdk
|
|
25
|
-
# or
|
|
26
|
-
pnpm add @checkflow/sdk
|
|
27
19
|
```
|
|
28
20
|
|
|
29
|
-
## Quick Start
|
|
30
|
-
|
|
31
|
-
### Vanilla JavaScript
|
|
32
|
-
|
|
33
|
-
```javascript
|
|
34
|
-
import { createCheckFlow } from '@checkflow/sdk';
|
|
21
|
+
## Quick Start (Vanilla JS)
|
|
35
22
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
showWidget: true,
|
|
39
|
-
captureErrors: true,
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
// Programmatically open the feedback form
|
|
43
|
-
checkflow.open();
|
|
23
|
+
```js
|
|
24
|
+
import { init } from '@checkflow/sdk';
|
|
44
25
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
description: 'Something is broken',
|
|
49
|
-
type: 'bug',
|
|
50
|
-
priority: 'high',
|
|
26
|
+
init({
|
|
27
|
+
apiKey: 'ck_live_...',
|
|
28
|
+
widget: { position: 'bottom-right', color: '#1e3a5f' },
|
|
51
29
|
});
|
|
52
30
|
```
|
|
53
31
|
|
|
54
|
-
|
|
32
|
+
## React / Next.js
|
|
55
33
|
|
|
56
34
|
```tsx
|
|
57
|
-
|
|
35
|
+
// app/layout.tsx or _app.tsx
|
|
36
|
+
import { useCheckflowInit } from '@checkflow/sdk/react';
|
|
58
37
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
return
|
|
62
|
-
<CheckFlowProvider
|
|
63
|
-
apiKey="your-api-key"
|
|
64
|
-
options={{ projectId: 'your-project-id' }}
|
|
65
|
-
>
|
|
66
|
-
<YourApp />
|
|
67
|
-
</CheckFlowProvider>
|
|
68
|
-
);
|
|
38
|
+
export default function Layout({ children }) {
|
|
39
|
+
useCheckflowInit({ apiKey: 'ck_live_...' });
|
|
40
|
+
return <>{children}</>;
|
|
69
41
|
}
|
|
42
|
+
```
|
|
70
43
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
44
|
+
```tsx
|
|
45
|
+
// Any component
|
|
46
|
+
import { useCheckflowFeedback } from '@checkflow/sdk/react';
|
|
74
47
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
Send Feedback
|
|
78
|
-
</button>
|
|
79
|
-
);
|
|
80
|
-
}
|
|
48
|
+
function MyComponent() {
|
|
49
|
+
const { send, screenshot, highlight } = useCheckflowFeedback();
|
|
81
50
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
51
|
+
const reportBug = async () => {
|
|
52
|
+
const img = await screenshot();
|
|
53
|
+
const annotations = await highlight();
|
|
54
|
+
await send({ title: 'Bug report', type: 'BUG' });
|
|
55
|
+
};
|
|
85
56
|
}
|
|
86
57
|
```
|
|
87
58
|
|
|
88
|
-
|
|
59
|
+
## Vue / Nuxt
|
|
89
60
|
|
|
90
|
-
```
|
|
91
|
-
|
|
92
|
-
import {
|
|
93
|
-
|
|
94
|
-
const app = createApp(App);
|
|
95
|
-
|
|
96
|
-
app.use(CheckFlowPlugin, {
|
|
97
|
-
apiKey: 'your-api-key',
|
|
98
|
-
projectId: 'your-project-id',
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
app.mount('#app');
|
|
61
|
+
```ts
|
|
62
|
+
// main.ts
|
|
63
|
+
import { createCheckflowPlugin } from '@checkflow/sdk/vue';
|
|
64
|
+
app.use(createCheckflowPlugin({ apiKey: 'ck_live_...' }));
|
|
102
65
|
```
|
|
103
66
|
|
|
104
67
|
```vue
|
|
105
68
|
<script setup>
|
|
106
|
-
import {
|
|
107
|
-
|
|
108
|
-
const { openWidget } = useCheckFlow();
|
|
109
|
-
const { formState, submit, isSubmitting } = useFeedbackForm();
|
|
69
|
+
import { useCheckflow } from '@checkflow/sdk/vue';
|
|
70
|
+
const { send, screenshot, highlight } = useCheckflow();
|
|
110
71
|
</script>
|
|
111
|
-
|
|
112
|
-
<template>
|
|
113
|
-
<button @click="openWidget">Send Feedback</button>
|
|
114
|
-
</template>
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
## Configuration Options
|
|
118
|
-
|
|
119
|
-
```typescript
|
|
120
|
-
interface CheckFlowOptions {
|
|
121
|
-
// API endpoint (default: https://api.checkflow.io)
|
|
122
|
-
apiUrl?: string;
|
|
123
|
-
|
|
124
|
-
// Project ID for feedback association
|
|
125
|
-
projectId?: string;
|
|
126
|
-
|
|
127
|
-
// Widget options
|
|
128
|
-
showWidget?: boolean; // Show floating button (default: true)
|
|
129
|
-
widgetPosition?: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left';
|
|
130
|
-
widgetButtonText?: string; // Button text (default: 'Feedback')
|
|
131
|
-
|
|
132
|
-
// Capture options
|
|
133
|
-
captureErrors?: boolean; // Auto-capture errors (default: true)
|
|
134
|
-
captureConsole?: boolean; // Capture console logs (default: true)
|
|
135
|
-
captureNetwork?: boolean; // Capture network requests (default: true)
|
|
136
|
-
capturePerformance?: boolean; // Capture performance metrics (default: true)
|
|
137
|
-
maxConsoleEntries?: number; // Max console entries (default: 100)
|
|
138
|
-
maxNetworkEntries?: number; // Max network entries (default: 100)
|
|
139
|
-
|
|
140
|
-
// User identification
|
|
141
|
-
user?: {
|
|
142
|
-
id?: string;
|
|
143
|
-
email?: string;
|
|
144
|
-
name?: string;
|
|
145
|
-
};
|
|
146
|
-
|
|
147
|
-
// Callbacks
|
|
148
|
-
beforeSubmit?: (feedback) => feedback | false;
|
|
149
|
-
onSubmit?: (result) => void;
|
|
150
|
-
onError?: (error) => void;
|
|
151
|
-
|
|
152
|
-
// Localization
|
|
153
|
-
locale?: 'en' | 'fr' | 'es' | 'de';
|
|
154
|
-
translations?: Partial<Translations>;
|
|
155
|
-
|
|
156
|
-
// Debug mode
|
|
157
|
-
debug?: boolean;
|
|
158
|
-
}
|
|
159
72
|
```
|
|
160
73
|
|
|
161
|
-
##
|
|
74
|
+
## Programmatic API
|
|
162
75
|
|
|
163
|
-
```
|
|
164
|
-
import {
|
|
165
|
-
|
|
166
|
-
function App() {
|
|
167
|
-
return (
|
|
168
|
-
<CheckFlowErrorBoundary
|
|
169
|
-
fallback={(error, reset) => (
|
|
170
|
-
<div>
|
|
171
|
-
<h1>Something went wrong</h1>
|
|
172
|
-
<pre>{error.message}</pre>
|
|
173
|
-
<button onClick={reset}>Try again</button>
|
|
174
|
-
</div>
|
|
175
|
-
)}
|
|
176
|
-
onError={(error, errorInfo) => {
|
|
177
|
-
console.error('Caught error:', error);
|
|
178
|
-
}}
|
|
179
|
-
>
|
|
180
|
-
<YourApp />
|
|
181
|
-
</CheckFlowErrorBoundary>
|
|
182
|
-
);
|
|
183
|
-
}
|
|
184
|
-
```
|
|
185
|
-
|
|
186
|
-
## Manual Context Capture
|
|
187
|
-
|
|
188
|
-
```typescript
|
|
189
|
-
import { createCheckFlow } from '@checkflow/sdk';
|
|
190
|
-
|
|
191
|
-
const checkflow = createCheckFlow('your-api-key');
|
|
192
|
-
|
|
193
|
-
// Capture current page context
|
|
194
|
-
const capture = await checkflow.capture({
|
|
195
|
-
fullPage: true, // Capture full page (not just viewport)
|
|
196
|
-
quality: 80, // Screenshot quality (1-100)
|
|
197
|
-
includeConsole: true, // Include console logs
|
|
198
|
-
includeNetwork: true, // Include network logs
|
|
199
|
-
hideElements: ['.secret'], // CSS selectors to hide
|
|
200
|
-
maskElements: ['.password'], // CSS selectors to mask
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
console.log(capture.screenshot); // Base64 screenshot
|
|
204
|
-
console.log(capture.consoleLogs); // Console entries
|
|
205
|
-
console.log(capture.networkLogs); // Network requests
|
|
206
|
-
console.log(capture.performance); // Performance metrics
|
|
207
|
-
console.log(capture.context); // Page context (URL, browser, etc.)
|
|
208
|
-
```
|
|
76
|
+
```js
|
|
77
|
+
import { sendFeedback, captureScreenshot, startHighlighting, showWidget, hideWidget, destroy } from '@checkflow/sdk';
|
|
209
78
|
|
|
210
|
-
|
|
79
|
+
// Send feedback
|
|
80
|
+
await sendFeedback({ title: 'Bug', type: 'BUG', priority: 'HIGH' });
|
|
211
81
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
checkflow.setUser({
|
|
215
|
-
id: 'user-123',
|
|
216
|
-
email: 'user@example.com',
|
|
217
|
-
name: 'John Doe',
|
|
218
|
-
});
|
|
219
|
-
|
|
220
|
-
// Clear user info (on logout)
|
|
221
|
-
checkflow.clearUser();
|
|
222
|
-
```
|
|
82
|
+
// Capture screenshot
|
|
83
|
+
const dataUrl = await captureScreenshot();
|
|
223
84
|
|
|
224
|
-
|
|
85
|
+
// Start element highlighting mode
|
|
86
|
+
const annotations = await startHighlighting();
|
|
225
87
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
<script>
|
|
231
|
-
const checkflow = new CheckFlow('your-api-key', {
|
|
232
|
-
projectId: 'your-project-id',
|
|
233
|
-
}).init();
|
|
234
|
-
</script>
|
|
88
|
+
// Widget control
|
|
89
|
+
showWidget();
|
|
90
|
+
hideWidget();
|
|
91
|
+
destroy();
|
|
235
92
|
```
|
|
236
93
|
|
|
237
|
-
##
|
|
238
|
-
|
|
239
|
-
The SDK is written in TypeScript and includes full type definitions.
|
|
240
|
-
|
|
241
|
-
```typescript
|
|
242
|
-
import type {
|
|
243
|
-
FeedbackData,
|
|
244
|
-
CaptureResult,
|
|
245
|
-
CheckFlowOptions
|
|
246
|
-
} from '@checkflow/sdk';
|
|
247
|
-
```
|
|
248
|
-
|
|
249
|
-
## Privacy & Security
|
|
250
|
-
|
|
251
|
-
- Screenshots can exclude sensitive elements using `hideElements` and `maskElements`
|
|
252
|
-
- Console logs are filtered to remove potentially sensitive data
|
|
253
|
-
- Network requests to CheckFlow API are excluded from capture
|
|
254
|
-
- All data is transmitted over HTTPS
|
|
255
|
-
|
|
256
|
-
## Browser Support
|
|
94
|
+
## Configuration
|
|
257
95
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
96
|
+
| Option | Type | Default | Description |
|
|
97
|
+
|--------|------|---------|-------------|
|
|
98
|
+
| `apiKey` | `string` | required | Your project API key |
|
|
99
|
+
| `endpoint` | `string` | `https://api.checkflow.space/api/v1` | API endpoint |
|
|
100
|
+
| `environment` | `string` | `production` | Environment name |
|
|
101
|
+
| `enabled` | `boolean` | `true` | Enable/disable SDK |
|
|
102
|
+
| `widget.position` | `string` | `bottom-right` | Widget position |
|
|
103
|
+
| `widget.color` | `string` | `#1e3a5f` | Widget accent color |
|
|
104
|
+
| `widget.text` | `string` | `Report Bug` | Widget button text |
|
|
105
|
+
| `widget.showOnInit` | `boolean` | `true` | Show widget on init |
|
|
262
106
|
|
|
263
107
|
## License
|
|
264
108
|
|
|
265
|
-
MIT
|
|
109
|
+
MIT
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
// src/highlighter.ts
|
|
2
|
+
var overlay = null;
|
|
3
|
+
var highlights = [];
|
|
4
|
+
var isActive = false;
|
|
5
|
+
var onDoneCallback = null;
|
|
6
|
+
function getSelector(el) {
|
|
7
|
+
if (el.id) return `#${el.id}`;
|
|
8
|
+
const parts = [];
|
|
9
|
+
let current = el;
|
|
10
|
+
while (current && current !== document.body) {
|
|
11
|
+
let sel = current.tagName.toLowerCase();
|
|
12
|
+
if (current.id) {
|
|
13
|
+
parts.unshift(`#${current.id}`);
|
|
14
|
+
break;
|
|
15
|
+
}
|
|
16
|
+
if (current.className && typeof current.className === "string") {
|
|
17
|
+
const cls = current.className.trim().split(/\s+/).slice(0, 2).join(".");
|
|
18
|
+
if (cls) sel += `.${cls}`;
|
|
19
|
+
}
|
|
20
|
+
const parent = current.parentElement;
|
|
21
|
+
if (parent) {
|
|
22
|
+
const siblings = Array.from(parent.children).filter((c) => c.tagName === current.tagName);
|
|
23
|
+
if (siblings.length > 1) sel += `:nth-child(${Array.from(parent.children).indexOf(current) + 1})`;
|
|
24
|
+
}
|
|
25
|
+
parts.unshift(sel);
|
|
26
|
+
current = current.parentElement;
|
|
27
|
+
}
|
|
28
|
+
return parts.join(" > ");
|
|
29
|
+
}
|
|
30
|
+
function createOverlay() {
|
|
31
|
+
overlay = document.createElement("div");
|
|
32
|
+
overlay.id = "cf-highlight-overlay";
|
|
33
|
+
overlay.style.cssText = "position:fixed;top:0;left:0;width:100%;height:100%;z-index:99999;cursor:crosshair;";
|
|
34
|
+
const toolbar = document.createElement("div");
|
|
35
|
+
toolbar.style.cssText = "position:fixed;top:12px;left:50%;transform:translateX(-50%);z-index:100002;background:#1e3a5f;color:#fff;padding:8px 16px;border-radius:10px;font-family:system-ui;font-size:13px;display:flex;align-items:center;gap:12px;box-shadow:0 4px 20px rgba(0,0,0,.25);";
|
|
36
|
+
toolbar.innerHTML = `
|
|
37
|
+
<span>Click elements to highlight them</span>
|
|
38
|
+
<button id="cf-hl-done" style="background:#10b981;color:#fff;border:none;border-radius:6px;padding:6px 14px;font-size:12px;cursor:pointer;font-weight:500;">Done (${highlights.length})</button>
|
|
39
|
+
<button id="cf-hl-cancel" style="background:transparent;color:#fff;border:1px solid rgba(255,255,255,.3);border-radius:6px;padding:6px 14px;font-size:12px;cursor:pointer;">Cancel</button>
|
|
40
|
+
`;
|
|
41
|
+
overlay.appendChild(toolbar);
|
|
42
|
+
document.body.appendChild(overlay);
|
|
43
|
+
overlay.addEventListener("click", handleClick);
|
|
44
|
+
overlay.addEventListener("mousemove", handleHover);
|
|
45
|
+
document.getElementById("cf-hl-done")?.addEventListener("click", finishHighlighting);
|
|
46
|
+
document.getElementById("cf-hl-cancel")?.addEventListener("click", cancelHighlighting);
|
|
47
|
+
}
|
|
48
|
+
var hoverBox = null;
|
|
49
|
+
function handleHover(e) {
|
|
50
|
+
if (!hoverBox) {
|
|
51
|
+
hoverBox = document.createElement("div");
|
|
52
|
+
hoverBox.style.cssText = "position:fixed;border:2px solid #0c66e4;background:rgba(12,102,228,0.08);pointer-events:none;z-index:100001;border-radius:3px;transition:all 0.05s;";
|
|
53
|
+
document.body.appendChild(hoverBox);
|
|
54
|
+
}
|
|
55
|
+
const target = document.elementFromPoint(e.clientX, e.clientY);
|
|
56
|
+
if (target && target !== overlay && !overlay?.contains(target)) {
|
|
57
|
+
const rect = target.getBoundingClientRect();
|
|
58
|
+
hoverBox.style.left = rect.left + "px";
|
|
59
|
+
hoverBox.style.top = rect.top + "px";
|
|
60
|
+
hoverBox.style.width = rect.width + "px";
|
|
61
|
+
hoverBox.style.height = rect.height + "px";
|
|
62
|
+
hoverBox.style.display = "block";
|
|
63
|
+
} else {
|
|
64
|
+
hoverBox.style.display = "none";
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
function handleClick(e) {
|
|
68
|
+
e.preventDefault();
|
|
69
|
+
e.stopPropagation();
|
|
70
|
+
const target = document.elementFromPoint(e.clientX, e.clientY);
|
|
71
|
+
if (!target || target === overlay || overlay?.contains(target)) return;
|
|
72
|
+
const rect = target.getBoundingClientRect();
|
|
73
|
+
highlights.push({ el: target, rect });
|
|
74
|
+
const marker = document.createElement("div");
|
|
75
|
+
marker.className = "cf-hl-marker";
|
|
76
|
+
marker.style.cssText = `position:fixed;left:${rect.left}px;top:${rect.top}px;width:${rect.width}px;height:${rect.height}px;border:2px solid #ae2a19;background:rgba(174,42,25,0.12);z-index:100001;pointer-events:none;border-radius:3px;`;
|
|
77
|
+
const badge = document.createElement("div");
|
|
78
|
+
badge.style.cssText = "position:absolute;top:-10px;right:-10px;background:#ae2a19;color:#fff;width:20px;height:20px;border-radius:50%;font-size:11px;display:flex;align-items:center;justify-content:center;font-family:system-ui;font-weight:600;";
|
|
79
|
+
badge.textContent = String(highlights.length);
|
|
80
|
+
marker.appendChild(badge);
|
|
81
|
+
overlay?.appendChild(marker);
|
|
82
|
+
const doneBtn = document.getElementById("cf-hl-done");
|
|
83
|
+
if (doneBtn) doneBtn.textContent = `Done (${highlights.length})`;
|
|
84
|
+
}
|
|
85
|
+
function finishHighlighting() {
|
|
86
|
+
const annotations = highlights.map((h) => ({
|
|
87
|
+
selector: getSelector(h.el),
|
|
88
|
+
tagName: h.el.tagName.toLowerCase(),
|
|
89
|
+
text: h.el.textContent?.slice(0, 100) || void 0,
|
|
90
|
+
rect: { x: Math.round(h.rect.x), y: Math.round(h.rect.y), width: Math.round(h.rect.width), height: Math.round(h.rect.height) },
|
|
91
|
+
note: h.note
|
|
92
|
+
}));
|
|
93
|
+
cleanup();
|
|
94
|
+
onDoneCallback?.(annotations);
|
|
95
|
+
}
|
|
96
|
+
function cancelHighlighting() {
|
|
97
|
+
cleanup();
|
|
98
|
+
onDoneCallback?.([]);
|
|
99
|
+
}
|
|
100
|
+
function cleanup() {
|
|
101
|
+
if (hoverBox) {
|
|
102
|
+
hoverBox.remove();
|
|
103
|
+
hoverBox = null;
|
|
104
|
+
}
|
|
105
|
+
if (overlay) {
|
|
106
|
+
overlay.remove();
|
|
107
|
+
overlay = null;
|
|
108
|
+
}
|
|
109
|
+
highlights = [];
|
|
110
|
+
isActive = false;
|
|
111
|
+
}
|
|
112
|
+
function startHighlighting() {
|
|
113
|
+
return new Promise((resolve) => {
|
|
114
|
+
if (isActive) {
|
|
115
|
+
resolve([]);
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
isActive = true;
|
|
119
|
+
highlights = [];
|
|
120
|
+
onDoneCallback = resolve;
|
|
121
|
+
createOverlay();
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
function isHighlighting() {
|
|
125
|
+
return isActive;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export {
|
|
129
|
+
startHighlighting,
|
|
130
|
+
isHighlighting
|
|
131
|
+
};
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
// src/screenshot.ts
|
|
2
|
+
var screenshotData = null;
|
|
3
|
+
async function captureScreenshot() {
|
|
4
|
+
try {
|
|
5
|
+
if (typeof window.html2canvas === "function") {
|
|
6
|
+
const canvas2 = await window.html2canvas(document.body, {
|
|
7
|
+
useCORS: true,
|
|
8
|
+
allowTaint: true,
|
|
9
|
+
scale: Math.min(window.devicePixelRatio, 2),
|
|
10
|
+
logging: false,
|
|
11
|
+
width: window.innerWidth,
|
|
12
|
+
height: window.innerHeight,
|
|
13
|
+
x: window.scrollX,
|
|
14
|
+
y: window.scrollY
|
|
15
|
+
});
|
|
16
|
+
screenshotData = canvas2.toDataURL("image/png", 0.8);
|
|
17
|
+
return screenshotData;
|
|
18
|
+
}
|
|
19
|
+
const canvas = document.createElement("canvas");
|
|
20
|
+
const ctx = canvas.getContext("2d");
|
|
21
|
+
if (!ctx) return null;
|
|
22
|
+
canvas.width = window.innerWidth;
|
|
23
|
+
canvas.height = window.innerHeight;
|
|
24
|
+
ctx.fillStyle = "#ffffff";
|
|
25
|
+
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
26
|
+
ctx.fillStyle = "#333333";
|
|
27
|
+
ctx.font = "14px system-ui";
|
|
28
|
+
ctx.fillText(`Page: ${window.location.href}`, 10, 30);
|
|
29
|
+
ctx.fillText(`Viewport: ${window.innerWidth}x${window.innerHeight}`, 10, 50);
|
|
30
|
+
ctx.fillText(`Screenshot captured at ${(/* @__PURE__ */ new Date()).toISOString()}`, 10, 70);
|
|
31
|
+
const svgData = `
|
|
32
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="${window.innerWidth}" height="${window.innerHeight}">
|
|
33
|
+
<foreignObject width="100%" height="100%">
|
|
34
|
+
<div xmlns="http://www.w3.org/1999/xhtml">
|
|
35
|
+
${document.documentElement.outerHTML}
|
|
36
|
+
</div>
|
|
37
|
+
</foreignObject>
|
|
38
|
+
</svg>`;
|
|
39
|
+
try {
|
|
40
|
+
const blob = new Blob([svgData], { type: "image/svg+xml;charset=utf-8" });
|
|
41
|
+
const url = URL.createObjectURL(blob);
|
|
42
|
+
const img = new Image();
|
|
43
|
+
await new Promise((resolve, reject) => {
|
|
44
|
+
img.onload = () => resolve();
|
|
45
|
+
img.onerror = () => reject();
|
|
46
|
+
img.src = url;
|
|
47
|
+
});
|
|
48
|
+
ctx.drawImage(img, 0, 0);
|
|
49
|
+
URL.revokeObjectURL(url);
|
|
50
|
+
} catch {
|
|
51
|
+
}
|
|
52
|
+
screenshotData = canvas.toDataURL("image/png", 0.8);
|
|
53
|
+
return screenshotData;
|
|
54
|
+
} catch {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
function getLastScreenshot() {
|
|
59
|
+
return screenshotData;
|
|
60
|
+
}
|
|
61
|
+
function clearScreenshot() {
|
|
62
|
+
screenshotData = null;
|
|
63
|
+
}
|
|
64
|
+
function loadHtml2Canvas() {
|
|
65
|
+
return new Promise((resolve, reject) => {
|
|
66
|
+
if (typeof window.html2canvas === "function") {
|
|
67
|
+
resolve();
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
const script = document.createElement("script");
|
|
71
|
+
script.src = "https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js";
|
|
72
|
+
script.onload = () => resolve();
|
|
73
|
+
script.onerror = () => reject(new Error("Failed to load html2canvas"));
|
|
74
|
+
document.head.appendChild(script);
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export {
|
|
79
|
+
captureScreenshot,
|
|
80
|
+
getLastScreenshot,
|
|
81
|
+
clearScreenshot,
|
|
82
|
+
loadHtml2Canvas
|
|
83
|
+
};
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
interface CheckflowConfig {
|
|
2
|
+
apiKey: string;
|
|
3
|
+
endpoint?: string;
|
|
4
|
+
projectId?: string;
|
|
5
|
+
environment?: string;
|
|
6
|
+
enabled?: boolean;
|
|
7
|
+
widget?: WidgetConfig;
|
|
8
|
+
/** Auto-load html2canvas for screenshot support (default: true) */
|
|
9
|
+
enableScreenshots?: boolean;
|
|
10
|
+
}
|
|
11
|
+
interface WidgetConfig {
|
|
12
|
+
position?: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left';
|
|
13
|
+
color?: string;
|
|
14
|
+
text?: string;
|
|
15
|
+
showOnInit?: boolean;
|
|
16
|
+
}
|
|
17
|
+
interface FeedbackPayload {
|
|
18
|
+
title: string;
|
|
19
|
+
description?: string;
|
|
20
|
+
type?: 'BUG' | 'FEATURE' | 'IMPROVEMENT' | 'QUESTION';
|
|
21
|
+
priority?: 'LOW' | 'MEDIUM' | 'HIGH' | 'CRITICAL';
|
|
22
|
+
url?: string;
|
|
23
|
+
environment?: string;
|
|
24
|
+
viewport?: {
|
|
25
|
+
width: number;
|
|
26
|
+
height: number;
|
|
27
|
+
device: string;
|
|
28
|
+
};
|
|
29
|
+
user_agent?: string;
|
|
30
|
+
browser?: string;
|
|
31
|
+
os?: string;
|
|
32
|
+
locale?: string;
|
|
33
|
+
timezone?: string;
|
|
34
|
+
console_logs?: any[];
|
|
35
|
+
network_logs?: any[];
|
|
36
|
+
performance_metrics?: Record<string, number>;
|
|
37
|
+
javascript_errors?: any[];
|
|
38
|
+
screenshot_url?: string;
|
|
39
|
+
sdk_version?: string;
|
|
40
|
+
}
|
|
41
|
+
interface FeedbackResponse {
|
|
42
|
+
success: boolean;
|
|
43
|
+
data?: {
|
|
44
|
+
id: string;
|
|
45
|
+
short_id: string;
|
|
46
|
+
};
|
|
47
|
+
error?: {
|
|
48
|
+
message: string;
|
|
49
|
+
code: string;
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Element highlighter for visual bug reporting.
|
|
55
|
+
* Allows users to click on elements to highlight them and annotate.
|
|
56
|
+
*/
|
|
57
|
+
interface HighlightAnnotation {
|
|
58
|
+
selector: string;
|
|
59
|
+
tagName: string;
|
|
60
|
+
text?: string;
|
|
61
|
+
rect: {
|
|
62
|
+
x: number;
|
|
63
|
+
y: number;
|
|
64
|
+
width: number;
|
|
65
|
+
height: number;
|
|
66
|
+
};
|
|
67
|
+
note?: string;
|
|
68
|
+
}
|
|
69
|
+
declare function startHighlighting(): Promise<HighlightAnnotation[]>;
|
|
70
|
+
|
|
71
|
+
export { type CheckflowConfig as C, type FeedbackPayload as F, type HighlightAnnotation as H, type WidgetConfig as W, type FeedbackResponse as a, startHighlighting as s };
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
interface CheckflowConfig {
|
|
2
|
+
apiKey: string;
|
|
3
|
+
endpoint?: string;
|
|
4
|
+
projectId?: string;
|
|
5
|
+
environment?: string;
|
|
6
|
+
enabled?: boolean;
|
|
7
|
+
widget?: WidgetConfig;
|
|
8
|
+
/** Auto-load html2canvas for screenshot support (default: true) */
|
|
9
|
+
enableScreenshots?: boolean;
|
|
10
|
+
}
|
|
11
|
+
interface WidgetConfig {
|
|
12
|
+
position?: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left';
|
|
13
|
+
color?: string;
|
|
14
|
+
text?: string;
|
|
15
|
+
showOnInit?: boolean;
|
|
16
|
+
}
|
|
17
|
+
interface FeedbackPayload {
|
|
18
|
+
title: string;
|
|
19
|
+
description?: string;
|
|
20
|
+
type?: 'BUG' | 'FEATURE' | 'IMPROVEMENT' | 'QUESTION';
|
|
21
|
+
priority?: 'LOW' | 'MEDIUM' | 'HIGH' | 'CRITICAL';
|
|
22
|
+
url?: string;
|
|
23
|
+
environment?: string;
|
|
24
|
+
viewport?: {
|
|
25
|
+
width: number;
|
|
26
|
+
height: number;
|
|
27
|
+
device: string;
|
|
28
|
+
};
|
|
29
|
+
user_agent?: string;
|
|
30
|
+
browser?: string;
|
|
31
|
+
os?: string;
|
|
32
|
+
locale?: string;
|
|
33
|
+
timezone?: string;
|
|
34
|
+
console_logs?: any[];
|
|
35
|
+
network_logs?: any[];
|
|
36
|
+
performance_metrics?: Record<string, number>;
|
|
37
|
+
javascript_errors?: any[];
|
|
38
|
+
screenshot_url?: string;
|
|
39
|
+
sdk_version?: string;
|
|
40
|
+
}
|
|
41
|
+
interface FeedbackResponse {
|
|
42
|
+
success: boolean;
|
|
43
|
+
data?: {
|
|
44
|
+
id: string;
|
|
45
|
+
short_id: string;
|
|
46
|
+
};
|
|
47
|
+
error?: {
|
|
48
|
+
message: string;
|
|
49
|
+
code: string;
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Element highlighter for visual bug reporting.
|
|
55
|
+
* Allows users to click on elements to highlight them and annotate.
|
|
56
|
+
*/
|
|
57
|
+
interface HighlightAnnotation {
|
|
58
|
+
selector: string;
|
|
59
|
+
tagName: string;
|
|
60
|
+
text?: string;
|
|
61
|
+
rect: {
|
|
62
|
+
x: number;
|
|
63
|
+
y: number;
|
|
64
|
+
width: number;
|
|
65
|
+
height: number;
|
|
66
|
+
};
|
|
67
|
+
note?: string;
|
|
68
|
+
}
|
|
69
|
+
declare function startHighlighting(): Promise<HighlightAnnotation[]>;
|
|
70
|
+
|
|
71
|
+
export { type CheckflowConfig as C, type FeedbackPayload as F, type HighlightAnnotation as H, type WidgetConfig as W, type FeedbackResponse as a, startHighlighting as s };
|