@checkflow/sdk 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +265 -0
- package/dist/analytics-tracker.d.ts +107 -0
- package/dist/annotation/editor.d.ts +72 -0
- package/dist/annotation/index.d.ts +9 -0
- package/dist/annotation/styles.d.ts +6 -0
- package/dist/annotation/toolbar.d.ts +32 -0
- package/dist/annotation/types.d.ts +85 -0
- package/dist/api-client.d.ts +78 -0
- package/dist/checkflow.css +1 -0
- package/dist/checkflow.d.ts +112 -0
- package/dist/context-capture.d.ts +42 -0
- package/dist/error-capture.d.ts +60 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.esm.js +4576 -0
- package/dist/index.esm.js.map +1 -0
- package/dist/index.js +4613 -0
- package/dist/index.js.map +1 -0
- package/dist/privacy/detector.d.ts +56 -0
- package/dist/privacy/index.d.ts +8 -0
- package/dist/privacy/masker.d.ts +43 -0
- package/dist/privacy/types.d.ts +54 -0
- package/dist/react/index.d.ts +77 -0
- package/dist/session-recording.d.ts +74 -0
- package/dist/types.d.ts +299 -0
- package/dist/vue/index.d.ts +55 -0
- package/dist/widget/Widget.d.ts +98 -0
- package/dist/widget/index.d.ts +2 -0
- package/package.json +69 -0
package/README.md
ADDED
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
# @checkflow/sdk
|
|
2
|
+
|
|
3
|
+
> User feedback collection SDK with automatic context capture
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@checkflow/sdk)
|
|
6
|
+
[](https://opensource.org/licenses/MIT)
|
|
7
|
+
|
|
8
|
+
## Features
|
|
9
|
+
|
|
10
|
+
- 📸 **Automatic Screenshot Capture** - Capture page screenshots with one click
|
|
11
|
+
- 🔍 **Context Collection** - Automatically collect console logs, network requests, and performance metrics
|
|
12
|
+
- 🎨 **Customizable Widget** - Beautiful feedback widget that matches your brand
|
|
13
|
+
- ⚛️ **React Support** - First-class React hooks and components
|
|
14
|
+
- 💚 **Vue Support** - Vue 3 composables and plugin
|
|
15
|
+
- 🛡️ **Error Boundary** - Automatic error capture and reporting
|
|
16
|
+
- 🌍 **i18n Ready** - Built-in translations (EN, FR, ES, DE)
|
|
17
|
+
- 📱 **Responsive** - Works on desktop, tablet, and mobile
|
|
18
|
+
|
|
19
|
+
## Installation
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npm install @checkflow/sdk
|
|
23
|
+
# or
|
|
24
|
+
yarn add @checkflow/sdk
|
|
25
|
+
# or
|
|
26
|
+
pnpm add @checkflow/sdk
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Quick Start
|
|
30
|
+
|
|
31
|
+
### Vanilla JavaScript
|
|
32
|
+
|
|
33
|
+
```javascript
|
|
34
|
+
import { createCheckFlow } from '@checkflow/sdk';
|
|
35
|
+
|
|
36
|
+
const checkflow = createCheckFlow('your-api-key', {
|
|
37
|
+
projectId: 'your-project-id',
|
|
38
|
+
showWidget: true,
|
|
39
|
+
captureErrors: true,
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
// Programmatically open the feedback form
|
|
43
|
+
checkflow.open();
|
|
44
|
+
|
|
45
|
+
// Submit feedback programmatically
|
|
46
|
+
await checkflow.submitFeedback({
|
|
47
|
+
title: 'Bug Report',
|
|
48
|
+
description: 'Something is broken',
|
|
49
|
+
type: 'bug',
|
|
50
|
+
priority: 'high',
|
|
51
|
+
});
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### React
|
|
55
|
+
|
|
56
|
+
```tsx
|
|
57
|
+
import { CheckFlowProvider, useCheckFlow, FeedbackButton } from '@checkflow/sdk';
|
|
58
|
+
|
|
59
|
+
// Wrap your app
|
|
60
|
+
function App() {
|
|
61
|
+
return (
|
|
62
|
+
<CheckFlowProvider
|
|
63
|
+
apiKey="your-api-key"
|
|
64
|
+
options={{ projectId: 'your-project-id' }}
|
|
65
|
+
>
|
|
66
|
+
<YourApp />
|
|
67
|
+
</CheckFlowProvider>
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Use the hook
|
|
72
|
+
function MyComponent() {
|
|
73
|
+
const { openWidget, submitFeedback } = useCheckFlow();
|
|
74
|
+
|
|
75
|
+
return (
|
|
76
|
+
<button onClick={openWidget}>
|
|
77
|
+
Send Feedback
|
|
78
|
+
</button>
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Or use the built-in button
|
|
83
|
+
function AnotherComponent() {
|
|
84
|
+
return <FeedbackButton>Report Issue</FeedbackButton>;
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Vue 3
|
|
89
|
+
|
|
90
|
+
```typescript
|
|
91
|
+
import { createApp } from 'vue';
|
|
92
|
+
import { CheckFlowPlugin } from '@checkflow/sdk';
|
|
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');
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
```vue
|
|
105
|
+
<script setup>
|
|
106
|
+
import { useCheckFlow, useFeedbackForm } from '@checkflow/sdk';
|
|
107
|
+
|
|
108
|
+
const { openWidget } = useCheckFlow();
|
|
109
|
+
const { formState, submit, isSubmitting } = useFeedbackForm();
|
|
110
|
+
</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
|
+
```
|
|
160
|
+
|
|
161
|
+
## Error Boundary (React)
|
|
162
|
+
|
|
163
|
+
```tsx
|
|
164
|
+
import { CheckFlowErrorBoundary } from '@checkflow/sdk';
|
|
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
|
+
```
|
|
209
|
+
|
|
210
|
+
## User Identification
|
|
211
|
+
|
|
212
|
+
```typescript
|
|
213
|
+
// Set user info
|
|
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
|
+
```
|
|
223
|
+
|
|
224
|
+
## CDN Usage
|
|
225
|
+
|
|
226
|
+
```html
|
|
227
|
+
<script src="https://unpkg.com/@checkflow/sdk/dist/checkflow.umd.js"></script>
|
|
228
|
+
<link rel="stylesheet" href="https://unpkg.com/@checkflow/sdk/dist/checkflow.css">
|
|
229
|
+
|
|
230
|
+
<script>
|
|
231
|
+
const checkflow = new CheckFlow('your-api-key', {
|
|
232
|
+
projectId: 'your-project-id',
|
|
233
|
+
}).init();
|
|
234
|
+
</script>
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
## TypeScript
|
|
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
|
|
257
|
+
|
|
258
|
+
- Chrome 80+
|
|
259
|
+
- Firefox 75+
|
|
260
|
+
- Safari 13+
|
|
261
|
+
- Edge 80+
|
|
262
|
+
|
|
263
|
+
## License
|
|
264
|
+
|
|
265
|
+
MIT © CheckFlow
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Analytics Tracker for CheckFlow SDK
|
|
3
|
+
* Automatically tracks user sessions and interactions for analytics
|
|
4
|
+
*/
|
|
5
|
+
import { APIClient } from './api-client';
|
|
6
|
+
export interface SessionData {
|
|
7
|
+
sessionId: string;
|
|
8
|
+
projectId: string;
|
|
9
|
+
userFingerprint?: string;
|
|
10
|
+
ipAddress?: string;
|
|
11
|
+
country?: string;
|
|
12
|
+
region?: string;
|
|
13
|
+
city?: string;
|
|
14
|
+
latitude?: number;
|
|
15
|
+
longitude?: number;
|
|
16
|
+
userAgent?: string;
|
|
17
|
+
viewport?: ViewportInfo;
|
|
18
|
+
browser?: string;
|
|
19
|
+
os?: string;
|
|
20
|
+
locale?: string;
|
|
21
|
+
timezone?: string;
|
|
22
|
+
entryUrl?: string;
|
|
23
|
+
referrer?: string;
|
|
24
|
+
}
|
|
25
|
+
export interface ViewportInfo {
|
|
26
|
+
width: number;
|
|
27
|
+
height: number;
|
|
28
|
+
device: 'desktop' | 'mobile' | 'tablet';
|
|
29
|
+
}
|
|
30
|
+
export interface InteractionEvent {
|
|
31
|
+
pageUrl: string;
|
|
32
|
+
pageTitle?: string;
|
|
33
|
+
eventType: 'page_view' | 'click' | 'scroll' | 'form_interaction' | 'error';
|
|
34
|
+
elementSelector?: string;
|
|
35
|
+
elementText?: string;
|
|
36
|
+
mouseX?: number;
|
|
37
|
+
mouseY?: number;
|
|
38
|
+
scrollDepth?: number;
|
|
39
|
+
loadTime?: number;
|
|
40
|
+
domReadyTime?: number;
|
|
41
|
+
errorMessage?: string;
|
|
42
|
+
errorStack?: string;
|
|
43
|
+
}
|
|
44
|
+
export interface AnalyticsOptions {
|
|
45
|
+
batchSize: number;
|
|
46
|
+
batchTimeout: number;
|
|
47
|
+
trackClicks: boolean;
|
|
48
|
+
trackScrolling: boolean;
|
|
49
|
+
trackFormInteractions: boolean;
|
|
50
|
+
trackErrors: boolean;
|
|
51
|
+
trackPerformance: boolean;
|
|
52
|
+
throttleScrollMs: number;
|
|
53
|
+
maxScrollEvents: number;
|
|
54
|
+
debug: boolean;
|
|
55
|
+
}
|
|
56
|
+
export declare class AnalyticsTracker {
|
|
57
|
+
private apiClient;
|
|
58
|
+
private options;
|
|
59
|
+
private sessionId;
|
|
60
|
+
private projectId;
|
|
61
|
+
private userFingerprint;
|
|
62
|
+
private isActive;
|
|
63
|
+
private sessionStartTime;
|
|
64
|
+
private currentPageUrl;
|
|
65
|
+
private pageLoadStart;
|
|
66
|
+
private interactionBuffer;
|
|
67
|
+
private batchTimer;
|
|
68
|
+
private scrollEvents;
|
|
69
|
+
private lastScrollTime;
|
|
70
|
+
private isFirstPageView;
|
|
71
|
+
private performanceObserver?;
|
|
72
|
+
constructor(apiClient: APIClient, options?: Partial<AnalyticsOptions>);
|
|
73
|
+
startTracking(): Promise<void>;
|
|
74
|
+
stopTracking(): Promise<void>;
|
|
75
|
+
private createSession;
|
|
76
|
+
private updateSessionEnd;
|
|
77
|
+
private setupEventListeners;
|
|
78
|
+
private removeEventListeners;
|
|
79
|
+
private handleClick;
|
|
80
|
+
private handleScroll;
|
|
81
|
+
private handleFormFocus;
|
|
82
|
+
private handleFormChange;
|
|
83
|
+
private handleError;
|
|
84
|
+
private handleUnhandledRejection;
|
|
85
|
+
private handlePageUnload;
|
|
86
|
+
private handleVisibilityChange;
|
|
87
|
+
private setupSPATracking;
|
|
88
|
+
private setupPerformanceTracking;
|
|
89
|
+
private trackPageView;
|
|
90
|
+
private trackPageLoadPerformance;
|
|
91
|
+
private queueInteraction;
|
|
92
|
+
private flushInteractions;
|
|
93
|
+
private generateSessionId;
|
|
94
|
+
private generateUserFingerprint;
|
|
95
|
+
private getGeolocation;
|
|
96
|
+
private getViewportInfo;
|
|
97
|
+
private getBrowserInfo;
|
|
98
|
+
private getOSInfo;
|
|
99
|
+
private getElementSelector;
|
|
100
|
+
private getElementText;
|
|
101
|
+
private isFormElement;
|
|
102
|
+
private calculateScrollDepth;
|
|
103
|
+
private getPageLoadTime;
|
|
104
|
+
private getDOMReadyTime;
|
|
105
|
+
private getPageViewCount;
|
|
106
|
+
private log;
|
|
107
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CheckFlow Annotation Editor
|
|
3
|
+
* Figma-like canvas editor for annotating screenshots
|
|
4
|
+
*/
|
|
5
|
+
import { Annotation, AnnotationEditorConfig } from './types';
|
|
6
|
+
export declare class AnnotationEditor {
|
|
7
|
+
private container;
|
|
8
|
+
private canvas;
|
|
9
|
+
private ctx;
|
|
10
|
+
private toolbar;
|
|
11
|
+
private config;
|
|
12
|
+
private annotations;
|
|
13
|
+
private backgroundImage;
|
|
14
|
+
private state;
|
|
15
|
+
private startPoint;
|
|
16
|
+
private freehandPoints;
|
|
17
|
+
private textInput;
|
|
18
|
+
constructor(config: AnnotationEditorConfig);
|
|
19
|
+
/**
|
|
20
|
+
* Open the annotation editor with a screenshot
|
|
21
|
+
*/
|
|
22
|
+
open(screenshotDataUrl: string): Promise<void>;
|
|
23
|
+
/**
|
|
24
|
+
* Close the editor
|
|
25
|
+
*/
|
|
26
|
+
close(): void;
|
|
27
|
+
/**
|
|
28
|
+
* Get the annotated image as data URL
|
|
29
|
+
*/
|
|
30
|
+
getAnnotatedImage(): string;
|
|
31
|
+
/**
|
|
32
|
+
* Get annotations data
|
|
33
|
+
*/
|
|
34
|
+
getAnnotations(): Annotation[];
|
|
35
|
+
private loadImage;
|
|
36
|
+
private createEditorUI;
|
|
37
|
+
private setupEventListeners;
|
|
38
|
+
private handleMouseDown;
|
|
39
|
+
private handleMouseMove;
|
|
40
|
+
private handleMouseUp;
|
|
41
|
+
private handleTouchStart;
|
|
42
|
+
private handleTouchMove;
|
|
43
|
+
private handleTouchEnd;
|
|
44
|
+
private handleKeyDown;
|
|
45
|
+
private getCanvasPoint;
|
|
46
|
+
private getCanvasPointFromTouch;
|
|
47
|
+
private startDrawing;
|
|
48
|
+
private continueDrawing;
|
|
49
|
+
private finishDrawing;
|
|
50
|
+
private isValidAnnotation;
|
|
51
|
+
private createAnnotation;
|
|
52
|
+
private createBounds;
|
|
53
|
+
private showTextInput;
|
|
54
|
+
private generateId;
|
|
55
|
+
private setActiveTool;
|
|
56
|
+
private setStyle;
|
|
57
|
+
private undo;
|
|
58
|
+
private clearAll;
|
|
59
|
+
private save;
|
|
60
|
+
private cancel;
|
|
61
|
+
private render;
|
|
62
|
+
private drawAnnotations;
|
|
63
|
+
private drawAnnotation;
|
|
64
|
+
private drawRectangle;
|
|
65
|
+
private drawEllipse;
|
|
66
|
+
private drawArrow;
|
|
67
|
+
private drawLine;
|
|
68
|
+
private drawHighlight;
|
|
69
|
+
private drawBlur;
|
|
70
|
+
private drawText;
|
|
71
|
+
private drawFreehand;
|
|
72
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CheckFlow Annotation Module
|
|
3
|
+
* Figma-like annotation tools for feedback screenshots
|
|
4
|
+
*/
|
|
5
|
+
export { AnnotationEditor } from './editor';
|
|
6
|
+
export { AnnotationToolbar } from './toolbar';
|
|
7
|
+
export { injectAnnotationStyles, removeAnnotationStyles } from './styles';
|
|
8
|
+
export type { AnnotationToolType, Point, Bounds, AnnotationStyle, Annotation, RectangleAnnotation, EllipseAnnotation, ArrowAnnotation, LineAnnotation, HighlightAnnotation, BlurAnnotation, TextAnnotation, FreehandAnnotation, AnnotationEditorConfig, ToolState, } from './types';
|
|
9
|
+
export { DEFAULT_STYLE, HIGHLIGHT_STYLE, BLUR_STYLE, COLOR_PALETTE, STROKE_WIDTHS, } from './types';
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CheckFlow Annotation Toolbar
|
|
3
|
+
* Figma-like floating toolbar for annotation tools
|
|
4
|
+
*/
|
|
5
|
+
import { AnnotationToolType, AnnotationStyle } from './types';
|
|
6
|
+
export interface ToolbarConfig {
|
|
7
|
+
tools: AnnotationToolType[];
|
|
8
|
+
activeTool: AnnotationToolType;
|
|
9
|
+
style: AnnotationStyle;
|
|
10
|
+
onToolChange: (tool: AnnotationToolType) => void;
|
|
11
|
+
onStyleChange: (style: Partial<AnnotationStyle>) => void;
|
|
12
|
+
onUndo: () => void;
|
|
13
|
+
onClear: () => void;
|
|
14
|
+
onSave: () => void;
|
|
15
|
+
onCancel: () => void;
|
|
16
|
+
}
|
|
17
|
+
export declare class AnnotationToolbar {
|
|
18
|
+
private element;
|
|
19
|
+
private config;
|
|
20
|
+
private colorPicker;
|
|
21
|
+
private strokePicker;
|
|
22
|
+
constructor(config: ToolbarConfig);
|
|
23
|
+
getElement(): HTMLElement;
|
|
24
|
+
setActiveTool(tool: AnnotationToolType): void;
|
|
25
|
+
setStyle(style: AnnotationStyle): void;
|
|
26
|
+
private createToolbar;
|
|
27
|
+
private createToolButton;
|
|
28
|
+
private updateActiveState;
|
|
29
|
+
private updateStyleDisplay;
|
|
30
|
+
private toggleColorPicker;
|
|
31
|
+
private toggleStrokePicker;
|
|
32
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CheckFlow Annotation System Types
|
|
3
|
+
* Figma-like annotation tools for feedback screenshots
|
|
4
|
+
*/
|
|
5
|
+
export type AnnotationToolType = 'select' | 'rectangle' | 'ellipse' | 'arrow' | 'line' | 'highlight' | 'blur' | 'text' | 'freehand';
|
|
6
|
+
export interface Point {
|
|
7
|
+
x: number;
|
|
8
|
+
y: number;
|
|
9
|
+
}
|
|
10
|
+
export interface Bounds {
|
|
11
|
+
x: number;
|
|
12
|
+
y: number;
|
|
13
|
+
width: number;
|
|
14
|
+
height: number;
|
|
15
|
+
}
|
|
16
|
+
export interface AnnotationStyle {
|
|
17
|
+
strokeColor: string;
|
|
18
|
+
strokeWidth: number;
|
|
19
|
+
fillColor: string;
|
|
20
|
+
opacity: number;
|
|
21
|
+
fontSize?: number;
|
|
22
|
+
fontFamily?: string;
|
|
23
|
+
}
|
|
24
|
+
export interface BaseAnnotation {
|
|
25
|
+
id: string;
|
|
26
|
+
type: AnnotationToolType;
|
|
27
|
+
style: AnnotationStyle;
|
|
28
|
+
timestamp: number;
|
|
29
|
+
}
|
|
30
|
+
export interface RectangleAnnotation extends BaseAnnotation {
|
|
31
|
+
type: 'rectangle';
|
|
32
|
+
bounds: Bounds;
|
|
33
|
+
}
|
|
34
|
+
export interface EllipseAnnotation extends BaseAnnotation {
|
|
35
|
+
type: 'ellipse';
|
|
36
|
+
bounds: Bounds;
|
|
37
|
+
}
|
|
38
|
+
export interface ArrowAnnotation extends BaseAnnotation {
|
|
39
|
+
type: 'arrow';
|
|
40
|
+
start: Point;
|
|
41
|
+
end: Point;
|
|
42
|
+
}
|
|
43
|
+
export interface LineAnnotation extends BaseAnnotation {
|
|
44
|
+
type: 'line';
|
|
45
|
+
start: Point;
|
|
46
|
+
end: Point;
|
|
47
|
+
}
|
|
48
|
+
export interface HighlightAnnotation extends BaseAnnotation {
|
|
49
|
+
type: 'highlight';
|
|
50
|
+
bounds: Bounds;
|
|
51
|
+
}
|
|
52
|
+
export interface BlurAnnotation extends BaseAnnotation {
|
|
53
|
+
type: 'blur';
|
|
54
|
+
bounds: Bounds;
|
|
55
|
+
blurAmount: number;
|
|
56
|
+
}
|
|
57
|
+
export interface TextAnnotation extends BaseAnnotation {
|
|
58
|
+
type: 'text';
|
|
59
|
+
position: Point;
|
|
60
|
+
text: string;
|
|
61
|
+
}
|
|
62
|
+
export interface FreehandAnnotation extends BaseAnnotation {
|
|
63
|
+
type: 'freehand';
|
|
64
|
+
points: Point[];
|
|
65
|
+
}
|
|
66
|
+
export type Annotation = RectangleAnnotation | EllipseAnnotation | ArrowAnnotation | LineAnnotation | HighlightAnnotation | BlurAnnotation | TextAnnotation | FreehandAnnotation;
|
|
67
|
+
export interface AnnotationEditorConfig {
|
|
68
|
+
tools: AnnotationToolType[];
|
|
69
|
+
defaultStyle: AnnotationStyle;
|
|
70
|
+
canvasWidth?: number;
|
|
71
|
+
canvasHeight?: number;
|
|
72
|
+
onSave?: (annotations: Annotation[], imageData: string) => void;
|
|
73
|
+
onCancel?: () => void;
|
|
74
|
+
}
|
|
75
|
+
export interface ToolState {
|
|
76
|
+
activeTool: AnnotationToolType;
|
|
77
|
+
style: AnnotationStyle;
|
|
78
|
+
isDrawing: boolean;
|
|
79
|
+
currentAnnotation: Annotation | null;
|
|
80
|
+
}
|
|
81
|
+
export declare const DEFAULT_STYLE: AnnotationStyle;
|
|
82
|
+
export declare const HIGHLIGHT_STYLE: Partial<AnnotationStyle>;
|
|
83
|
+
export declare const BLUR_STYLE: Partial<AnnotationStyle>;
|
|
84
|
+
export declare const COLOR_PALETTE: string[];
|
|
85
|
+
export declare const STROKE_WIDTHS: number[];
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CheckFlow API Client
|
|
3
|
+
* Handles all communication with the CheckFlow backend
|
|
4
|
+
*/
|
|
5
|
+
import { APIResponse, FeedbackData, SubmitResult, CaptureResult, UserInfo } from './types';
|
|
6
|
+
export interface APIClientOptions {
|
|
7
|
+
apiUrl: string;
|
|
8
|
+
apiKey: string;
|
|
9
|
+
projectId?: string;
|
|
10
|
+
timeout?: number;
|
|
11
|
+
debug?: boolean;
|
|
12
|
+
}
|
|
13
|
+
export declare class APIClient {
|
|
14
|
+
private apiUrl;
|
|
15
|
+
private apiKey;
|
|
16
|
+
private projectId?;
|
|
17
|
+
private timeout;
|
|
18
|
+
private debug;
|
|
19
|
+
constructor(options: APIClientOptions);
|
|
20
|
+
getProjectId(): string | undefined;
|
|
21
|
+
getBaseUrl(): string;
|
|
22
|
+
private log;
|
|
23
|
+
get<T>(endpoint: string, headers?: Record<string, string>): Promise<APIResponse<T>>;
|
|
24
|
+
post<T>(endpoint: string, data?: any, headers?: Record<string, string>): Promise<APIResponse<T>>;
|
|
25
|
+
put<T>(endpoint: string, data?: any, headers?: Record<string, string>): Promise<APIResponse<T>>;
|
|
26
|
+
private request;
|
|
27
|
+
/**
|
|
28
|
+
* Submit feedback to the backend
|
|
29
|
+
*/
|
|
30
|
+
submitFeedback(feedback: FeedbackData, capture?: CaptureResult, user?: UserInfo, sessionRecording?: {
|
|
31
|
+
events: any[];
|
|
32
|
+
sessionId: string;
|
|
33
|
+
}, annotations?: any[]): Promise<SubmitResult>;
|
|
34
|
+
/**
|
|
35
|
+
* Upload screenshot as attachment
|
|
36
|
+
*/
|
|
37
|
+
uploadScreenshot(feedbackId: string, base64Image: string): Promise<APIResponse>;
|
|
38
|
+
/**
|
|
39
|
+
* Send SDK capture data to backend for processing
|
|
40
|
+
* Aligns with backend SDKCapturePayload schema
|
|
41
|
+
*/
|
|
42
|
+
sendCapture(capture: CaptureResult, sessionId?: string): Promise<APIResponse>;
|
|
43
|
+
/**
|
|
44
|
+
* Upload just a screenshot (simpler endpoint)
|
|
45
|
+
*/
|
|
46
|
+
uploadScreenshotOnly(screenshotBase64: string, url: string): Promise<APIResponse>;
|
|
47
|
+
/**
|
|
48
|
+
* Save session recording to backend
|
|
49
|
+
* Aligns with backend /sdk/recording endpoint
|
|
50
|
+
*/
|
|
51
|
+
saveSessionRecording(recording: {
|
|
52
|
+
events: any[];
|
|
53
|
+
sessionId: string;
|
|
54
|
+
startUrl: string;
|
|
55
|
+
endUrl?: string;
|
|
56
|
+
durationSeconds: number;
|
|
57
|
+
}): Promise<APIResponse>;
|
|
58
|
+
/**
|
|
59
|
+
* Report an error to the backend
|
|
60
|
+
*/
|
|
61
|
+
reportError(error: {
|
|
62
|
+
message: string;
|
|
63
|
+
stack?: string;
|
|
64
|
+
type: string;
|
|
65
|
+
filename?: string;
|
|
66
|
+
lineno?: number;
|
|
67
|
+
colno?: number;
|
|
68
|
+
context?: any;
|
|
69
|
+
}): Promise<APIResponse>;
|
|
70
|
+
/**
|
|
71
|
+
* Check API health
|
|
72
|
+
*/
|
|
73
|
+
healthCheck(): Promise<boolean>;
|
|
74
|
+
/**
|
|
75
|
+
* Update project ID
|
|
76
|
+
*/
|
|
77
|
+
setProjectId(projectId: string): void;
|
|
78
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.checkflow-widget{--cf-bg:#fff;--cf-bg-elevated:#fafafa;--cf-bg-hover:#f5f5f5;--cf-text:#18181b;--cf-text-secondary:#71717a;--cf-text-muted:#a1a1aa;--cf-border:#e4e4e7;--cf-border-light:#f4f4f5;--cf-accent:#18181b;--cf-accent-hover:#27272a;--cf-success:#22c55e;--cf-error:#ef4444;--cf-shadow-sm:0 1px 2px rgba(0,0,0,.05);--cf-shadow:0 4px 6px -1px rgba(0,0,0,.1),0 2px 4px -2px rgba(0,0,0,.1);--cf-shadow-lg:0 10px 40px -10px rgba(0,0,0,.2);--cf-radius:16px;--cf-radius-sm:8px;--cf-radius-xs:6px;-webkit-font-smoothing:antialiased;color:var(--cf-text);font-family:Inter,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,sans-serif;font-size:14px;line-height:1.5}.checkflow-widget,.checkflow-widget *,.checkflow-widget :after,.checkflow-widget :before{box-sizing:border-box}.checkflow-trigger{align-items:center;background:var(--cf-accent);border:none;border-radius:50%;box-shadow:var(--cf-shadow-lg);color:#fff;cursor:pointer;display:flex;height:56px;justify-content:center;position:fixed;transition:all .3s cubic-bezier(.4,0,.2,1);width:56px;z-index:999998}.checkflow-trigger-pill{background:#fff;border-radius:24px;box-shadow:0 4px 20px rgba(0,0,0,.1),0 0 0 1px rgba(0,0,0,.05);color:#18181b;gap:8px;height:48px;min-width:160px;padding:0 20px;width:auto}.checkflow-trigger-pill:hover{box-shadow:0 8px 25px rgba(0,0,0,.15),0 0 0 1px rgba(0,0,0,.05);transform:translateY(-1px)}.checkflow-trigger-pill:active{transform:translateY(0)}.checkflow-trigger.bottom-right{bottom:24px;right:24px}.checkflow-trigger.bottom-left{bottom:24px;left:24px}.checkflow-trigger.top-right{right:24px;top:24px}.checkflow-trigger.top-left{left:24px;top:24px}.checkflow-trigger-icon{flex-shrink:0;height:20px;transition:transform .3s ease;width:20px}.checkflow-trigger-text{font-size:14px;font-weight:500;white-space:nowrap}.checkflow-trigger:hover .checkflow-trigger-icon{transform:rotate(-8deg)}.checkflow-panel{animation:cf-panel-in .3s cubic-bezier(.4,0,.2,1);background:var(--cf-bg);border:1px solid var(--cf-border-light);border-radius:var(--cf-radius);bottom:96px;box-shadow:var(--cf-shadow-lg);display:flex;flex-direction:column;max-height:calc(100vh - 140px);overflow:hidden;position:fixed;right:24px;width:380px;z-index:999999}.checkflow-panel.bottom-left{left:24px;right:auto}@keyframes cf-panel-in{0%{opacity:0;transform:translateY(16px) scale(.96)}to{opacity:1;transform:translateY(0) scale(1)}}.checkflow-header{align-items:center;background:var(--cf-bg);border-bottom:1px solid var(--cf-border-light);display:flex;justify-content:space-between;padding:16px 20px}.checkflow-header-left{align-items:center;display:flex;gap:12px}.checkflow-logo{align-items:center;background:var(--cf-accent);border-radius:8px;color:#fff;display:flex;height:32px;justify-content:center;width:32px}.checkflow-header-text h3{color:var(--cf-text);font-size:15px;font-weight:600;margin:0}.checkflow-header-text p{color:var(--cf-text-muted);font-size:12px;margin:0}.checkflow-close{align-items:center;background:transparent;border:none;border-radius:8px;color:var(--cf-text-secondary);cursor:pointer;display:flex;height:32px;justify-content:center;transition:all .2s;width:32px}.checkflow-close:hover{background:var(--cf-bg-hover);color:var(--cf-text)}.checkflow-body{flex:1;max-height:480px;overflow-y:auto;padding:20px}.checkflow-form{gap:16px}.checkflow-field,.checkflow-form{display:flex;flex-direction:column}.checkflow-field{gap:6px}.checkflow-label{color:var(--cf-text);font-size:13px;font-weight:500}.cf-required{color:var(--cf-text-muted);font-size:12px;font-weight:400}.checkflow-input,.checkflow-select,.checkflow-textarea{background:var(--cf-bg);border:1px solid var(--cf-border);border-radius:var(--cf-radius-sm);color:var(--cf-text);font-size:14px;padding:12px 14px;transition:all .2s}.checkflow-input::placeholder,.checkflow-textarea::placeholder{color:var(--cf-text-muted)}.checkflow-input:focus,.checkflow-select:focus,.checkflow-textarea:focus{border-color:var(--cf-accent);box-shadow:0 0 0 3px rgba(24,24,27,.08);outline:none}.checkflow-textarea{font-family:inherit;min-height:80px;resize:none}.checkflow-select{appearance:none;background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%2371717a' stroke-width='2'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");background-position:right 12px center;background-repeat:no-repeat;cursor:pointer;padding-right:40px}.checkflow-type-grid{display:flex;flex-wrap:wrap;gap:8px}.checkflow-type-option{align-items:center;background:var(--cf-bg);border:1px solid var(--cf-border);border-radius:20px;cursor:pointer;display:flex;font-size:13px;gap:6px;padding:8px 14px;transition:all .2s}.checkflow-type-option:hover{background:var(--cf-bg-hover);border-color:var(--cf-text-muted)}.checkflow-type-option.selected{background:var(--cf-accent);border-color:var(--cf-accent);color:#fff}.checkflow-type-icon{font-size:14px;line-height:1}.checkflow-type-label{font-size:13px;font-weight:500}.checkflow-screenshot{background:var(--cf-bg-elevated);border-radius:var(--cf-radius-sm);overflow:hidden;position:relative}.checkflow-screenshot img{display:block;height:auto;width:100%}.checkflow-screenshot-actions{bottom:10px;display:flex;gap:6px;position:absolute;right:10px}.checkflow-screenshot-btn{align-items:center;backdrop-filter:blur(8px);background:hsla(0,0%,100%,.95);border:none;border-radius:6px;box-shadow:0 2px 8px rgba(0,0,0,.1);color:var(--cf-text);cursor:pointer;display:flex;font-size:11px;font-weight:500;gap:5px;padding:6px 10px;transition:all .2s}.checkflow-screenshot-btn:hover{background:#fff;box-shadow:0 4px 12px rgba(0,0,0,.15);transform:translateY(-1px)}.checkflow-screenshot-btn svg{height:12px;width:12px}.checkflow-annotate-btn{background:var(--cf-accent);color:#fff}.checkflow-annotate-btn:hover{background:var(--cf-accent-hover)}.checkflow-checkbox{align-items:center;cursor:pointer;display:flex;gap:10px;padding:8px 0}.checkflow-checkbox input{accent-color:var(--cf-accent);cursor:pointer;height:18px;width:18px}.checkflow-checkbox-label{color:var(--cf-text-secondary);font-size:13px}.checkflow-footer{background:var(--cf-bg);border-top:1px solid var(--cf-border-light);display:flex;gap:10px;padding:16px 20px}.checkflow-btn{border-radius:var(--cf-radius-sm);cursor:pointer;flex:1;font-size:14px;font-weight:500;padding:12px 16px;text-align:center;transition:all .2s}.checkflow-btn-secondary{background:var(--cf-bg);border:1px solid var(--cf-border);color:var(--cf-text-secondary)}.checkflow-btn-secondary:hover{background:var(--cf-bg-hover);color:var(--cf-text)}.checkflow-btn-primary{background:var(--cf-accent);border:none;color:#fff}.checkflow-btn-primary:hover{background:var(--cf-accent-hover)}.checkflow-btn-primary:disabled{background:var(--cf-border);color:var(--cf-text-muted);cursor:not-allowed}.checkflow-loading{align-items:center;display:flex;flex-direction:column;gap:16px;justify-content:center;padding:48px 20px}.checkflow-spinner{animation:cf-spin .7s linear infinite;border:2px solid var(--cf-border);border-radius:50%;border-top-color:var(--cf-accent);height:28px;width:28px}@keyframes cf-spin{to{transform:rotate(1turn)}}.checkflow-loading-text{color:var(--cf-text-muted);font-size:13px}.checkflow-success{padding:48px 24px;text-align:center}.checkflow-success-icon{align-items:center;background:linear-gradient(135deg,#22c55e,#16a34a);border-radius:50%;color:#fff;display:flex;font-size:28px;height:56px;justify-content:center;margin:0 auto 20px;width:56px}.checkflow-success-title{color:var(--cf-text);font-size:17px;font-weight:600;margin:0 0 8px}.checkflow-success-message{color:var(--cf-text-secondary);font-size:14px;line-height:1.6;margin:0}.checkflow-error{align-items:flex-start;background:#fef2f2;border:1px solid #fecaca;border-radius:var(--cf-radius-sm);color:#dc2626;display:flex;font-size:13px;gap:10px;margin-bottom:16px;padding:12px 14px}.checkflow-error-icon{flex-shrink:0;height:18px;width:18px}.checkflow-capture-btn{align-items:center;background:var(--cf-bg-elevated);border:1px dashed var(--cf-border);border-radius:var(--cf-radius-sm);color:var(--cf-text-secondary);cursor:pointer;display:flex;font-size:13px;font-weight:500;gap:8px;justify-content:center;padding:14px;transition:all .2s;width:100%}.checkflow-capture-btn:hover{background:var(--cf-bg-hover);border-color:var(--cf-text-muted);color:var(--cf-text)}.checkflow-capture-btn svg{height:18px;width:18px}.checkflow-options{display:flex;flex-wrap:wrap;gap:16px;padding-top:4px}.checkflow-divider{background:var(--cf-border-light);height:1px;margin:8px 0}.checkflow-powered{border-top:1px solid var(--cf-border-light);color:var(--cf-text-muted);font-size:11px;padding:12px;text-align:center}.checkflow-powered a{color:var(--cf-text-secondary);font-weight:500;text-decoration:none}.checkflow-powered a:hover{color:var(--cf-text)}@media (max-width:480px){.checkflow-panel{bottom:80px;max-height:calc(100vh - 100px);right:16px;width:calc(100vw - 32px)}.checkflow-trigger{bottom:16px;height:52px;right:16px;width:52px}.checkflow-trigger.bottom-left{left:16px}}.checkflow-body::-webkit-scrollbar{width:6px}.checkflow-body::-webkit-scrollbar-track{background:transparent}.checkflow-body::-webkit-scrollbar-thumb{background:var(--cf-border);border-radius:3px}.checkflow-body::-webkit-scrollbar-thumb:hover{background:var(--cf-text-muted)}.checkflow-overlay{align-items:center;animation:cf-fade-in .2s ease;background:rgba(0,0,0,.5);bottom:0;display:flex;justify-content:center;left:0;position:fixed;right:0;top:0;z-index:999999}@keyframes cf-fade-in{0%{opacity:0}to{opacity:1}}.checkflow-modal-compact{animation:cf-modal-in .3s cubic-bezier(.4,0,.2,1);background:var(--cf-bg);border-radius:var(--cf-radius);box-shadow:var(--cf-shadow-lg);display:flex;flex-direction:column;max-height:90vh;max-width:90vw;overflow:hidden;width:420px}@keyframes cf-modal-in{0%{opacity:0;transform:scale(.95) translateY(10px)}to{opacity:1;transform:scale(1) translateY(0)}}.checkflow-modal-compact .checkflow-header{padding:20px 24px}.checkflow-modal-compact .checkflow-header h3{font-size:18px;font-weight:600;margin:0}.checkflow-modal-compact .checkflow-body{max-height:none;padding:0 24px 24px}.checkflow-modal-compact .checkflow-footer{flex-direction:column;padding:20px 24px}.checkflow-modal-compact .checkflow-btn{width:100%}.checkflow-expanded{backdrop-filter:blur(4px);background:rgba(24,24,27,.8)}.checkflow-modal-expanded{animation:cf-modal-in .3s cubic-bezier(.4,0,.2,1);background:var(--cf-bg);border-radius:var(--cf-radius);box-shadow:0 25px 80px -20px rgba(0,0,0,.4);display:flex;flex-direction:column;height:90vh;max-height:800px;max-width:1200px;overflow:hidden;width:95vw}.checkflow-modal-expanded .checkflow-header{border-bottom:1px solid var(--cf-border-light);padding:16px 24px}.checkflow-modal-expanded .checkflow-header h3{font-size:16px;font-weight:600;margin:0}.checkflow-content-split{display:flex;flex:1;overflow:hidden}.checkflow-screenshot-area{background:#1a1a1a;display:flex;flex:1;flex-direction:column;position:relative}.checkflow-screenshot-container{align-items:center;display:flex;flex:1;justify-content:center;overflow:auto;padding:24px}.checkflow-screenshot-large{border-radius:8px;box-shadow:0 8px 32px rgba(0,0,0,.3);max-height:100%;max-width:100%;object-fit:contain}.checkflow-annotation-toolbar{background:rgba(0,0,0,.3);display:flex;gap:12px;justify-content:center;padding:16px}.checkflow-tool-btn{align-items:center;background:hsla(0,0%,100%,.1);border:1px solid hsla(0,0%,100%,.2);border-radius:8px;color:#fff;cursor:pointer;display:flex;font-size:13px;font-weight:500;gap:8px;padding:10px 20px;transition:all .2s}.checkflow-tool-btn:hover{background:hsla(0,0%,100%,.2);border-color:hsla(0,0%,100%,.3)}.checkflow-tool-btn svg{height:16px;width:16px}.checkflow-tool-highlight{background:#7c3aed;border-color:#7c3aed}.checkflow-tool-highlight:hover{background:#6d28d9;border-color:#6d28d9}.checkflow-form-area{background:var(--cf-bg);border-left:1px solid var(--cf-border-light);display:flex;flex-direction:column;width:320px}.checkflow-form-area .checkflow-form{flex:1;overflow-y:auto;padding:20px}.checkflow-form-area .checkflow-textarea{min-height:100px}.checkflow-btn-link{background:none;border:none;color:var(--cf-text-muted);cursor:pointer;font-size:12px;padding:8px 0;text-decoration:underline;transition:color .2s}.checkflow-btn-link:hover{color:var(--cf-error)}.checkflow-footer-expanded{border-top:1px solid var(--cf-border-light);display:flex;flex-direction:column;gap:10px;padding:16px 20px}.checkflow-footer-expanded .checkflow-btn{width:100%}.checkflow-form-area .checkflow-type-grid{display:flex;flex-wrap:wrap;gap:6px}.checkflow-form-area .checkflow-type-option{font-size:12px;padding:6px 10px}.checkflow-form-area .checkflow-type-icon{display:none}@media (max-width:768px){.checkflow-modal-expanded{border-radius:0;height:100vh;max-height:none;max-width:none;width:100vw}.checkflow-content-split{flex-direction:column}.checkflow-screenshot-area{flex:0 0 50%}.checkflow-form-area{border-left:none;border-top:1px solid var(--cf-border-light);width:100%}}
|