@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 ADDED
@@ -0,0 +1,265 @@
1
+ # @checkflow/sdk
2
+
3
+ > User feedback collection SDK with automatic context capture
4
+
5
+ [![npm version](https://img.shields.io/npm/v/@checkflow/sdk.svg)](https://www.npmjs.com/package/@checkflow/sdk)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](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,6 @@
1
+ /**
2
+ * CheckFlow Annotation Styles
3
+ * CSS-in-JS styles for the annotation editor
4
+ */
5
+ export declare function injectAnnotationStyles(): void;
6
+ export declare function removeAnnotationStyles(): void;
@@ -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%}}