@usero/sdk 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/types.ts","../src/api.ts","../src/colorUtils.ts","../src/validation.ts","../src/widgetCss.ts","../src/vanilla.ts"],"names":["EMOJI_MAP","RATING_LABELS","EMOJI_BACKGROUNDS","DEFAULT_API_URL","DEFAULT_THEME","DARK_THEME","mergeTheme","customTheme","isJsonErrorBody","value","parseScreenshotUploadBody","obj","success","error","rawShot","screenshot","s","FeedbackApiClient","baseUrl","data","response","errorMessage","errorData","result","message","file","clientId","formData","body","raw","colorNameToHex","color","ctx","getGradientEnd","hex","r","g","b","shiftedR","shiftedG","shiftedB","x","validateFeedbackSubmission","errors","hasRating","hasComment","FEEDBACK_CSS","EMAIL_STORAGE_KEY","escapeHtml","ch","readStoredEmail","writeStoredEmail","email","initUseroFeedbackWidget","props","err","position","theme","title","placeholder","showEmailOption","showScreenshotOption","environment","metadata","onSubmit","onError","onOpen","onClose","apiClient","isOpen","selectedRating","comment","shareEmail","userEmail","isSubmitting","submitMessage","screenshots","isUploadingScreenshot","screenshotError","MAX_SCREENSHOTS","MAX_SCREENSHOT_BYTES","host","root","style","buttonEl","backdropEl","panelEl","setSubmitMessage","next","render","open","handleScreenshotFile","uploaded","removeScreenshot","index","_","i","close","submitForm","feedbackData","submission","validation","msg","renderButton","renderBackdrop","renderPanel","remaining","lowChars","ratingsHtml","sel","bg","messageHtml","screenshotBlockHtml","atMax","btnDisabled","previewsHtml","shot","errorHtml","limitHtml","extrasHtml","emailBlockHtml","submitDisabled","submitStyle","e","btn","textarea","counter","left","shareCb","emailInp","fileInput","idx","onKeyDown","destroyed","needsRender"],"mappings":"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AAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;A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","file":"usero.iife.js","sourcesContent":["// Shared types used by both the vanilla and React entry points.\n// Keep this file framework-free so the vanilla bundle never pulls react.\n\nexport type FeedbackRating = 1 | 2 | 3 | 4\n\nexport interface FeedbackMetadata {\n\tpageUrl: string\n\tpageTitle: string\n\treferrer?: string\n\ttimestamp: number\n}\n\nexport interface ScreenshotData {\n\tfileName: string\n\turl: string\n\tfileSize: number\n\twidth?: number\n\theight?: number\n\tmimeType: string\n}\n\nexport interface FeedbackSubmission {\n\tclientId: string\n\trating?: FeedbackRating\n\tcomment?: string\n\tuserEmail?: string\n\tpageUrl: string\n\tpageTitle: string\n\treferrer?: string\n\tenvironment?: string\n\tscreenshots?: ScreenshotData[]\n\tmetadata?: Record<string, unknown>\n}\n\nexport interface FeedbackData {\n\trating?: FeedbackRating\n\tcomment?: string\n\tuserEmail?: string\n\tscreenshots?: ScreenshotData[]\n\tmetadata: FeedbackMetadata\n}\n\nexport type WidgetPosition = 'right' | 'left'\n\nexport interface WidgetTheme {\n\tprimary: string\n\tbackground: string\n\ttext: string\n\tborder: string\n\tshadow: string\n}\n\nexport interface FeedbackWidgetProps {\n\tclientId: string\n\tposition?: WidgetPosition\n\ttheme?: Partial<WidgetTheme>\n\ttitle?: string\n\tplaceholder?: string\n\tshowEmailOption?: boolean\n\tshowScreenshotOption?: boolean\n\tenvironment?: string\n\tbaseUrl?: string\n\tmetadata?: Record<string, unknown>\n\tonSubmit?: (feedback: FeedbackData) => void\n\tonError?: (error: Error) => void\n\tonOpen?: () => void\n\tonClose?: () => void\n}\n\nexport interface SubmissionResponse {\n\tsuccess: boolean\n\terror?: string\n\tid?: string\n\tmessage?: string\n\tdata?: unknown\n}\n\nexport const EMOJI_MAP: Record<FeedbackRating, string> = {\n\t1: '😞',\n\t2: '😐',\n\t3: '😊',\n\t4: '🤩',\n}\n\nexport const RATING_LABELS: Record<FeedbackRating, string> = {\n\t1: 'Needs work',\n\t2: \"It's okay\",\n\t3: 'Pretty good',\n\t4: 'Amazing!',\n}\n\nexport const EMOJI_BACKGROUNDS: Record<FeedbackRating, string> = {\n\t1: 'linear-gradient(135deg,#ff6b6b14,#ff6b6b1f)',\n\t2: 'linear-gradient(135deg,#9ca3af0f,#9ca3af1a)',\n\t3: 'linear-gradient(135deg,#3b82f614,#3b82f61f)',\n\t4: 'linear-gradient(135deg,#f59e0b14,#f59e0b1f)',\n}\n\nexport const DEFAULT_API_URL = 'https://usero.io'\n\nexport const DEFAULT_THEME: WidgetTheme = {\n\tprimary: '#2563eb',\n\tbackground: '#ffffff',\n\ttext: '#374151',\n\tborder: '#e5e7eb',\n\tshadow:\n\t\t'0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05)',\n}\n\nexport const DARK_THEME: WidgetTheme = {\n\tprimary: '#2563eb',\n\tbackground: '#1f2937',\n\ttext: '#f9fafb',\n\tborder: '#374151',\n\tshadow:\n\t\t'0 10px 15px -3px rgba(0, 0, 0, 0.3), 0 4px 6px -2px rgba(0, 0, 0, 0.2)',\n}\n\nexport function mergeTheme(customTheme: Partial<WidgetTheme> = {}): WidgetTheme {\n\treturn { ...DEFAULT_THEME, ...customTheme }\n}\n","import {\n\tDEFAULT_API_URL,\n\ttype FeedbackSubmission,\n\ttype ScreenshotData,\n\ttype SubmissionResponse,\n} from './types'\n\ninterface JsonErrorBody {\n\terror?: string\n}\n\nfunction isJsonErrorBody(value: unknown): value is JsonErrorBody {\n\treturn typeof value === 'object' && value !== null && 'error' in value\n}\n\ninterface ScreenshotUploadResponseBody {\n\tsuccess: boolean\n\terror?: string\n\tscreenshot?: ScreenshotData\n}\n\nfunction parseScreenshotUploadBody(\n\tvalue: unknown,\n): ScreenshotUploadResponseBody {\n\tif (typeof value !== 'object' || value === null) {\n\t\treturn { success: false, error: 'Invalid response' }\n\t}\n\tconst obj = value as Record<string, unknown>\n\tconst success = obj.success === true\n\tconst error = typeof obj.error === 'string' ? obj.error : undefined\n\tconst rawShot = obj.screenshot\n\tlet screenshot: ScreenshotData | undefined\n\tif (typeof rawShot === 'object' && rawShot !== null) {\n\t\tconst s = rawShot as Record<string, unknown>\n\t\tif (\n\t\t\ttypeof s.fileName === 'string' &&\n\t\t\ttypeof s.url === 'string' &&\n\t\t\ttypeof s.fileSize === 'number' &&\n\t\t\ttypeof s.mimeType === 'string'\n\t\t) {\n\t\t\tscreenshot = {\n\t\t\t\tfileName: s.fileName,\n\t\t\t\turl: s.url,\n\t\t\t\tfileSize: s.fileSize,\n\t\t\t\tmimeType: s.mimeType,\n\t\t\t\twidth: typeof s.width === 'number' ? s.width : undefined,\n\t\t\t\theight: typeof s.height === 'number' ? s.height : undefined,\n\t\t\t}\n\t\t}\n\t}\n\treturn { success, error, screenshot }\n}\n\nexport class FeedbackApiClient {\n\tprivate baseUrl: string\n\n\tconstructor(baseUrl: string = DEFAULT_API_URL) {\n\t\tthis.baseUrl = baseUrl.replace(/\\/$/, '')\n\t}\n\n\tasync submitFeedback(data: FeedbackSubmission): Promise<SubmissionResponse> {\n\t\ttry {\n\t\t\tconst response = await fetch(`${this.baseUrl}/api/feedback`, {\n\t\t\t\tmethod: 'POST',\n\t\t\t\theaders: {\n\t\t\t\t\t'Content-Type': 'application/json',\n\t\t\t\t\tAccept: 'application/json',\n\t\t\t\t},\n\t\t\t\tbody: JSON.stringify(data),\n\t\t\t\tsignal: AbortSignal.timeout(10000),\n\t\t\t})\n\n\t\t\tif (!response.ok) {\n\t\t\t\tlet errorMessage = `HTTP ${response.status}: ${response.statusText}`\n\t\t\t\ttry {\n\t\t\t\t\tconst errorData: unknown = await response.json()\n\t\t\t\t\tif (isJsonErrorBody(errorData) && typeof errorData.error === 'string') {\n\t\t\t\t\t\terrorMessage = errorData.error\n\t\t\t\t\t}\n\t\t\t\t} catch {\n\t\t\t\t\t// Ignore JSON parse errors\n\t\t\t\t}\n\t\t\t\tthrow new Error(errorMessage)\n\t\t\t}\n\n\t\t\tconst result: unknown = await response.json()\n\t\t\tconst message =\n\t\t\t\ttypeof result === 'object' &&\n\t\t\t\tresult !== null &&\n\t\t\t\t'message' in result &&\n\t\t\t\ttypeof (result as { message: unknown }).message === 'string'\n\t\t\t\t\t? (result as { message: string }).message\n\t\t\t\t\t: 'Feedback submitted successfully'\n\n\t\t\treturn {\n\t\t\t\tsuccess: true,\n\t\t\t\tdata: result,\n\t\t\t\tmessage,\n\t\t\t}\n\t\t} catch (error) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror:\n\t\t\t\t\terror instanceof Error ? error.message : 'An unexpected error occurred',\n\t\t\t}\n\t\t}\n\t}\n\n\tasync uploadScreenshot(\n\t\tfile: File,\n\t\tclientId: string,\n\t): Promise<ScreenshotData> {\n\t\tconst formData = new FormData()\n\t\tformData.append('screenshot', file)\n\t\tformData.append('clientId', clientId)\n\n\t\tconst response = await fetch(`${this.baseUrl}/api/screenshots`, {\n\t\t\tmethod: 'POST',\n\t\t\tbody: formData,\n\t\t\tsignal: AbortSignal.timeout(30000),\n\t\t})\n\n\t\tlet body: ScreenshotUploadResponseBody = { success: false }\n\t\ttry {\n\t\t\tconst raw: unknown = await response.json()\n\t\t\tbody = parseScreenshotUploadBody(raw)\n\t\t} catch {\n\t\t\t// fall through to error handling below\n\t\t}\n\n\t\tif (!response.ok || !body.success || !body.screenshot) {\n\t\t\tconst message =\n\t\t\t\tbody.error ?? `HTTP ${response.status}: ${response.statusText}`\n\t\t\tthrow new Error(message)\n\t\t}\n\n\t\treturn body.screenshot\n\t}\n\n\tping(): void {\n\t\tfetch(`${this.baseUrl}/api/ping`, {\n\t\t\tsignal: AbortSignal.timeout(5000),\n\t\t}).catch(() => {})\n\t}\n}\n","export function colorNameToHex(color: string): string {\n\tif (color.startsWith('#')) return color\n\tif (typeof document === 'undefined') return color\n\n\tconst canvas = document.createElement('canvas')\n\tconst ctx = canvas.getContext('2d')\n\tif (!ctx) return color\n\n\tctx.fillStyle = color\n\treturn ctx.fillStyle\n}\n\nexport function getGradientEnd(color: string): string {\n\tconst hex = colorNameToHex(color)\n\tif (!hex.startsWith('#') || hex.length < 7) return hex\n\tconst r = parseInt(hex.slice(1, 3), 16)\n\tconst g = parseInt(hex.slice(3, 5), 16)\n\tconst b = parseInt(hex.slice(5, 7), 16)\n\tconst shiftedR = Math.max(0, r - 60)\n\tconst shiftedG = Math.min(255, g + 40)\n\tconst shiftedB = Math.min(255, b + 20)\n\treturn `#${[shiftedR, shiftedG, shiftedB]\n\t\t.map(x => x.toString(16).padStart(2, '0'))\n\t\t.join('')}`\n}\n","import type { FeedbackSubmission } from './types'\n\nexport interface ValidationResult {\n\tisValid: boolean\n\terrors: string[]\n}\n\nexport function validateFeedbackSubmission(\n\tdata: Partial<FeedbackSubmission>,\n): ValidationResult {\n\tconst errors: string[] = []\n\tconst hasRating = data.rating != null\n\tconst hasComment = !!data.comment?.trim()\n\n\tif (!hasRating && !hasComment) {\n\t\terrors.push('Add rating or comment')\n\t}\n\tif (hasRating && data.rating !== undefined && ![1, 2, 3, 4].includes(data.rating)) {\n\t\terrors.push('Invalid rating')\n\t}\n\tif (hasComment && data.comment !== undefined) {\n\t\tif (data.comment.length > 1000) {\n\t\t\terrors.push('Comment too long')\n\t\t}\n\t\tif (/<script[^>]*>.*?<\\/script>/gi.test(data.comment)) {\n\t\t\terrors.push('Invalid comment')\n\t\t}\n\t}\n\n\treturn {\n\t\tisValid: errors.length === 0,\n\t\terrors,\n\t}\n}\n","// CSS used by both entry points.\n//\n// React entry injects it once into <head> via injectFeedbackCSS().\n// Vanilla entry injects it inside a shadow root, so host page styles\n// can't bleed in and our class names can't collide with the host.\n\nexport const FEEDBACK_CSS = `\n@keyframes spin {\n 0% { transform: rotate(0deg); }\n 100% { transform: rotate(360deg); }\n}\n\n.fb-es {\n display: flex;\n justify-content: center;\n gap: 15px;\n padding-bottom: 10px;\n}\n\n.fb-ec {\n border-radius: 16px;\n padding: 0 5px;\n transition: all 300ms cubic-bezier(0.68, -0.55, 0.265, 1.55);\n border: 3px solid transparent;\n cursor: pointer;\n text-align: center;\n}\n\n.fb-ec--sel {\n border-color: #2563eb;\n transform: scale(1.05);\n box-shadow: 0 4px 15px rgba(37, 99, 235, 0.2);\n}\n\n.fb-ec--hov:not(.fb-ec--sel) {\n transform: scale(1.05);\n}\n\n.fb-eb {\n background: transparent;\n border: none;\n cursor: pointer;\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 2px;\n width: 100%;\n padding: 0;\n transition: all 200ms ease;\n}\n\n.fb-ei {\n font-size: 36px;\n transition: transform 200ms ease;\n}\n\n.fb-ei--hov {\n transform: scale(1.1);\n}\n\n.fb-el {\n font-size: 13px;\n font-weight: 600;\n color: currentColor;\n line-height: 1.2;\n}\n\n.fb-hdr {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding-bottom: 4px;\n margin-bottom: 10px;\n}\n\n.fb-msg {\n font-size: 14px;\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 12px;\n margin-bottom: 8px;\n border-radius: 6px;\n}\n\n.fb-msg--header {\n font-size: 12px;\n padding: 4px 8px;\n margin-bottom: 0;\n margin-left: auto;\n margin-right: 8px;\n}\n\n.fb-msg--ok {\n background-color: #f0fdf4;\n border: 1px solid #bbf7d0;\n color: #16a34a;\n}\n\n.fb-msg--err {\n background-color: #fef2f2;\n border: 1px solid #fecaca;\n color: #dc2626;\n}\n\n.fb-sub {\n width: 100%;\n padding: 16px 24px;\n border: none;\n border-radius: 12px;\n font-size: 15px;\n font-weight: 600;\n cursor: pointer;\n transition: all 200ms ease;\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n}\n\n.fb-sub--dis {\n cursor: not-allowed;\n opacity: 0.5;\n}\n\n.fb-spin {\n width: 16px;\n height: 16px;\n border: 2px solid transparent;\n border-top: 2px solid currentColor;\n border-radius: 50%;\n animation: spin 1s linear infinite;\n}\n\n.fb-cnt {\n padding: 24px;\n overflow: auto;\n max-height: calc(90vh - 48px);\n}\n\n.fb-ttl {\n margin: 0;\n font-size: 20px;\n font-weight: 600;\n}\n\n.fb-ta {\n width: 100%;\n min-height: 100px;\n padding: 12px;\n border-radius: 8px;\n font-size: 14px;\n font-family: inherit;\n outline: none;\n resize: vertical;\n transition: border-color 150ms ease;\n margin-bottom: 4px;\n box-sizing: border-box;\n}\n\n.fb-charcount {\n font-size: 12px;\n margin-left: auto;\n margin-bottom: 8px;\n text-align: right;\n}\n\n.fb-charcount--low {\n color: #dc2626;\n}\n\n.fb-email {\n display: flex;\n flex-direction: column;\n gap: 8px;\n margin-bottom: 16px;\n}\n\n.fb-email-lbl {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 14px;\n font-weight: 500;\n cursor: pointer;\n}\n\n.fb-email-cb {\n margin: 0;\n cursor: pointer;\n}\n\n.fb-email-inp {\n width: 100%;\n padding: 8px 12px;\n border-radius: 4px;\n font-size: 14px;\n outline: none;\n transition: border-color 150ms ease;\n box-sizing: border-box;\n}\n\n.fb-btn {\n position: fixed;\n width: 50px;\n height: 50px;\n border: none;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 18px;\n transition: all 300ms cubic-bezier(0.68, -0.55, 0.265, 1.55);\n z-index: 9998;\n color: #ffffff;\n top: 50%;\n transform: translateY(-50%);\n box-shadow: 0 4px 15px rgba(37, 99, 235, 0.3);\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", \"Roboto\", \"Helvetica Neue\", Arial, sans-serif;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n box-sizing: border-box;\n}\n\n.fb-btn--right {\n right: -25px;\n border-radius: 40px 0 0 40px;\n padding-right: 8px;\n box-shadow: -4px 0 15px rgba(37, 99, 235, 0.3);\n}\n\n.fb-btn--left {\n left: -25px;\n border-radius: 0 40px 40px 0;\n padding-left: 8px;\n box-shadow: 4px 0 15px rgba(37, 99, 235, 0.3);\n}\n\n.fb-btn--right.fb-btn--open {\n right: -15px;\n transform: translateY(-50%) scale(1.05);\n}\n\n.fb-btn--left.fb-btn--open {\n left: -15px;\n transform: translateY(-50%) scale(1.05);\n}\n\n.fb-backdrop {\n position: fixed;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n background-color: rgba(0, 0, 0, 0.3);\n transition: opacity 300ms ease;\n z-index: 9999;\n backdrop-filter: blur(8px);\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", \"Roboto\", \"Helvetica Neue\", Arial, sans-serif;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n box-sizing: border-box;\n}\n\n.fb-pnl-base {\n position: fixed;\n top: 10vh;\n width: 400px;\n max-width: 90vw;\n max-height: 60vh;\n box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);\n transition: transform 300ms cubic-bezier(0.25, 0.46, 0.45, 0.94);\n z-index: 10000;\n display: flex;\n flex-direction: column;\n overflow-y: auto;\n overflow-x: hidden;\n border-radius: 16px;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", \"Roboto\", \"Helvetica Neue\", Arial, sans-serif;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n box-sizing: border-box;\n}\n\n.fb-pnl--right { right: 0; }\n.fb-pnl--right.fb-pnl--open { transform: translateX(0px); }\n.fb-pnl--right.fb-pnl--closed { transform: translateX(100%); }\n\n.fb-pnl--left { left: 0; }\n.fb-pnl--left.fb-pnl--open { transform: translateX(0px); }\n.fb-pnl--left.fb-pnl--closed { transform: translateX(-100%); }\n\n.fb-close-btn {\n background: none;\n border: none;\n font-size: 24px;\n cursor: pointer;\n opacity: 0.7;\n padding: 0;\n width: 32px;\n height: 32px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 4px;\n transition: background-color 150ms ease;\n}\n\n.fb-up {\n display: flex;\n flex-direction: column;\n gap: 8px;\n margin-bottom: 12px;\n}\n\n.fb-upb {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n align-self: flex-start;\n padding: 8px 12px;\n border-radius: 8px;\n background: transparent;\n font-size: 13px;\n font-weight: 500;\n cursor: pointer;\n transition: background-color 150ms ease, opacity 150ms ease;\n font-family: inherit;\n}\n\n.fb-upb:hover:not(.fb-upb--dis) {\n background-color: rgba(37, 99, 235, 0.06);\n}\n\n.fb-upb--dis {\n cursor: not-allowed;\n opacity: 0.5;\n}\n\n.fb-ups {\n width: 12px;\n height: 12px;\n border: 2px solid transparent;\n border-top: 2px solid currentColor;\n border-radius: 50%;\n animation: spin 1s linear infinite;\n display: inline-block;\n}\n\n.fb-up-extras {\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n\n.fb-upe {\n font-size: 12px;\n color: #dc2626;\n}\n\n.fb-ss {\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n}\n\n.fb-sp {\n position: relative;\n width: 64px;\n height: 64px;\n border-radius: 6px;\n overflow: hidden;\n border: 1px solid rgba(0, 0, 0, 0.08);\n}\n\n.fb-si {\n width: 100%;\n height: 100%;\n object-fit: cover;\n display: block;\n}\n\n.fb-sr {\n position: absolute;\n top: 2px;\n right: 2px;\n width: 18px;\n height: 18px;\n border-radius: 50%;\n border: none;\n background: rgba(0, 0, 0, 0.65);\n color: #fff;\n font-size: 11px;\n line-height: 1;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 0;\n}\n\n.fb-sr:hover {\n background: rgba(0, 0, 0, 0.85);\n}\n\n.fb-sl {\n font-size: 11px;\n opacity: 0.6;\n}\n\n@media (max-width: 768px) {\n .fb-pnl-base {\n width: 100% !important;\n max-width: none !important;\n top: 5vh !important;\n max-height: 70vh !important;\n }\n .fb-cnt { padding: 20px !important; max-height: calc(100vh - 80px) !important; }\n .fb-ta { font-size: 16px !important; min-height: 80px !important; }\n .fb-ttl { font-size: 18px !important; }\n .fb-ei { font-size: 24px !important; }\n .fb-el { font-size: 11px !important; }\n .fb-sub { padding: 14px 20px !important; font-size: 16px !important; }\n}\n`\n\nexport function injectFeedbackCSS(): void {\n\tif (typeof document === 'undefined') return\n\tconst styleId = 'usero-feedback-widget-css'\n\tif (document.getElementById(styleId)) return\n\tconst style = document.createElement('style')\n\tstyle.id = styleId\n\tstyle.textContent = FEEDBACK_CSS\n\tdocument.head.appendChild(style)\n}\n","// Framework-free Usero widget. Renders into a shadow root attached to a\n// container <div> on document.body so host page styles cannot bleed in\n// and our class names cannot collide with the host's.\n//\n// API:\n// const widget = initUseroFeedbackWidget({ clientId: '...' })\n// widget.destroy()\n//\n// The endpoint and request shape match the React widget exactly so a\n// feedback row created here is indistinguishable from one created via React.\n\nimport { FeedbackApiClient } from './api'\nimport { getGradientEnd } from './colorUtils'\nimport {\n\tDARK_THEME,\n\tDEFAULT_THEME,\n\tEMOJI_BACKGROUNDS,\n\tEMOJI_MAP,\n\ttype FeedbackData,\n\ttype FeedbackRating,\n\ttype FeedbackSubmission,\n\ttype FeedbackWidgetProps,\n\tmergeTheme,\n\tRATING_LABELS,\n\ttype ScreenshotData,\n\ttype WidgetPosition,\n\ttype WidgetTheme,\n} from './types'\nimport { validateFeedbackSubmission } from './validation'\nimport { FEEDBACK_CSS } from './widgetCss'\n\nexport {\n\tDARK_THEME,\n\tDEFAULT_THEME,\n\tmergeTheme,\n}\nexport type {\n\tFeedbackData,\n\tFeedbackRating,\n\tFeedbackSubmission,\n\tFeedbackWidgetProps,\n\tScreenshotData,\n\tWidgetPosition,\n\tWidgetTheme,\n} from './types'\n\nexport interface UseroWidgetHandle {\n\tdestroy: () => void\n\topen: () => void\n\tclose: () => void\n\t// Hot-swap any subset of props EXCEPT `clientId` and `baseUrl`. Changing\n\t// those requires destroy + re-init (the API client is bound to baseUrl,\n\t// and clientId is the identity of the widget). Callers (e.g. the React\n\t// wrapper) typically route callbacks through this so identity changes on\n\t// re-render don't force a tear-down.\n\tupdate: (next: Partial<Omit<FeedbackWidgetProps, 'clientId' | 'baseUrl'>>) => void\n}\n\nconst EMAIL_STORAGE_KEY = 'feedback_user_email'\n\nfunction escapeHtml(value: string): string {\n\treturn value.replace(/[&<>\"']/g, ch => {\n\t\tswitch (ch) {\n\t\t\tcase '&':\n\t\t\t\treturn '&amp;'\n\t\t\tcase '<':\n\t\t\t\treturn '&lt;'\n\t\t\tcase '>':\n\t\t\t\treturn '&gt;'\n\t\t\tcase '\"':\n\t\t\t\treturn '&quot;'\n\t\t\tcase \"'\":\n\t\t\t\treturn '&#x27;'\n\t\t\tdefault:\n\t\t\t\treturn ch\n\t\t}\n\t})\n}\n\nfunction readStoredEmail(): string {\n\tif (typeof window === 'undefined') return ''\n\ttry {\n\t\treturn window.localStorage.getItem(EMAIL_STORAGE_KEY) ?? ''\n\t} catch {\n\t\treturn ''\n\t}\n}\n\nfunction writeStoredEmail(email: string): void {\n\ttry {\n\t\twindow.localStorage.setItem(EMAIL_STORAGE_KEY, email)\n\t} catch {\n\t\t// ignore\n\t}\n}\n\nexport function initUseroFeedbackWidget(\n\tprops: FeedbackWidgetProps,\n): UseroWidgetHandle {\n\tif (typeof document === 'undefined') {\n\t\treturn {\n\t\t\tdestroy: () => {},\n\t\t\topen: () => {},\n\t\t\tclose: () => {},\n\t\t\tupdate: () => {},\n\t\t}\n\t}\n\n\tconst { clientId, baseUrl } = props\n\n\tif (!clientId || clientId.length < 3) {\n\t\tconst err = new Error('Invalid config. Contact admin.')\n\t\tprops.onError?.(err)\n\t\treturn {\n\t\t\tdestroy: () => {},\n\t\t\topen: () => {},\n\t\t\tclose: () => {},\n\t\t\tupdate: () => {},\n\t\t}\n\t}\n\n\t// Mutable view of every prop that can be hot-swapped via update(). Read\n\t// these at render time, never destructure into local const above the\n\t// render closures or you'll capture stale values.\n\tlet position: WidgetPosition = props.position ?? 'right'\n\tlet theme: WidgetTheme = mergeTheme(props.theme)\n\tlet title: string = props.title ?? 'Share Feedback'\n\tlet placeholder: string = props.placeholder ?? 'Tell us what you think... (optional)'\n\tlet showEmailOption: boolean = props.showEmailOption ?? true\n\tlet showScreenshotOption: boolean = props.showScreenshotOption ?? true\n\tlet environment: string | undefined = props.environment\n\tlet metadata: Record<string, unknown> | undefined = props.metadata\n\tlet onSubmit: FeedbackWidgetProps['onSubmit'] = props.onSubmit\n\tlet onError: FeedbackWidgetProps['onError'] = props.onError\n\tlet onOpen: FeedbackWidgetProps['onOpen'] = props.onOpen\n\tlet onClose: FeedbackWidgetProps['onClose'] = props.onClose\n\n\tconst apiClient = new FeedbackApiClient(baseUrl)\n\n\t// State\n\tlet isOpen = false\n\tlet selectedRating: FeedbackRating | undefined = undefined\n\tlet comment = ''\n\tlet shareEmail = false\n\tlet userEmail = readStoredEmail()\n\tlet isSubmitting = false\n\tlet submitMessage: { type: 'success' | 'error'; text: string } | null = null\n\tlet screenshots: ScreenshotData[] = []\n\tlet isUploadingScreenshot = false\n\tlet screenshotError: string | null = null\n\n\tconst MAX_SCREENSHOTS = 3\n\tconst MAX_SCREENSHOT_BYTES = 10 * 1024 * 1024 // 10MB, matches old React widget\n\n\t// Host element on the page. ShadowRoot keeps host CSS isolated.\n\tconst host = document.createElement('div')\n\thost.setAttribute('data-usero-widget', '')\n\t// position: static so the host element doesn't take any space; the\n\t// fixed-position children inside the shadow root anchor to the viewport.\n\thost.style.cssText = 'all: initial;'\n\tdocument.body.appendChild(host)\n\tconst root = host.attachShadow({ mode: 'open' })\n\n\t// Inject styles once into the shadow root.\n\tconst style = document.createElement('style')\n\tstyle.textContent = FEEDBACK_CSS\n\troot.appendChild(style)\n\n\t// Containers\n\tconst buttonEl = document.createElement('button')\n\tconst backdropEl = document.createElement('div')\n\tconst panelEl = document.createElement('div')\n\troot.appendChild(buttonEl)\n\troot.appendChild(backdropEl)\n\troot.appendChild(panelEl)\n\n\tfunction setSubmitMessage(\n\t\tnext: { type: 'success' | 'error'; text: string } | null,\n\t): void {\n\t\tsubmitMessage = next\n\t\trender()\n\t}\n\n\tfunction open(): void {\n\t\tif (isOpen) return\n\t\tisOpen = true\n\t\t// Reset transient state\n\t\tselectedRating = undefined\n\t\tcomment = ''\n\t\tshareEmail = false\n\t\tsubmitMessage = null\n\t\tscreenshots = []\n\t\tscreenshotError = null\n\t\tisUploadingScreenshot = false\n\t\tapiClient.ping()\n\t\tonOpen?.()\n\t\trender()\n\t}\n\n\tasync function handleScreenshotFile(file: File): Promise<void> {\n\t\tscreenshotError = null\n\t\tif (!file.type.startsWith('image/')) {\n\t\t\tscreenshotError = 'Image files only'\n\t\t\trender()\n\t\t\treturn\n\t\t}\n\t\tif (file.size > MAX_SCREENSHOT_BYTES) {\n\t\t\tscreenshotError = 'Max 10MB'\n\t\t\trender()\n\t\t\treturn\n\t\t}\n\t\tif (screenshots.length >= MAX_SCREENSHOTS) {\n\t\t\tscreenshotError = `Max ${MAX_SCREENSHOTS} screenshots`\n\t\t\trender()\n\t\t\treturn\n\t\t}\n\n\t\tisUploadingScreenshot = true\n\t\trender()\n\t\ttry {\n\t\t\tconst uploaded = await apiClient.uploadScreenshot(file, clientId)\n\t\t\tscreenshots = [...screenshots, uploaded]\n\t\t} catch (err) {\n\t\t\tscreenshotError = err instanceof Error ? err.message : 'Upload failed'\n\t\t} finally {\n\t\t\tisUploadingScreenshot = false\n\t\t\trender()\n\t\t}\n\t}\n\n\tfunction removeScreenshot(index: number): void {\n\t\tscreenshots = screenshots.filter((_, i) => i !== index)\n\t\trender()\n\t}\n\n\tfunction close(): void {\n\t\tif (!isOpen) return\n\t\tisOpen = false\n\t\tonClose?.()\n\t\trender()\n\t}\n\n\tasync function submitForm(): Promise<void> {\n\t\tif (isSubmitting) return\n\t\tisSubmitting = true\n\t\tsubmitMessage = null\n\t\trender()\n\n\t\tconst feedbackData: FeedbackData = {\n\t\t\trating: selectedRating,\n\t\t\tcomment: comment.trim() || undefined,\n\t\t\tuserEmail: shareEmail ? userEmail : undefined,\n\t\t\tscreenshots: screenshots.length > 0 ? screenshots : undefined,\n\t\t\tmetadata: {\n\t\t\t\tpageUrl: window.location.href,\n\t\t\t\tpageTitle: document.title || 'Untitled Page',\n\t\t\t\treferrer: document.referrer || undefined,\n\t\t\t\ttimestamp: Date.now(),\n\t\t\t},\n\t\t}\n\n\t\tconst submission: FeedbackSubmission = {\n\t\t\tclientId,\n\t\t\trating: feedbackData.rating,\n\t\t\tcomment: feedbackData.comment,\n\t\t\tuserEmail: feedbackData.userEmail,\n\t\t\tpageUrl: feedbackData.metadata.pageUrl,\n\t\t\tpageTitle: feedbackData.metadata.pageTitle,\n\t\t\treferrer: feedbackData.metadata.referrer,\n\t\t\tenvironment,\n\t\t}\n\t\tif (screenshots.length > 0) submission.screenshots = screenshots\n\t\tif (metadata !== undefined) submission.metadata = metadata\n\n\t\tconst validation = validateFeedbackSubmission(submission)\n\t\tif (!validation.isValid) {\n\t\t\tisSubmitting = false\n\t\t\tsetSubmitMessage({ type: 'error', text: validation.errors.join(', ') })\n\t\t\treturn\n\t\t}\n\n\t\ttry {\n\t\t\tconst response = await apiClient.submitFeedback(submission)\n\t\t\tif (response.success) {\n\t\t\t\tif (shareEmail && userEmail) writeStoredEmail(userEmail)\n\t\t\t\tonSubmit?.(feedbackData)\n\t\t\t\tselectedRating = undefined\n\t\t\t\tcomment = ''\n\t\t\t\tshareEmail = false\n\t\t\t\tscreenshots = []\n\t\t\t\tscreenshotError = null\n\t\t\t\tsubmitMessage = { type: 'success', text: 'Thank you!' }\n\t\t\t} else {\n\t\t\t\tconst msg = response.error ?? 'Error occurred. Try again.'\n\t\t\t\tonError?.(new Error(msg))\n\t\t\t\tsubmitMessage = { type: 'error', text: msg }\n\t\t\t}\n\t\t} catch (err) {\n\t\t\tconst msg = err instanceof Error ? err.message : 'Error occurred. Try again.'\n\t\t\tonError?.(new Error(msg))\n\t\t\tsubmitMessage = { type: 'error', text: msg }\n\t\t} finally {\n\t\t\tisSubmitting = false\n\t\t\trender()\n\t\t}\n\t}\n\n\t// Static button content + styles (only style.background changes once)\n\tfunction renderButton(): void {\n\t\tbuttonEl.className = `fb-btn fb-btn--${position} ${isOpen ? 'fb-btn--open' : ''}`\n\t\tbuttonEl.setAttribute('aria-label', 'Open feedback')\n\t\tbuttonEl.type = 'button'\n\t\tbuttonEl.style.background = `linear-gradient(135deg, ${theme.primary}, ${getGradientEnd(theme.primary)})`\n\t\tbuttonEl.innerHTML = isOpen\n\t\t\t? `<span style=\"font-size:20px;\">✕</span>`\n\t\t\t: ''\n\t}\n\n\tfunction renderBackdrop(): void {\n\t\tbackdropEl.className = 'fb-backdrop'\n\t\tbackdropEl.style.display = isOpen ? 'block' : 'none'\n\t\tbackdropEl.setAttribute('aria-label', 'Close modal')\n\t}\n\n\tfunction renderPanel(): void {\n\t\tpanelEl.className = `fb-pnl-base fb-pnl--${position} ${\n\t\t\tisOpen ? 'fb-pnl--open' : 'fb-pnl--closed'\n\t\t}`\n\t\tpanelEl.style.backgroundColor = theme.background\n\t\tif (position === 'right') {\n\t\t\tpanelEl.style.borderLeft = `1px solid ${theme.border}`\n\t\t\tpanelEl.style.borderRight = ''\n\t\t} else {\n\t\t\tpanelEl.style.borderRight = `1px solid ${theme.border}`\n\t\t\tpanelEl.style.borderLeft = ''\n\t\t}\n\t\tpanelEl.setAttribute('role', 'dialog')\n\t\tpanelEl.setAttribute('aria-modal', 'true')\n\t\tpanelEl.setAttribute('aria-labelledby', 'usero-feedback-title')\n\n\t\tconst remaining = 1000 - comment.length\n\t\tconst lowChars = remaining < 50\n\n\t\tconst ratingsHtml = ([1, 2, 3, 4] as FeedbackRating[])\n\t\t\t.map(r => {\n\t\t\t\tconst sel = selectedRating === r\n\t\t\t\tconst bg = EMOJI_BACKGROUNDS[r]\n\t\t\t\tconst cls = ['fb-ec', sel && 'fb-ec--sel'].filter(Boolean).join(' ')\n\t\t\t\treturn `\n\t\t\t\t\t<div class=\"${cls}\" style=\"background:${bg}\">\n\t\t\t\t\t\t<button type=\"button\" class=\"fb-eb\" data-rating=\"${r}\" role=\"radio\" aria-checked=\"${sel}\" aria-label=\"${r}: ${RATING_LABELS[r]}\">\n\t\t\t\t\t\t\t<div class=\"fb-ei\"><span role=\"img\" aria-label=\"${RATING_LABELS[r]}\">${EMOJI_MAP[r]}</span></div>\n\t\t\t\t\t\t\t<div class=\"fb-el\">${RATING_LABELS[r]}</div>\n\t\t\t\t\t\t</button>\n\t\t\t\t\t</div>\n\t\t\t\t`\n\t\t\t})\n\t\t\t.join('')\n\n\t\tconst messageHtml = submitMessage\n\t\t\t? `<div class=\"fb-msg fb-msg--header ${submitMessage.type === 'success' ? 'fb-msg--ok' : 'fb-msg--err'}\">${submitMessage.type === 'success' ? '✓' : '⚠'} ${escapeHtml(submitMessage.text)}</div>`\n\t\t\t: ''\n\n\t\tconst screenshotBlockHtml = showScreenshotOption\n\t\t\t? (() => {\n\t\t\t\t\tconst atMax = screenshots.length >= MAX_SCREENSHOTS\n\t\t\t\t\tconst btnDisabled = isUploadingScreenshot || atMax\n\t\t\t\t\tconst previewsHtml = screenshots\n\t\t\t\t\t\t.map(\n\t\t\t\t\t\t\t(shot, i) => `\n\t\t\t\t\t\t\t\t<div class=\"fb-sp\">\n\t\t\t\t\t\t\t\t\t<img src=\"${escapeHtml(shot.url)}\" alt=\"Screenshot ${i + 1}\" class=\"fb-si\" />\n\t\t\t\t\t\t\t\t\t<button type=\"button\" class=\"fb-sr\" data-role=\"screenshot-remove\" data-index=\"${i}\" aria-label=\"Remove screenshot\">✕</button>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t`,\n\t\t\t\t\t\t)\n\t\t\t\t\t\t.join('')\n\t\t\t\t\tconst errorHtml = screenshotError\n\t\t\t\t\t\t? `<div class=\"fb-upe\">⚠ ${escapeHtml(screenshotError)}</div>`\n\t\t\t\t\t\t: ''\n\t\t\t\t\tconst limitHtml = atMax\n\t\t\t\t\t\t? `<div class=\"fb-sl\">Max ${MAX_SCREENSHOTS}</div>`\n\t\t\t\t\t\t: ''\n\t\t\t\t\tconst extrasHtml =\n\t\t\t\t\t\tscreenshotError || screenshots.length > 0 || atMax\n\t\t\t\t\t\t\t? `<div class=\"fb-up-extras\">${errorHtml}${\n\t\t\t\t\t\t\t\t\tscreenshots.length > 0\n\t\t\t\t\t\t\t\t\t\t? `<div class=\"fb-ss\">${previewsHtml}</div>`\n\t\t\t\t\t\t\t\t\t\t: ''\n\t\t\t\t\t\t\t\t}${limitHtml}</div>`\n\t\t\t\t\t\t\t: ''\n\t\t\t\t\treturn `\n\t\t\t\t\t\t<div class=\"fb-up\">\n\t\t\t\t\t\t\t<input type=\"file\" accept=\"image/*\" data-role=\"screenshot-input\" style=\"display:none;\" aria-label=\"Choose screenshot\" />\n\t\t\t\t\t\t\t<button type=\"button\" class=\"fb-upb ${btnDisabled ? 'fb-upb--dis' : ''}\" data-role=\"screenshot-pick\" ${btnDisabled ? 'disabled' : ''} style=\"border:1px solid ${theme.border};color:${theme.text};\">\n\t\t\t\t\t\t\t\t${\n\t\t\t\t\t\t\t\t\tisUploadingScreenshot\n\t\t\t\t\t\t\t\t\t\t? '<span class=\"fb-ups\"></span> Uploading...'\n\t\t\t\t\t\t\t\t\t\t: '📷 Add screenshot'\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t${extrasHtml}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t`\n\t\t\t\t})()\n\t\t\t: ''\n\n\t\tconst emailBlockHtml = showEmailOption\n\t\t\t? `\n\t\t\t\t<div class=\"fb-email\">\n\t\t\t\t\t<label class=\"fb-email-lbl\" style=\"color:${theme.text}\">\n\t\t\t\t\t\t<input type=\"checkbox\" class=\"fb-email-cb\" data-role=\"share-email\" ${shareEmail ? 'checked' : ''} aria-label=\"Share email\" />\n\t\t\t\t\t\t<span>Share my email</span>\n\t\t\t\t\t</label>\n\t\t\t\t\t${\n\t\t\t\t\t\tshareEmail\n\t\t\t\t\t\t\t? `<input type=\"email\" class=\"fb-email-inp\" data-role=\"email-input\" value=\"${escapeHtml(userEmail)}\" placeholder=\"your.email@example.com\" aria-label=\"Email\" maxlength=\"254\" autocomplete=\"email\" style=\"border:1px solid ${theme.border};color:${theme.text};background-color:${theme.background};\" />`\n\t\t\t\t\t\t\t: ''\n\t\t\t\t\t}\n\t\t\t\t</div>\n\t\t\t`\n\t\t\t: ''\n\n\t\tconst submitDisabled = isSubmitting\n\t\tconst submitStyle = `background:linear-gradient(135deg, ${theme.primary}, ${getGradientEnd(theme.primary)});color:#ffffff;${submitDisabled ? 'opacity:0.6;cursor:not-allowed;' : ''}`\n\n\t\tpanelEl.innerHTML = `\n\t\t\t<div class=\"fb-cnt\">\n\t\t\t\t<div class=\"fb-hdr\" style=\"border-bottom:1px solid ${theme.border}\">\n\t\t\t\t\t<h2 id=\"usero-feedback-title\" class=\"fb-ttl\" style=\"color:${theme.text}\">${escapeHtml(title)}</h2>\n\t\t\t\t\t${messageHtml}\n\t\t\t\t\t<button class=\"fb-close-btn\" data-role=\"close\" style=\"color:${theme.text}\" aria-label=\"Close\" type=\"button\">✕</button>\n\t\t\t\t</div>\n\t\t\t\t<form data-role=\"form\">\n\t\t\t\t\t<div class=\"fb-es\" role=\"radiogroup\" aria-label=\"Rate experience\">${ratingsHtml}</div>\n\t\t\t\t\t<textarea class=\"fb-ta\" data-role=\"comment\" placeholder=\"${escapeHtml(placeholder)}\" aria-label=\"Comments\" maxlength=\"1000\" rows=\"2\" style=\"border:1px solid ${theme.border};color:${theme.text};background-color:${theme.background};\">${escapeHtml(comment)}</textarea>\n\t\t\t\t\t<div style=\"display:flex;justify-content:flex-end;margin-bottom:8px;\">\n\t\t\t\t\t\t<div style=\"font-size:12px;color:${lowChars ? '#dc2626' : theme.text};opacity:${lowChars ? 1 : 0.6};margin-left:auto;\">\n\t\t\t\t\t\t\t${remaining} chars remaining\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t\t${screenshotBlockHtml}\n\t\t\t\t\t${emailBlockHtml}\n\t\t\t\t\t<button class=\"fb-sub ${submitDisabled ? 'fb-sub--dis' : ''}\" type=\"submit\" aria-label=\"Submit\" ${submitDisabled ? 'disabled' : ''} style=\"${submitStyle}\">\n\t\t\t\t\t\t${isSubmitting ? '<span class=\"fb-spin\"></span>' : ''}\n\t\t\t\t\t\t${isSubmitting ? 'Submitting...' : 'Send Feedback 🚀'}\n\t\t\t\t\t</button>\n\t\t\t\t</form>\n\t\t\t</div>\n\t\t`\n\n\t\t// Wire up panel-internal events\n\t\tconst form = panelEl.querySelector<HTMLFormElement>('form[data-role=\"form\"]')\n\t\tform?.addEventListener('submit', e => {\n\t\t\te.preventDefault()\n\t\t\tvoid submitForm()\n\t\t})\n\n\t\tpanelEl\n\t\t\t.querySelector<HTMLButtonElement>('button[data-role=\"close\"]')\n\t\t\t?.addEventListener('click', close)\n\n\t\tpanelEl\n\t\t\t.querySelectorAll<HTMLButtonElement>('button[data-rating]')\n\t\t\t.forEach(btn => {\n\t\t\t\tbtn.addEventListener('click', () => {\n\t\t\t\t\tconst value = btn.dataset.rating\n\t\t\t\t\tif (value === '1' || value === '2' || value === '3' || value === '4') {\n\t\t\t\t\t\tselectedRating = Number(value) as FeedbackRating\n\t\t\t\t\t\trender()\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t})\n\n\t\tconst textarea = panelEl.querySelector<HTMLTextAreaElement>(\n\t\t\t'textarea[data-role=\"comment\"]',\n\t\t)\n\t\tif (textarea) {\n\t\t\ttextarea.addEventListener('input', () => {\n\t\t\t\tif (textarea.value.length <= 1000) {\n\t\t\t\t\tcomment = textarea.value\n\t\t\t\t\t// Update char count without full rerender to avoid losing focus.\n\t\t\t\t\tconst counter = panelEl.querySelector<HTMLDivElement>(\n\t\t\t\t\t\t'.fb-cnt form > div > div',\n\t\t\t\t\t)\n\t\t\t\t\tif (counter) {\n\t\t\t\t\t\tconst left = 1000 - comment.length\n\t\t\t\t\t\tcounter.textContent = `${left} chars remaining`\n\t\t\t\t\t\tcounter.style.color = left < 50 ? '#dc2626' : theme.text\n\t\t\t\t\t\tcounter.style.opacity = left < 50 ? '1' : '0.6'\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\n\t\tconst shareCb = panelEl.querySelector<HTMLInputElement>(\n\t\t\t'input[data-role=\"share-email\"]',\n\t\t)\n\t\tshareCb?.addEventListener('change', () => {\n\t\t\tshareEmail = shareCb.checked\n\t\t\trender()\n\t\t})\n\n\t\tconst emailInp = panelEl.querySelector<HTMLInputElement>(\n\t\t\t'input[data-role=\"email-input\"]',\n\t\t)\n\t\temailInp?.addEventListener('input', () => {\n\t\t\tif (emailInp.value.length <= 254) {\n\t\t\t\tuserEmail = emailInp.value\n\t\t\t}\n\t\t})\n\n\t\tconst fileInput = panelEl.querySelector<HTMLInputElement>(\n\t\t\t'input[data-role=\"screenshot-input\"]',\n\t\t)\n\t\tconst pickBtn = panelEl.querySelector<HTMLButtonElement>(\n\t\t\t'button[data-role=\"screenshot-pick\"]',\n\t\t)\n\t\tpickBtn?.addEventListener('click', () => {\n\t\t\tfileInput?.click()\n\t\t})\n\t\tfileInput?.addEventListener('change', () => {\n\t\t\tconst file = fileInput.files?.[0]\n\t\t\tif (!file) return\n\t\t\tvoid handleScreenshotFile(file).finally(() => {\n\t\t\t\tif (fileInput) fileInput.value = ''\n\t\t\t})\n\t\t})\n\t\tpanelEl\n\t\t\t.querySelectorAll<HTMLButtonElement>(\n\t\t\t\t'button[data-role=\"screenshot-remove\"]',\n\t\t\t)\n\t\t\t.forEach(btn => {\n\t\t\t\tbtn.addEventListener('click', () => {\n\t\t\t\t\tconst idx = Number(btn.dataset.index)\n\t\t\t\t\tif (Number.isInteger(idx)) removeScreenshot(idx)\n\t\t\t\t})\n\t\t\t})\n\t}\n\n\tfunction render(): void {\n\t\trenderButton()\n\t\trenderBackdrop()\n\t\trenderPanel()\n\t}\n\n\t// Top-level event listeners\n\tbuttonEl.addEventListener('click', () => {\n\t\tif (isOpen) close()\n\t\telse open()\n\t})\n\tbackdropEl.addEventListener('click', close)\n\n\tconst onKeyDown = (e: KeyboardEvent): void => {\n\t\tif (!isOpen) return\n\t\tif (e.key === 'Escape') close()\n\t\tif (e.key === 'Enter' && (e.metaKey || e.ctrlKey)) {\n\t\t\te.preventDefault()\n\t\t\tvoid submitForm()\n\t\t}\n\t}\n\tdocument.addEventListener('keydown', onKeyDown)\n\n\t// Initial paint\n\trender()\n\n\tlet destroyed = false\n\treturn {\n\t\tdestroy: () => {\n\t\t\tif (destroyed) return\n\t\t\tdestroyed = true\n\t\t\tdocument.removeEventListener('keydown', onKeyDown)\n\t\t\thost.remove()\n\t\t},\n\t\topen,\n\t\tclose,\n\t\tupdate: next => {\n\t\t\tif (destroyed) return\n\t\t\tlet needsRender = false\n\t\t\tif (next.position !== undefined && next.position !== position) {\n\t\t\t\tposition = next.position\n\t\t\t\tneedsRender = true\n\t\t\t}\n\t\t\tif (next.theme !== undefined) {\n\t\t\t\ttheme = mergeTheme(next.theme)\n\t\t\t\tneedsRender = true\n\t\t\t}\n\t\t\tif (next.title !== undefined && next.title !== title) {\n\t\t\t\ttitle = next.title\n\t\t\t\tneedsRender = true\n\t\t\t}\n\t\t\tif (next.placeholder !== undefined && next.placeholder !== placeholder) {\n\t\t\t\tplaceholder = next.placeholder\n\t\t\t\tneedsRender = true\n\t\t\t}\n\t\t\tif (\n\t\t\t\tnext.showEmailOption !== undefined &&\n\t\t\t\tnext.showEmailOption !== showEmailOption\n\t\t\t) {\n\t\t\t\tshowEmailOption = next.showEmailOption\n\t\t\t\tneedsRender = true\n\t\t\t}\n\t\t\tif (\n\t\t\t\tnext.showScreenshotOption !== undefined &&\n\t\t\t\tnext.showScreenshotOption !== showScreenshotOption\n\t\t\t) {\n\t\t\t\tshowScreenshotOption = next.showScreenshotOption\n\t\t\t\tneedsRender = true\n\t\t\t}\n\t\t\t// Non-render-affecting props: just swap refs.\n\t\t\tif ('environment' in next) environment = next.environment\n\t\t\tif ('metadata' in next) metadata = next.metadata\n\t\t\tif ('onSubmit' in next) onSubmit = next.onSubmit\n\t\t\tif ('onError' in next) onError = next.onError\n\t\t\tif ('onOpen' in next) onOpen = next.onOpen\n\t\t\tif ('onClose' in next) onClose = next.onClose\n\t\t\tif (needsRender) render()\n\t\t},\n\t}\n}\n"]}
1
+ {"version":3,"sources":["../src/types.ts","../src/api.ts","../src/colorUtils.ts","../src/validation.ts","../src/widgetCss.ts","../src/vanilla.ts"],"names":["EMOJI_MAP","RATING_LABELS","EMOJI_BACKGROUNDS","DEFAULT_API_URL","DEFAULT_THEME","DARK_THEME","mergeTheme","customTheme","isJsonErrorBody","value","parseScreenshotUploadBody","obj","success","error","rawShot","screenshot","s","FeedbackApiClient","baseUrl","data","response","errorMessage","errorData","result","message","file","clientId","formData","body","raw","colorNameToHex","color","ctx","getGradientEnd","hex","r","g","b","shiftedR","shiftedG","shiftedB","x","validateFeedbackSubmission","errors","hasRating","hasComment","FEEDBACK_CSS","resolveBaseTheme","resolveTheme","userTheme","base","EMAIL_STORAGE_KEY","escapeHtml","ch","readStoredEmail","writeStoredEmail","email","initUseroFeedbackWidget","props","err","position","userThemeOverride","theme","title","placeholder","showEmailOption","showScreenshotOption","environment","metadata","onSubmit","onError","onOpen","onClose","apiClient","isOpen","selectedRating","comment","shareEmail","userEmail","isSubmitting","submitMessage","screenshots","isUploadingScreenshot","screenshotError","MAX_SCREENSHOTS","MAX_SCREENSHOT_BYTES","host","root","style","buttonEl","backdropEl","panelEl","setSubmitMessage","next","render","open","handleScreenshotFile","uploaded","removeScreenshot","index","_","i","close","submitForm","feedbackData","submission","validation","msg","renderButton","renderBackdrop","renderPanel","remaining","lowChars","ratingsHtml","sel","bg","messageHtml","screenshotBlockHtml","atMax","btnDisabled","previewsHtml","shot","errorHtml","limitHtml","extrasHtml","emailBlockHtml","submitDisabled","submitStyle","e","btn","textarea","counter","left","shareCb","emailInp","fileInput","idx","onKeyDown","darkMql","mqlListener","detachMqlListener","attachMqlListener","destroyed","needsRender"],"mappings":"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AAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;A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","file":"usero.iife.js","sourcesContent":["// Shared types used by both the vanilla and React entry points.\n// Keep this file framework-free so the vanilla bundle never pulls react.\n\nexport type FeedbackRating = 1 | 2 | 3 | 4\n\nexport interface FeedbackMetadata {\n\tpageUrl: string\n\tpageTitle: string\n\treferrer?: string\n\ttimestamp: number\n}\n\nexport interface ScreenshotData {\n\tfileName: string\n\turl: string\n\tfileSize: number\n\twidth?: number\n\theight?: number\n\tmimeType: string\n}\n\nexport interface FeedbackSubmission {\n\tclientId: string\n\trating?: FeedbackRating\n\tcomment?: string\n\tuserEmail?: string\n\tpageUrl: string\n\tpageTitle: string\n\treferrer?: string\n\tenvironment?: string\n\tscreenshots?: ScreenshotData[]\n\tmetadata?: Record<string, unknown>\n}\n\nexport interface FeedbackData {\n\trating?: FeedbackRating\n\tcomment?: string\n\tuserEmail?: string\n\tscreenshots?: ScreenshotData[]\n\tmetadata: FeedbackMetadata\n}\n\nexport type WidgetPosition = 'right' | 'left'\n\nexport interface WidgetTheme {\n\tprimary: string\n\tbackground: string\n\ttext: string\n\tborder: string\n\tshadow: string\n}\n\nexport interface FeedbackWidgetProps {\n\tclientId: string\n\tposition?: WidgetPosition\n\ttheme?: Partial<WidgetTheme>\n\ttitle?: string\n\tplaceholder?: string\n\tshowEmailOption?: boolean\n\tshowScreenshotOption?: boolean\n\tenvironment?: string\n\tbaseUrl?: string\n\tmetadata?: Record<string, unknown>\n\tonSubmit?: (feedback: FeedbackData) => void\n\tonError?: (error: Error) => void\n\tonOpen?: () => void\n\tonClose?: () => void\n}\n\nexport interface SubmissionResponse {\n\tsuccess: boolean\n\terror?: string\n\tid?: string\n\tmessage?: string\n\tdata?: unknown\n}\n\nexport const EMOJI_MAP: Record<FeedbackRating, string> = {\n\t1: '😞',\n\t2: '😐',\n\t3: '😊',\n\t4: '🤩',\n}\n\nexport const RATING_LABELS: Record<FeedbackRating, string> = {\n\t1: 'Needs work',\n\t2: \"It's okay\",\n\t3: 'Pretty good',\n\t4: 'Amazing!',\n}\n\nexport const EMOJI_BACKGROUNDS: Record<FeedbackRating, string> = {\n\t1: 'linear-gradient(135deg,#ff6b6b14,#ff6b6b1f)',\n\t2: 'linear-gradient(135deg,#9ca3af0f,#9ca3af1a)',\n\t3: 'linear-gradient(135deg,#3b82f614,#3b82f61f)',\n\t4: 'linear-gradient(135deg,#f59e0b14,#f59e0b1f)',\n}\n\nexport const DEFAULT_API_URL = 'https://usero.io'\n\nexport const DEFAULT_THEME: WidgetTheme = {\n\tprimary: '#2563eb',\n\tbackground: '#ffffff',\n\ttext: '#374151',\n\tborder: '#e5e7eb',\n\tshadow:\n\t\t'0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05)',\n}\n\nexport const DARK_THEME: WidgetTheme = {\n\tprimary: '#2563eb',\n\tbackground: '#1f2937',\n\ttext: '#f9fafb',\n\tborder: '#374151',\n\tshadow:\n\t\t'0 10px 15px -3px rgba(0, 0, 0, 0.3), 0 4px 6px -2px rgba(0, 0, 0, 0.2)',\n}\n\nexport function mergeTheme(customTheme: Partial<WidgetTheme> = {}): WidgetTheme {\n\treturn { ...DEFAULT_THEME, ...customTheme }\n}\n","import {\n\tDEFAULT_API_URL,\n\ttype FeedbackSubmission,\n\ttype ScreenshotData,\n\ttype SubmissionResponse,\n} from './types'\n\ninterface JsonErrorBody {\n\terror?: string\n}\n\nfunction isJsonErrorBody(value: unknown): value is JsonErrorBody {\n\treturn typeof value === 'object' && value !== null && 'error' in value\n}\n\ninterface ScreenshotUploadResponseBody {\n\tsuccess: boolean\n\terror?: string\n\tscreenshot?: ScreenshotData\n}\n\nfunction parseScreenshotUploadBody(\n\tvalue: unknown,\n): ScreenshotUploadResponseBody {\n\tif (typeof value !== 'object' || value === null) {\n\t\treturn { success: false, error: 'Invalid response' }\n\t}\n\tconst obj = value as Record<string, unknown>\n\tconst success = obj.success === true\n\tconst error = typeof obj.error === 'string' ? obj.error : undefined\n\tconst rawShot = obj.screenshot\n\tlet screenshot: ScreenshotData | undefined\n\tif (typeof rawShot === 'object' && rawShot !== null) {\n\t\tconst s = rawShot as Record<string, unknown>\n\t\tif (\n\t\t\ttypeof s.fileName === 'string' &&\n\t\t\ttypeof s.url === 'string' &&\n\t\t\ttypeof s.fileSize === 'number' &&\n\t\t\ttypeof s.mimeType === 'string'\n\t\t) {\n\t\t\tscreenshot = {\n\t\t\t\tfileName: s.fileName,\n\t\t\t\turl: s.url,\n\t\t\t\tfileSize: s.fileSize,\n\t\t\t\tmimeType: s.mimeType,\n\t\t\t\twidth: typeof s.width === 'number' ? s.width : undefined,\n\t\t\t\theight: typeof s.height === 'number' ? s.height : undefined,\n\t\t\t}\n\t\t}\n\t}\n\treturn { success, error, screenshot }\n}\n\nexport class FeedbackApiClient {\n\tprivate baseUrl: string\n\n\tconstructor(baseUrl: string = DEFAULT_API_URL) {\n\t\tthis.baseUrl = baseUrl.replace(/\\/$/, '')\n\t}\n\n\tasync submitFeedback(data: FeedbackSubmission): Promise<SubmissionResponse> {\n\t\ttry {\n\t\t\tconst response = await fetch(`${this.baseUrl}/api/feedback`, {\n\t\t\t\tmethod: 'POST',\n\t\t\t\theaders: {\n\t\t\t\t\t'Content-Type': 'application/json',\n\t\t\t\t\tAccept: 'application/json',\n\t\t\t\t},\n\t\t\t\tbody: JSON.stringify(data),\n\t\t\t\tsignal: AbortSignal.timeout(10000),\n\t\t\t})\n\n\t\t\tif (!response.ok) {\n\t\t\t\tlet errorMessage = `HTTP ${response.status}: ${response.statusText}`\n\t\t\t\ttry {\n\t\t\t\t\tconst errorData: unknown = await response.json()\n\t\t\t\t\tif (isJsonErrorBody(errorData) && typeof errorData.error === 'string') {\n\t\t\t\t\t\terrorMessage = errorData.error\n\t\t\t\t\t}\n\t\t\t\t} catch {\n\t\t\t\t\t// Ignore JSON parse errors\n\t\t\t\t}\n\t\t\t\tthrow new Error(errorMessage)\n\t\t\t}\n\n\t\t\tconst result: unknown = await response.json()\n\t\t\tconst message =\n\t\t\t\ttypeof result === 'object' &&\n\t\t\t\tresult !== null &&\n\t\t\t\t'message' in result &&\n\t\t\t\ttypeof (result as { message: unknown }).message === 'string'\n\t\t\t\t\t? (result as { message: string }).message\n\t\t\t\t\t: 'Feedback submitted successfully'\n\n\t\t\treturn {\n\t\t\t\tsuccess: true,\n\t\t\t\tdata: result,\n\t\t\t\tmessage,\n\t\t\t}\n\t\t} catch (error) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror:\n\t\t\t\t\terror instanceof Error ? error.message : 'An unexpected error occurred',\n\t\t\t}\n\t\t}\n\t}\n\n\tasync uploadScreenshot(\n\t\tfile: File,\n\t\tclientId: string,\n\t): Promise<ScreenshotData> {\n\t\tconst formData = new FormData()\n\t\tformData.append('screenshot', file)\n\t\tformData.append('clientId', clientId)\n\n\t\tconst response = await fetch(`${this.baseUrl}/api/screenshots`, {\n\t\t\tmethod: 'POST',\n\t\t\tbody: formData,\n\t\t\tsignal: AbortSignal.timeout(30000),\n\t\t})\n\n\t\tlet body: ScreenshotUploadResponseBody = { success: false }\n\t\ttry {\n\t\t\tconst raw: unknown = await response.json()\n\t\t\tbody = parseScreenshotUploadBody(raw)\n\t\t} catch {\n\t\t\t// fall through to error handling below\n\t\t}\n\n\t\tif (!response.ok || !body.success || !body.screenshot) {\n\t\t\tconst message =\n\t\t\t\tbody.error ?? `HTTP ${response.status}: ${response.statusText}`\n\t\t\tthrow new Error(message)\n\t\t}\n\n\t\treturn body.screenshot\n\t}\n\n\tping(): void {\n\t\tfetch(`${this.baseUrl}/api/ping`, {\n\t\t\tsignal: AbortSignal.timeout(5000),\n\t\t}).catch(() => {})\n\t}\n}\n","export function colorNameToHex(color: string): string {\n\tif (color.startsWith('#')) return color\n\tif (typeof document === 'undefined') return color\n\n\tconst canvas = document.createElement('canvas')\n\tconst ctx = canvas.getContext('2d')\n\tif (!ctx) return color\n\n\tctx.fillStyle = color\n\treturn ctx.fillStyle\n}\n\nexport function getGradientEnd(color: string): string {\n\tconst hex = colorNameToHex(color)\n\tif (!hex.startsWith('#') || hex.length < 7) return hex\n\tconst r = parseInt(hex.slice(1, 3), 16)\n\tconst g = parseInt(hex.slice(3, 5), 16)\n\tconst b = parseInt(hex.slice(5, 7), 16)\n\tconst shiftedR = Math.max(0, r - 60)\n\tconst shiftedG = Math.min(255, g + 40)\n\tconst shiftedB = Math.min(255, b + 20)\n\treturn `#${[shiftedR, shiftedG, shiftedB]\n\t\t.map(x => x.toString(16).padStart(2, '0'))\n\t\t.join('')}`\n}\n","import type { FeedbackSubmission } from './types'\n\nexport interface ValidationResult {\n\tisValid: boolean\n\terrors: string[]\n}\n\nexport function validateFeedbackSubmission(\n\tdata: Partial<FeedbackSubmission>,\n): ValidationResult {\n\tconst errors: string[] = []\n\tconst hasRating = data.rating != null\n\tconst hasComment = !!data.comment?.trim()\n\n\tif (!hasRating && !hasComment) {\n\t\terrors.push('Add rating or comment')\n\t}\n\tif (hasRating && data.rating !== undefined && ![1, 2, 3, 4].includes(data.rating)) {\n\t\terrors.push('Invalid rating')\n\t}\n\tif (hasComment && data.comment !== undefined) {\n\t\tif (data.comment.length > 1000) {\n\t\t\terrors.push('Comment too long')\n\t\t}\n\t\tif (/<script[^>]*>.*?<\\/script>/gi.test(data.comment)) {\n\t\t\terrors.push('Invalid comment')\n\t\t}\n\t}\n\n\treturn {\n\t\tisValid: errors.length === 0,\n\t\terrors,\n\t}\n}\n","// CSS used by both entry points.\n//\n// React entry injects it once into <head> via injectFeedbackCSS().\n// Vanilla entry injects it inside a shadow root, so host page styles\n// can't bleed in and our class names can't collide with the host.\n\nexport const FEEDBACK_CSS = `\n@keyframes spin {\n 0% { transform: rotate(0deg); }\n 100% { transform: rotate(360deg); }\n}\n\n.fb-es {\n display: flex;\n justify-content: center;\n gap: 15px;\n padding-bottom: 10px;\n}\n\n.fb-ec {\n border-radius: 16px;\n padding: 0 5px;\n transition: all 300ms cubic-bezier(0.68, -0.55, 0.265, 1.55);\n border: 3px solid transparent;\n cursor: pointer;\n text-align: center;\n}\n\n.fb-ec--sel {\n border-color: #2563eb;\n transform: scale(1.05);\n box-shadow: 0 4px 15px rgba(37, 99, 235, 0.2);\n}\n\n.fb-ec--hov:not(.fb-ec--sel) {\n transform: scale(1.05);\n}\n\n.fb-eb {\n background: transparent;\n border: none;\n cursor: pointer;\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 2px;\n width: 100%;\n padding: 0;\n transition: all 200ms ease;\n}\n\n.fb-ei {\n font-size: 36px;\n transition: transform 200ms ease;\n}\n\n.fb-ei--hov {\n transform: scale(1.1);\n}\n\n.fb-el {\n font-size: 13px;\n font-weight: 600;\n color: currentColor;\n line-height: 1.2;\n}\n\n.fb-hdr {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding-bottom: 4px;\n margin-bottom: 10px;\n}\n\n.fb-msg {\n font-size: 14px;\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 12px;\n margin-bottom: 8px;\n border-radius: 6px;\n}\n\n.fb-msg--header {\n font-size: 12px;\n padding: 4px 8px;\n margin-bottom: 0;\n margin-left: auto;\n margin-right: 8px;\n}\n\n.fb-msg--ok {\n background-color: #f0fdf4;\n border: 1px solid #bbf7d0;\n color: #16a34a;\n}\n\n.fb-msg--err {\n background-color: #fef2f2;\n border: 1px solid #fecaca;\n color: #dc2626;\n}\n\n.fb-sub {\n width: 100%;\n padding: 16px 24px;\n border: none;\n border-radius: 12px;\n font-size: 15px;\n font-weight: 600;\n cursor: pointer;\n transition: all 200ms ease;\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n}\n\n.fb-sub--dis {\n cursor: not-allowed;\n opacity: 0.5;\n}\n\n.fb-spin {\n width: 16px;\n height: 16px;\n border: 2px solid transparent;\n border-top: 2px solid currentColor;\n border-radius: 50%;\n animation: spin 1s linear infinite;\n}\n\n.fb-cnt {\n padding: 24px;\n overflow: auto;\n max-height: calc(90vh - 48px);\n}\n\n.fb-ttl {\n margin: 0;\n font-size: 20px;\n font-weight: 600;\n}\n\n.fb-ta {\n width: 100%;\n min-height: 100px;\n padding: 12px;\n border-radius: 8px;\n font-size: 14px;\n font-family: inherit;\n outline: none;\n resize: vertical;\n transition: border-color 150ms ease;\n margin-bottom: 4px;\n box-sizing: border-box;\n}\n\n.fb-charcount {\n font-size: 12px;\n margin-left: auto;\n margin-bottom: 8px;\n text-align: right;\n}\n\n.fb-charcount--low {\n color: #dc2626;\n}\n\n.fb-email {\n display: flex;\n flex-direction: column;\n gap: 8px;\n margin-bottom: 16px;\n}\n\n.fb-email-lbl {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 14px;\n font-weight: 500;\n cursor: pointer;\n}\n\n.fb-email-cb {\n margin: 0;\n cursor: pointer;\n}\n\n.fb-email-inp {\n width: 100%;\n padding: 8px 12px;\n border-radius: 4px;\n font-size: 14px;\n outline: none;\n transition: border-color 150ms ease;\n box-sizing: border-box;\n}\n\n.fb-btn {\n position: fixed;\n width: 50px;\n height: 50px;\n border: none;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 18px;\n transition: all 300ms cubic-bezier(0.68, -0.55, 0.265, 1.55);\n z-index: 9998;\n color: #ffffff;\n top: 50%;\n transform: translateY(-50%);\n box-shadow: 0 4px 15px rgba(37, 99, 235, 0.3);\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", \"Roboto\", \"Helvetica Neue\", Arial, sans-serif;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n box-sizing: border-box;\n}\n\n.fb-btn--right {\n right: -25px;\n border-radius: 40px 0 0 40px;\n padding-right: 8px;\n box-shadow: -4px 0 15px rgba(37, 99, 235, 0.3);\n}\n\n.fb-btn--left {\n left: -25px;\n border-radius: 0 40px 40px 0;\n padding-left: 8px;\n box-shadow: 4px 0 15px rgba(37, 99, 235, 0.3);\n}\n\n.fb-btn--right.fb-btn--open {\n right: -15px;\n transform: translateY(-50%) scale(1.05);\n}\n\n.fb-btn--left.fb-btn--open {\n left: -15px;\n transform: translateY(-50%) scale(1.05);\n}\n\n.fb-backdrop {\n position: fixed;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n background-color: rgba(0, 0, 0, 0.3);\n transition: opacity 300ms ease;\n z-index: 9999;\n backdrop-filter: blur(8px);\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", \"Roboto\", \"Helvetica Neue\", Arial, sans-serif;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n box-sizing: border-box;\n}\n\n.fb-pnl-base {\n position: fixed;\n top: 10vh;\n width: 400px;\n max-width: 90vw;\n max-height: 60vh;\n box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);\n transition: transform 300ms cubic-bezier(0.25, 0.46, 0.45, 0.94);\n z-index: 10000;\n display: flex;\n flex-direction: column;\n overflow-y: auto;\n overflow-x: hidden;\n border-radius: 16px;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", \"Roboto\", \"Helvetica Neue\", Arial, sans-serif;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n box-sizing: border-box;\n}\n\n.fb-pnl--right { right: 0; }\n.fb-pnl--right.fb-pnl--open { transform: translateX(0px); }\n.fb-pnl--right.fb-pnl--closed { transform: translateX(100%); }\n\n.fb-pnl--left { left: 0; }\n.fb-pnl--left.fb-pnl--open { transform: translateX(0px); }\n.fb-pnl--left.fb-pnl--closed { transform: translateX(-100%); }\n\n.fb-close-btn {\n background: none;\n border: none;\n font-size: 24px;\n cursor: pointer;\n opacity: 0.7;\n padding: 0;\n width: 32px;\n height: 32px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 4px;\n transition: background-color 150ms ease;\n}\n\n.fb-up {\n display: flex;\n flex-direction: column;\n gap: 8px;\n margin-bottom: 12px;\n}\n\n.fb-upb {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n align-self: flex-start;\n padding: 8px 12px;\n border-radius: 8px;\n background: transparent;\n font-size: 13px;\n font-weight: 500;\n cursor: pointer;\n transition: background-color 150ms ease, opacity 150ms ease;\n font-family: inherit;\n}\n\n.fb-upb:hover:not(.fb-upb--dis) {\n background-color: rgba(37, 99, 235, 0.06);\n}\n\n.fb-upb--dis {\n cursor: not-allowed;\n opacity: 0.5;\n}\n\n.fb-ups {\n width: 12px;\n height: 12px;\n border: 2px solid transparent;\n border-top: 2px solid currentColor;\n border-radius: 50%;\n animation: spin 1s linear infinite;\n display: inline-block;\n}\n\n.fb-up-extras {\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n\n.fb-upe {\n font-size: 12px;\n color: #dc2626;\n}\n\n.fb-ss {\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n}\n\n.fb-sp {\n position: relative;\n width: 64px;\n height: 64px;\n border-radius: 6px;\n overflow: hidden;\n border: 1px solid rgba(0, 0, 0, 0.08);\n}\n\n.fb-si {\n width: 100%;\n height: 100%;\n object-fit: cover;\n display: block;\n}\n\n.fb-sr {\n position: absolute;\n top: 2px;\n right: 2px;\n width: 18px;\n height: 18px;\n border-radius: 50%;\n border: none;\n background: rgba(0, 0, 0, 0.65);\n color: #fff;\n font-size: 11px;\n line-height: 1;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 0;\n}\n\n.fb-sr:hover {\n background: rgba(0, 0, 0, 0.85);\n}\n\n.fb-sl {\n font-size: 11px;\n opacity: 0.6;\n}\n\n@media (max-width: 768px) {\n .fb-pnl-base {\n width: 100% !important;\n max-width: none !important;\n top: 5vh !important;\n max-height: 70vh !important;\n }\n .fb-cnt { padding: 20px !important; max-height: calc(100vh - 80px) !important; }\n .fb-ta { font-size: 16px !important; min-height: 80px !important; }\n .fb-ttl { font-size: 18px !important; }\n .fb-ei { font-size: 24px !important; }\n .fb-el { font-size: 11px !important; }\n .fb-sub { padding: 14px 20px !important; font-size: 16px !important; }\n}\n`\n\nexport function injectFeedbackCSS(): void {\n\tif (typeof document === 'undefined') return\n\tconst styleId = 'usero-feedback-widget-css'\n\tif (document.getElementById(styleId)) return\n\tconst style = document.createElement('style')\n\tstyle.id = styleId\n\tstyle.textContent = FEEDBACK_CSS\n\tdocument.head.appendChild(style)\n}\n","// Framework-free Usero widget. Renders into a shadow root attached to a\n// container <div> on document.body so host page styles cannot bleed in\n// and our class names cannot collide with the host's.\n//\n// API:\n// const widget = initUseroFeedbackWidget({ clientId: '...' })\n// widget.destroy()\n//\n// The endpoint and request shape match the React widget exactly so a\n// feedback row created here is indistinguishable from one created via React.\n\nimport { FeedbackApiClient } from './api'\nimport { getGradientEnd } from './colorUtils'\nimport {\n\tDARK_THEME,\n\tDEFAULT_THEME,\n\tEMOJI_BACKGROUNDS,\n\tEMOJI_MAP,\n\ttype FeedbackData,\n\ttype FeedbackRating,\n\ttype FeedbackSubmission,\n\ttype FeedbackWidgetProps,\n\tmergeTheme,\n\tRATING_LABELS,\n\ttype ScreenshotData,\n\ttype WidgetPosition,\n\ttype WidgetTheme,\n} from './types'\nimport { validateFeedbackSubmission } from './validation'\nimport { FEEDBACK_CSS } from './widgetCss'\n\nexport {\n\tDARK_THEME,\n\tDEFAULT_THEME,\n\tmergeTheme,\n}\n\n// Pick the base theme to merge user overrides onto, based on the OS color\n// scheme. Defaults to dark when matchMedia is unavailable (SSR, old browsers)\n// or when neither dark nor light is explicitly preferred.\nfunction resolveBaseTheme(): WidgetTheme {\n\tif (typeof window === 'undefined' || typeof window.matchMedia !== 'function') {\n\t\treturn DARK_THEME\n\t}\n\tif (window.matchMedia('(prefers-color-scheme: dark)').matches) return DARK_THEME\n\tif (window.matchMedia('(prefers-color-scheme: light)').matches) return DEFAULT_THEME\n\treturn DARK_THEME\n}\n\n// Resolve the effective theme. If the caller passed a partial theme, it wins\n// per-key over the OS-resolved base. If they passed nothing, we just use the\n// OS-resolved base directly.\nexport function resolveTheme(userTheme: Partial<WidgetTheme> | undefined): WidgetTheme {\n\tconst base = resolveBaseTheme()\n\tif (!userTheme) return base\n\treturn { ...base, ...userTheme }\n}\nexport type {\n\tFeedbackData,\n\tFeedbackRating,\n\tFeedbackSubmission,\n\tFeedbackWidgetProps,\n\tScreenshotData,\n\tWidgetPosition,\n\tWidgetTheme,\n} from './types'\n\nexport interface UseroWidgetHandle {\n\tdestroy: () => void\n\topen: () => void\n\tclose: () => void\n\t// Hot-swap any subset of props EXCEPT `clientId` and `baseUrl`. Changing\n\t// those requires destroy + re-init (the API client is bound to baseUrl,\n\t// and clientId is the identity of the widget). Callers (e.g. the React\n\t// wrapper) typically route callbacks through this so identity changes on\n\t// re-render don't force a tear-down.\n\tupdate: (next: Partial<Omit<FeedbackWidgetProps, 'clientId' | 'baseUrl'>>) => void\n}\n\nconst EMAIL_STORAGE_KEY = 'feedback_user_email'\n\nfunction escapeHtml(value: string): string {\n\treturn value.replace(/[&<>\"']/g, ch => {\n\t\tswitch (ch) {\n\t\t\tcase '&':\n\t\t\t\treturn '&amp;'\n\t\t\tcase '<':\n\t\t\t\treturn '&lt;'\n\t\t\tcase '>':\n\t\t\t\treturn '&gt;'\n\t\t\tcase '\"':\n\t\t\t\treturn '&quot;'\n\t\t\tcase \"'\":\n\t\t\t\treturn '&#x27;'\n\t\t\tdefault:\n\t\t\t\treturn ch\n\t\t}\n\t})\n}\n\nfunction readStoredEmail(): string {\n\tif (typeof window === 'undefined') return ''\n\ttry {\n\t\treturn window.localStorage.getItem(EMAIL_STORAGE_KEY) ?? ''\n\t} catch {\n\t\treturn ''\n\t}\n}\n\nfunction writeStoredEmail(email: string): void {\n\ttry {\n\t\twindow.localStorage.setItem(EMAIL_STORAGE_KEY, email)\n\t} catch {\n\t\t// ignore\n\t}\n}\n\nexport function initUseroFeedbackWidget(\n\tprops: FeedbackWidgetProps,\n): UseroWidgetHandle {\n\tif (typeof document === 'undefined') {\n\t\treturn {\n\t\t\tdestroy: () => {},\n\t\t\topen: () => {},\n\t\t\tclose: () => {},\n\t\t\tupdate: () => {},\n\t\t}\n\t}\n\n\tconst { clientId, baseUrl } = props\n\n\tif (!clientId || clientId.length < 3) {\n\t\tconst err = new Error('Invalid config. Contact admin.')\n\t\tprops.onError?.(err)\n\t\treturn {\n\t\t\tdestroy: () => {},\n\t\t\topen: () => {},\n\t\t\tclose: () => {},\n\t\t\tupdate: () => {},\n\t\t}\n\t}\n\n\t// Mutable view of every prop that can be hot-swapped via update(). Read\n\t// these at render time, never destructure into local const above the\n\t// render closures or you'll capture stale values.\n\tlet position: WidgetPosition = props.position ?? 'right'\n\tlet userThemeOverride: Partial<WidgetTheme> | undefined = props.theme\n\tlet theme: WidgetTheme = resolveTheme(userThemeOverride)\n\tlet title: string = props.title ?? 'Share Feedback'\n\tlet placeholder: string = props.placeholder ?? 'Tell us what you think... (optional)'\n\tlet showEmailOption: boolean = props.showEmailOption ?? true\n\tlet showScreenshotOption: boolean = props.showScreenshotOption ?? true\n\tlet environment: string | undefined = props.environment\n\tlet metadata: Record<string, unknown> | undefined = props.metadata\n\tlet onSubmit: FeedbackWidgetProps['onSubmit'] = props.onSubmit\n\tlet onError: FeedbackWidgetProps['onError'] = props.onError\n\tlet onOpen: FeedbackWidgetProps['onOpen'] = props.onOpen\n\tlet onClose: FeedbackWidgetProps['onClose'] = props.onClose\n\n\tconst apiClient = new FeedbackApiClient(baseUrl)\n\n\t// State\n\tlet isOpen = false\n\tlet selectedRating: FeedbackRating | undefined = undefined\n\tlet comment = ''\n\tlet shareEmail = false\n\tlet userEmail = readStoredEmail()\n\tlet isSubmitting = false\n\tlet submitMessage: { type: 'success' | 'error'; text: string } | null = null\n\tlet screenshots: ScreenshotData[] = []\n\tlet isUploadingScreenshot = false\n\tlet screenshotError: string | null = null\n\n\tconst MAX_SCREENSHOTS = 3\n\tconst MAX_SCREENSHOT_BYTES = 10 * 1024 * 1024 // 10MB, matches old React widget\n\n\t// Host element on the page. ShadowRoot keeps host CSS isolated.\n\tconst host = document.createElement('div')\n\thost.setAttribute('data-usero-widget', '')\n\t// position: static so the host element doesn't take any space; the\n\t// fixed-position children inside the shadow root anchor to the viewport.\n\thost.style.cssText = 'all: initial;'\n\tdocument.body.appendChild(host)\n\tconst root = host.attachShadow({ mode: 'open' })\n\n\t// Inject styles once into the shadow root.\n\tconst style = document.createElement('style')\n\tstyle.textContent = FEEDBACK_CSS\n\troot.appendChild(style)\n\n\t// Containers\n\tconst buttonEl = document.createElement('button')\n\tconst backdropEl = document.createElement('div')\n\tconst panelEl = document.createElement('div')\n\troot.appendChild(buttonEl)\n\troot.appendChild(backdropEl)\n\troot.appendChild(panelEl)\n\n\tfunction setSubmitMessage(\n\t\tnext: { type: 'success' | 'error'; text: string } | null,\n\t): void {\n\t\tsubmitMessage = next\n\t\trender()\n\t}\n\n\tfunction open(): void {\n\t\tif (isOpen) return\n\t\tisOpen = true\n\t\t// Reset transient state\n\t\tselectedRating = undefined\n\t\tcomment = ''\n\t\tshareEmail = false\n\t\tsubmitMessage = null\n\t\tscreenshots = []\n\t\tscreenshotError = null\n\t\tisUploadingScreenshot = false\n\t\tapiClient.ping()\n\t\tonOpen?.()\n\t\trender()\n\t}\n\n\tasync function handleScreenshotFile(file: File): Promise<void> {\n\t\tscreenshotError = null\n\t\tif (!file.type.startsWith('image/')) {\n\t\t\tscreenshotError = 'Image files only'\n\t\t\trender()\n\t\t\treturn\n\t\t}\n\t\tif (file.size > MAX_SCREENSHOT_BYTES) {\n\t\t\tscreenshotError = 'Max 10MB'\n\t\t\trender()\n\t\t\treturn\n\t\t}\n\t\tif (screenshots.length >= MAX_SCREENSHOTS) {\n\t\t\tscreenshotError = `Max ${MAX_SCREENSHOTS} screenshots`\n\t\t\trender()\n\t\t\treturn\n\t\t}\n\n\t\tisUploadingScreenshot = true\n\t\trender()\n\t\ttry {\n\t\t\tconst uploaded = await apiClient.uploadScreenshot(file, clientId)\n\t\t\tscreenshots = [...screenshots, uploaded]\n\t\t} catch (err) {\n\t\t\tscreenshotError = err instanceof Error ? err.message : 'Upload failed'\n\t\t} finally {\n\t\t\tisUploadingScreenshot = false\n\t\t\trender()\n\t\t}\n\t}\n\n\tfunction removeScreenshot(index: number): void {\n\t\tscreenshots = screenshots.filter((_, i) => i !== index)\n\t\trender()\n\t}\n\n\tfunction close(): void {\n\t\tif (!isOpen) return\n\t\tisOpen = false\n\t\tonClose?.()\n\t\trender()\n\t}\n\n\tasync function submitForm(): Promise<void> {\n\t\tif (isSubmitting) return\n\t\tisSubmitting = true\n\t\tsubmitMessage = null\n\t\trender()\n\n\t\tconst feedbackData: FeedbackData = {\n\t\t\trating: selectedRating,\n\t\t\tcomment: comment.trim() || undefined,\n\t\t\tuserEmail: shareEmail ? userEmail : undefined,\n\t\t\tscreenshots: screenshots.length > 0 ? screenshots : undefined,\n\t\t\tmetadata: {\n\t\t\t\tpageUrl: window.location.href,\n\t\t\t\tpageTitle: document.title || 'Untitled Page',\n\t\t\t\treferrer: document.referrer || undefined,\n\t\t\t\ttimestamp: Date.now(),\n\t\t\t},\n\t\t}\n\n\t\tconst submission: FeedbackSubmission = {\n\t\t\tclientId,\n\t\t\trating: feedbackData.rating,\n\t\t\tcomment: feedbackData.comment,\n\t\t\tuserEmail: feedbackData.userEmail,\n\t\t\tpageUrl: feedbackData.metadata.pageUrl,\n\t\t\tpageTitle: feedbackData.metadata.pageTitle,\n\t\t\treferrer: feedbackData.metadata.referrer,\n\t\t\tenvironment,\n\t\t}\n\t\tif (screenshots.length > 0) submission.screenshots = screenshots\n\t\tif (metadata !== undefined) submission.metadata = metadata\n\n\t\tconst validation = validateFeedbackSubmission(submission)\n\t\tif (!validation.isValid) {\n\t\t\tisSubmitting = false\n\t\t\tsetSubmitMessage({ type: 'error', text: validation.errors.join(', ') })\n\t\t\treturn\n\t\t}\n\n\t\ttry {\n\t\t\tconst response = await apiClient.submitFeedback(submission)\n\t\t\tif (response.success) {\n\t\t\t\tif (shareEmail && userEmail) writeStoredEmail(userEmail)\n\t\t\t\tonSubmit?.(feedbackData)\n\t\t\t\tselectedRating = undefined\n\t\t\t\tcomment = ''\n\t\t\t\tshareEmail = false\n\t\t\t\tscreenshots = []\n\t\t\t\tscreenshotError = null\n\t\t\t\tsubmitMessage = { type: 'success', text: 'Thank you!' }\n\t\t\t} else {\n\t\t\t\tconst msg = response.error ?? 'Error occurred. Try again.'\n\t\t\t\tonError?.(new Error(msg))\n\t\t\t\tsubmitMessage = { type: 'error', text: msg }\n\t\t\t}\n\t\t} catch (err) {\n\t\t\tconst msg = err instanceof Error ? err.message : 'Error occurred. Try again.'\n\t\t\tonError?.(new Error(msg))\n\t\t\tsubmitMessage = { type: 'error', text: msg }\n\t\t} finally {\n\t\t\tisSubmitting = false\n\t\t\trender()\n\t\t}\n\t}\n\n\t// Static button content + styles (only style.background changes once)\n\tfunction renderButton(): void {\n\t\tbuttonEl.className = `fb-btn fb-btn--${position} ${isOpen ? 'fb-btn--open' : ''}`\n\t\tbuttonEl.setAttribute('aria-label', 'Open feedback')\n\t\tbuttonEl.type = 'button'\n\t\tbuttonEl.style.background = `linear-gradient(135deg, ${theme.primary}, ${getGradientEnd(theme.primary)})`\n\t\tbuttonEl.innerHTML = isOpen\n\t\t\t? `<span style=\"font-size:20px;\">✕</span>`\n\t\t\t: ''\n\t}\n\n\tfunction renderBackdrop(): void {\n\t\tbackdropEl.className = 'fb-backdrop'\n\t\tbackdropEl.style.display = isOpen ? 'block' : 'none'\n\t\tbackdropEl.setAttribute('aria-label', 'Close modal')\n\t}\n\n\tfunction renderPanel(): void {\n\t\tpanelEl.className = `fb-pnl-base fb-pnl--${position} ${\n\t\t\tisOpen ? 'fb-pnl--open' : 'fb-pnl--closed'\n\t\t}`\n\t\tpanelEl.style.backgroundColor = theme.background\n\t\tif (position === 'right') {\n\t\t\tpanelEl.style.borderLeft = `1px solid ${theme.border}`\n\t\t\tpanelEl.style.borderRight = ''\n\t\t} else {\n\t\t\tpanelEl.style.borderRight = `1px solid ${theme.border}`\n\t\t\tpanelEl.style.borderLeft = ''\n\t\t}\n\t\tpanelEl.setAttribute('role', 'dialog')\n\t\tpanelEl.setAttribute('aria-modal', 'true')\n\t\tpanelEl.setAttribute('aria-labelledby', 'usero-feedback-title')\n\n\t\tconst remaining = 1000 - comment.length\n\t\tconst lowChars = remaining < 50\n\n\t\tconst ratingsHtml = ([1, 2, 3, 4] as FeedbackRating[])\n\t\t\t.map(r => {\n\t\t\t\tconst sel = selectedRating === r\n\t\t\t\tconst bg = EMOJI_BACKGROUNDS[r]\n\t\t\t\tconst cls = ['fb-ec', sel && 'fb-ec--sel'].filter(Boolean).join(' ')\n\t\t\t\treturn `\n\t\t\t\t\t<div class=\"${cls}\" style=\"background:${bg}\">\n\t\t\t\t\t\t<button type=\"button\" class=\"fb-eb\" data-rating=\"${r}\" role=\"radio\" aria-checked=\"${sel}\" aria-label=\"${r}: ${RATING_LABELS[r]}\">\n\t\t\t\t\t\t\t<div class=\"fb-ei\"><span role=\"img\" aria-label=\"${RATING_LABELS[r]}\">${EMOJI_MAP[r]}</span></div>\n\t\t\t\t\t\t\t<div class=\"fb-el\">${RATING_LABELS[r]}</div>\n\t\t\t\t\t\t</button>\n\t\t\t\t\t</div>\n\t\t\t\t`\n\t\t\t})\n\t\t\t.join('')\n\n\t\tconst messageHtml = submitMessage\n\t\t\t? `<div class=\"fb-msg fb-msg--header ${submitMessage.type === 'success' ? 'fb-msg--ok' : 'fb-msg--err'}\">${submitMessage.type === 'success' ? '✓' : '⚠'} ${escapeHtml(submitMessage.text)}</div>`\n\t\t\t: ''\n\n\t\tconst screenshotBlockHtml = showScreenshotOption\n\t\t\t? (() => {\n\t\t\t\t\tconst atMax = screenshots.length >= MAX_SCREENSHOTS\n\t\t\t\t\tconst btnDisabled = isUploadingScreenshot || atMax\n\t\t\t\t\tconst previewsHtml = screenshots\n\t\t\t\t\t\t.map(\n\t\t\t\t\t\t\t(shot, i) => `\n\t\t\t\t\t\t\t\t<div class=\"fb-sp\">\n\t\t\t\t\t\t\t\t\t<img src=\"${escapeHtml(shot.url)}\" alt=\"Screenshot ${i + 1}\" class=\"fb-si\" />\n\t\t\t\t\t\t\t\t\t<button type=\"button\" class=\"fb-sr\" data-role=\"screenshot-remove\" data-index=\"${i}\" aria-label=\"Remove screenshot\">✕</button>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t`,\n\t\t\t\t\t\t)\n\t\t\t\t\t\t.join('')\n\t\t\t\t\tconst errorHtml = screenshotError\n\t\t\t\t\t\t? `<div class=\"fb-upe\">⚠ ${escapeHtml(screenshotError)}</div>`\n\t\t\t\t\t\t: ''\n\t\t\t\t\tconst limitHtml = atMax\n\t\t\t\t\t\t? `<div class=\"fb-sl\">Max ${MAX_SCREENSHOTS}</div>`\n\t\t\t\t\t\t: ''\n\t\t\t\t\tconst extrasHtml =\n\t\t\t\t\t\tscreenshotError || screenshots.length > 0 || atMax\n\t\t\t\t\t\t\t? `<div class=\"fb-up-extras\">${errorHtml}${\n\t\t\t\t\t\t\t\t\tscreenshots.length > 0\n\t\t\t\t\t\t\t\t\t\t? `<div class=\"fb-ss\">${previewsHtml}</div>`\n\t\t\t\t\t\t\t\t\t\t: ''\n\t\t\t\t\t\t\t\t}${limitHtml}</div>`\n\t\t\t\t\t\t\t: ''\n\t\t\t\t\treturn `\n\t\t\t\t\t\t<div class=\"fb-up\">\n\t\t\t\t\t\t\t<input type=\"file\" accept=\"image/*\" data-role=\"screenshot-input\" style=\"display:none;\" aria-label=\"Choose screenshot\" />\n\t\t\t\t\t\t\t<button type=\"button\" class=\"fb-upb ${btnDisabled ? 'fb-upb--dis' : ''}\" data-role=\"screenshot-pick\" ${btnDisabled ? 'disabled' : ''} style=\"border:1px solid ${theme.border};color:${theme.text};\">\n\t\t\t\t\t\t\t\t${\n\t\t\t\t\t\t\t\t\tisUploadingScreenshot\n\t\t\t\t\t\t\t\t\t\t? '<span class=\"fb-ups\"></span> Uploading...'\n\t\t\t\t\t\t\t\t\t\t: '📷 Add screenshot'\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t${extrasHtml}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t`\n\t\t\t\t})()\n\t\t\t: ''\n\n\t\tconst emailBlockHtml = showEmailOption\n\t\t\t? `\n\t\t\t\t<div class=\"fb-email\">\n\t\t\t\t\t<label class=\"fb-email-lbl\" style=\"color:${theme.text}\">\n\t\t\t\t\t\t<input type=\"checkbox\" class=\"fb-email-cb\" data-role=\"share-email\" ${shareEmail ? 'checked' : ''} aria-label=\"Share email\" />\n\t\t\t\t\t\t<span>Share my email</span>\n\t\t\t\t\t</label>\n\t\t\t\t\t${\n\t\t\t\t\t\tshareEmail\n\t\t\t\t\t\t\t? `<input type=\"email\" class=\"fb-email-inp\" data-role=\"email-input\" value=\"${escapeHtml(userEmail)}\" placeholder=\"your.email@example.com\" aria-label=\"Email\" maxlength=\"254\" autocomplete=\"email\" style=\"border:1px solid ${theme.border};color:${theme.text};background-color:${theme.background};\" />`\n\t\t\t\t\t\t\t: ''\n\t\t\t\t\t}\n\t\t\t\t</div>\n\t\t\t`\n\t\t\t: ''\n\n\t\tconst submitDisabled = isSubmitting\n\t\tconst submitStyle = `background:linear-gradient(135deg, ${theme.primary}, ${getGradientEnd(theme.primary)});color:#ffffff;${submitDisabled ? 'opacity:0.6;cursor:not-allowed;' : ''}`\n\n\t\tpanelEl.innerHTML = `\n\t\t\t<div class=\"fb-cnt\">\n\t\t\t\t<div class=\"fb-hdr\" style=\"border-bottom:1px solid ${theme.border}\">\n\t\t\t\t\t<h2 id=\"usero-feedback-title\" class=\"fb-ttl\" style=\"color:${theme.text}\">${escapeHtml(title)}</h2>\n\t\t\t\t\t${messageHtml}\n\t\t\t\t\t<button class=\"fb-close-btn\" data-role=\"close\" style=\"color:${theme.text}\" aria-label=\"Close\" type=\"button\">✕</button>\n\t\t\t\t</div>\n\t\t\t\t<form data-role=\"form\">\n\t\t\t\t\t<div class=\"fb-es\" role=\"radiogroup\" aria-label=\"Rate experience\">${ratingsHtml}</div>\n\t\t\t\t\t<textarea class=\"fb-ta\" data-role=\"comment\" placeholder=\"${escapeHtml(placeholder)}\" aria-label=\"Comments\" maxlength=\"1000\" rows=\"2\" style=\"border:1px solid ${theme.border};color:${theme.text};background-color:${theme.background};\">${escapeHtml(comment)}</textarea>\n\t\t\t\t\t<div class=\"fb-charcount${lowChars ? ' fb-charcount--low' : ''}\" data-role=\"charcount\" style=\"color:${lowChars ? '#dc2626' : theme.text};opacity:${lowChars ? 1 : 0.6};\">${remaining} chars remaining</div>\n\t\t\t\t\t${screenshotBlockHtml}\n\t\t\t\t\t${emailBlockHtml}\n\t\t\t\t\t<button class=\"fb-sub ${submitDisabled ? 'fb-sub--dis' : ''}\" type=\"submit\" aria-label=\"Submit\" ${submitDisabled ? 'disabled' : ''} style=\"${submitStyle}\">\n\t\t\t\t\t\t${isSubmitting ? '<span class=\"fb-spin\"></span>' : ''}\n\t\t\t\t\t\t${isSubmitting ? 'Submitting...' : 'Send Feedback 🚀'}\n\t\t\t\t\t</button>\n\t\t\t\t</form>\n\t\t\t</div>\n\t\t`\n\n\t\t// Wire up panel-internal events\n\t\tconst form = panelEl.querySelector<HTMLFormElement>('form[data-role=\"form\"]')\n\t\tform?.addEventListener('submit', e => {\n\t\t\te.preventDefault()\n\t\t\tvoid submitForm()\n\t\t})\n\n\t\tpanelEl\n\t\t\t.querySelector<HTMLButtonElement>('button[data-role=\"close\"]')\n\t\t\t?.addEventListener('click', close)\n\n\t\tpanelEl\n\t\t\t.querySelectorAll<HTMLButtonElement>('button[data-rating]')\n\t\t\t.forEach(btn => {\n\t\t\t\tbtn.addEventListener('click', () => {\n\t\t\t\t\tconst value = btn.dataset.rating\n\t\t\t\t\tif (value === '1' || value === '2' || value === '3' || value === '4') {\n\t\t\t\t\t\tselectedRating = Number(value) as FeedbackRating\n\t\t\t\t\t\trender()\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t})\n\n\t\tconst textarea = panelEl.querySelector<HTMLTextAreaElement>(\n\t\t\t'textarea[data-role=\"comment\"]',\n\t\t)\n\t\tif (textarea) {\n\t\t\ttextarea.addEventListener('input', () => {\n\t\t\t\tif (textarea.value.length <= 1000) {\n\t\t\t\t\tcomment = textarea.value\n\t\t\t\t\t// Update char count without full rerender to avoid losing focus.\n\t\t\t\t\t// IMPORTANT: target by stable class. A previous selector\n\t\t\t\t\t// `.fb-cnt form > div > div` matched the first rating tile,\n\t\t\t\t\t// hijacking it with the char-count text on every keystroke.\n\t\t\t\t\tconst counter = panelEl.querySelector<HTMLDivElement>(\n\t\t\t\t\t\t'[data-role=\"charcount\"]',\n\t\t\t\t\t)\n\t\t\t\t\tif (counter) {\n\t\t\t\t\t\tconst left = 1000 - comment.length\n\t\t\t\t\t\tcounter.textContent = `${left} chars remaining`\n\t\t\t\t\t\tcounter.style.color = left < 50 ? '#dc2626' : theme.text\n\t\t\t\t\t\tcounter.style.opacity = left < 50 ? '1' : '0.6'\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\n\t\tconst shareCb = panelEl.querySelector<HTMLInputElement>(\n\t\t\t'input[data-role=\"share-email\"]',\n\t\t)\n\t\tshareCb?.addEventListener('change', () => {\n\t\t\tshareEmail = shareCb.checked\n\t\t\trender()\n\t\t})\n\n\t\tconst emailInp = panelEl.querySelector<HTMLInputElement>(\n\t\t\t'input[data-role=\"email-input\"]',\n\t\t)\n\t\temailInp?.addEventListener('input', () => {\n\t\t\tif (emailInp.value.length <= 254) {\n\t\t\t\tuserEmail = emailInp.value\n\t\t\t}\n\t\t})\n\n\t\tconst fileInput = panelEl.querySelector<HTMLInputElement>(\n\t\t\t'input[data-role=\"screenshot-input\"]',\n\t\t)\n\t\tconst pickBtn = panelEl.querySelector<HTMLButtonElement>(\n\t\t\t'button[data-role=\"screenshot-pick\"]',\n\t\t)\n\t\tpickBtn?.addEventListener('click', () => {\n\t\t\tfileInput?.click()\n\t\t})\n\t\tfileInput?.addEventListener('change', () => {\n\t\t\tconst file = fileInput.files?.[0]\n\t\t\tif (!file) return\n\t\t\tvoid handleScreenshotFile(file).finally(() => {\n\t\t\t\tif (fileInput) fileInput.value = ''\n\t\t\t})\n\t\t})\n\t\tpanelEl\n\t\t\t.querySelectorAll<HTMLButtonElement>(\n\t\t\t\t'button[data-role=\"screenshot-remove\"]',\n\t\t\t)\n\t\t\t.forEach(btn => {\n\t\t\t\tbtn.addEventListener('click', () => {\n\t\t\t\t\tconst idx = Number(btn.dataset.index)\n\t\t\t\t\tif (Number.isInteger(idx)) removeScreenshot(idx)\n\t\t\t\t})\n\t\t\t})\n\t}\n\n\tfunction render(): void {\n\t\trenderButton()\n\t\trenderBackdrop()\n\t\trenderPanel()\n\t}\n\n\t// Top-level event listeners\n\tbuttonEl.addEventListener('click', () => {\n\t\tif (isOpen) close()\n\t\telse open()\n\t})\n\tbackdropEl.addEventListener('click', close)\n\n\tconst onKeyDown = (e: KeyboardEvent): void => {\n\t\tif (!isOpen) return\n\t\tif (e.key === 'Escape') close()\n\t\tif (e.key === 'Enter' && (e.metaKey || e.ctrlKey)) {\n\t\t\te.preventDefault()\n\t\t\tvoid submitForm()\n\t\t}\n\t}\n\tdocument.addEventListener('keydown', onKeyDown)\n\n\t// Live OS color-scheme tracking. Only active while the caller has not\n\t// provided an explicit `theme` prop. If they later pass one via update(),\n\t// we detach. If they later clear it (set to undefined), we re-attach.\n\tlet darkMql: MediaQueryList | null = null\n\tlet mqlListener: ((ev: MediaQueryListEvent) => void) | null = null\n\n\tfunction detachMqlListener(): void {\n\t\tif (darkMql && mqlListener) {\n\t\t\tdarkMql.removeEventListener('change', mqlListener)\n\t\t}\n\t\tdarkMql = null\n\t\tmqlListener = null\n\t}\n\n\tfunction attachMqlListener(): void {\n\t\tif (darkMql) return\n\t\tif (typeof window === 'undefined' || typeof window.matchMedia !== 'function') return\n\t\tdarkMql = window.matchMedia('(prefers-color-scheme: dark)')\n\t\tmqlListener = () => {\n\t\t\t// Only react if user still hasn't overridden the theme.\n\t\t\tif (userThemeOverride !== undefined) return\n\t\t\ttheme = resolveTheme(undefined)\n\t\t\trender()\n\t\t}\n\t\tdarkMql.addEventListener('change', mqlListener)\n\t}\n\n\tif (userThemeOverride === undefined) attachMqlListener()\n\n\t// Initial paint\n\trender()\n\n\tlet destroyed = false\n\treturn {\n\t\tdestroy: () => {\n\t\t\tif (destroyed) return\n\t\t\tdestroyed = true\n\t\t\tdocument.removeEventListener('keydown', onKeyDown)\n\t\t\tdetachMqlListener()\n\t\t\thost.remove()\n\t\t},\n\t\topen,\n\t\tclose,\n\t\tupdate: next => {\n\t\t\tif (destroyed) return\n\t\t\tlet needsRender = false\n\t\t\tif (next.position !== undefined && next.position !== position) {\n\t\t\t\tposition = next.position\n\t\t\t\tneedsRender = true\n\t\t\t}\n\t\t\tif ('theme' in next) {\n\t\t\t\t// Caller opted in/out of explicit theme control. Track the\n\t\t\t\t// override so the matchMedia listener and any further\n\t\t\t\t// resolutions know whether the user is in charge.\n\t\t\t\tuserThemeOverride = next.theme\n\t\t\t\ttheme = resolveTheme(userThemeOverride)\n\t\t\t\tif (userThemeOverride === undefined) attachMqlListener()\n\t\t\t\telse detachMqlListener()\n\t\t\t\tneedsRender = true\n\t\t\t}\n\t\t\tif (next.title !== undefined && next.title !== title) {\n\t\t\t\ttitle = next.title\n\t\t\t\tneedsRender = true\n\t\t\t}\n\t\t\tif (next.placeholder !== undefined && next.placeholder !== placeholder) {\n\t\t\t\tplaceholder = next.placeholder\n\t\t\t\tneedsRender = true\n\t\t\t}\n\t\t\tif (\n\t\t\t\tnext.showEmailOption !== undefined &&\n\t\t\t\tnext.showEmailOption !== showEmailOption\n\t\t\t) {\n\t\t\t\tshowEmailOption = next.showEmailOption\n\t\t\t\tneedsRender = true\n\t\t\t}\n\t\t\tif (\n\t\t\t\tnext.showScreenshotOption !== undefined &&\n\t\t\t\tnext.showScreenshotOption !== showScreenshotOption\n\t\t\t) {\n\t\t\t\tshowScreenshotOption = next.showScreenshotOption\n\t\t\t\tneedsRender = true\n\t\t\t}\n\t\t\t// Non-render-affecting props: just swap refs.\n\t\t\tif ('environment' in next) environment = next.environment\n\t\t\tif ('metadata' in next) metadata = next.metadata\n\t\t\tif ('onSubmit' in next) onSubmit = next.onSubmit\n\t\t\tif ('onError' in next) onError = next.onError\n\t\t\tif ('onOpen' in next) onOpen = next.onOpen\n\t\t\tif ('onClose' in next) onClose = next.onClose\n\t\t\tif (needsRender) render()\n\t\t},\n\t}\n}\n"]}
package/dist/vanilla.cjs CHANGED
@@ -604,6 +604,19 @@ var FEEDBACK_CSS = `
604
604
  `;
605
605
 
606
606
  // src/vanilla.ts
607
+ function resolveBaseTheme() {
608
+ if (typeof window === "undefined" || typeof window.matchMedia !== "function") {
609
+ return DARK_THEME;
610
+ }
611
+ if (window.matchMedia("(prefers-color-scheme: dark)").matches) return DARK_THEME;
612
+ if (window.matchMedia("(prefers-color-scheme: light)").matches) return DEFAULT_THEME;
613
+ return DARK_THEME;
614
+ }
615
+ function resolveTheme(userTheme) {
616
+ const base = resolveBaseTheme();
617
+ if (!userTheme) return base;
618
+ return { ...base, ...userTheme };
619
+ }
607
620
  var EMAIL_STORAGE_KEY = "feedback_user_email";
608
621
  function escapeHtml(value) {
609
622
  return value.replace(/[&<>"']/g, (ch) => {
@@ -666,7 +679,8 @@ function initUseroFeedbackWidget(props) {
666
679
  };
667
680
  }
668
681
  let position = props.position ?? "right";
669
- let theme = mergeTheme(props.theme);
682
+ let userThemeOverride = props.theme;
683
+ let theme = resolveTheme(userThemeOverride);
670
684
  let title = props.title ?? "Share Feedback";
671
685
  let placeholder = props.placeholder ?? "Tell us what you think... (optional)";
672
686
  let showEmailOption = props.showEmailOption ?? true;
@@ -907,11 +921,7 @@ function initUseroFeedbackWidget(props) {
907
921
  <form data-role="form">
908
922
  <div class="fb-es" role="radiogroup" aria-label="Rate experience">${ratingsHtml}</div>
909
923
  <textarea class="fb-ta" data-role="comment" placeholder="${escapeHtml(placeholder)}" aria-label="Comments" maxlength="1000" rows="2" style="border:1px solid ${theme.border};color:${theme.text};background-color:${theme.background};">${escapeHtml(comment)}</textarea>
910
- <div style="display:flex;justify-content:flex-end;margin-bottom:8px;">
911
- <div style="font-size:12px;color:${lowChars ? "#dc2626" : theme.text};opacity:${lowChars ? 1 : 0.6};margin-left:auto;">
912
- ${remaining} chars remaining
913
- </div>
914
- </div>
924
+ <div class="fb-charcount${lowChars ? " fb-charcount--low" : ""}" data-role="charcount" style="color:${lowChars ? "#dc2626" : theme.text};opacity:${lowChars ? 1 : 0.6};">${remaining} chars remaining</div>
915
925
  ${screenshotBlockHtml}
916
926
  ${emailBlockHtml}
917
927
  <button class="fb-sub ${submitDisabled ? "fb-sub--dis" : ""}" type="submit" aria-label="Submit" ${submitDisabled ? "disabled" : ""} style="${submitStyle}">
@@ -944,7 +954,7 @@ function initUseroFeedbackWidget(props) {
944
954
  if (textarea.value.length <= 1e3) {
945
955
  comment = textarea.value;
946
956
  const counter = panelEl.querySelector(
947
- ".fb-cnt form > div > div"
957
+ '[data-role="charcount"]'
948
958
  );
949
959
  if (counter) {
950
960
  const left = 1e3 - comment.length;
@@ -1014,6 +1024,27 @@ function initUseroFeedbackWidget(props) {
1014
1024
  }
1015
1025
  };
1016
1026
  document.addEventListener("keydown", onKeyDown);
1027
+ let darkMql = null;
1028
+ let mqlListener = null;
1029
+ function detachMqlListener() {
1030
+ if (darkMql && mqlListener) {
1031
+ darkMql.removeEventListener("change", mqlListener);
1032
+ }
1033
+ darkMql = null;
1034
+ mqlListener = null;
1035
+ }
1036
+ function attachMqlListener() {
1037
+ if (darkMql) return;
1038
+ if (typeof window === "undefined" || typeof window.matchMedia !== "function") return;
1039
+ darkMql = window.matchMedia("(prefers-color-scheme: dark)");
1040
+ mqlListener = () => {
1041
+ if (userThemeOverride !== void 0) return;
1042
+ theme = resolveTheme(void 0);
1043
+ render();
1044
+ };
1045
+ darkMql.addEventListener("change", mqlListener);
1046
+ }
1047
+ if (userThemeOverride === void 0) attachMqlListener();
1017
1048
  render();
1018
1049
  let destroyed = false;
1019
1050
  return {
@@ -1021,6 +1052,7 @@ function initUseroFeedbackWidget(props) {
1021
1052
  if (destroyed) return;
1022
1053
  destroyed = true;
1023
1054
  document.removeEventListener("keydown", onKeyDown);
1055
+ detachMqlListener();
1024
1056
  host.remove();
1025
1057
  },
1026
1058
  open,
@@ -1032,8 +1064,11 @@ function initUseroFeedbackWidget(props) {
1032
1064
  position = next.position;
1033
1065
  needsRender = true;
1034
1066
  }
1035
- if (next.theme !== void 0) {
1036
- theme = mergeTheme(next.theme);
1067
+ if ("theme" in next) {
1068
+ userThemeOverride = next.theme;
1069
+ theme = resolveTheme(userThemeOverride);
1070
+ if (userThemeOverride === void 0) attachMqlListener();
1071
+ else detachMqlListener();
1037
1072
  needsRender = true;
1038
1073
  }
1039
1074
  if (next.title !== void 0 && next.title !== title) {
@@ -1067,5 +1102,6 @@ exports.DARK_THEME = DARK_THEME;
1067
1102
  exports.DEFAULT_THEME = DEFAULT_THEME;
1068
1103
  exports.initUseroFeedbackWidget = initUseroFeedbackWidget;
1069
1104
  exports.mergeTheme = mergeTheme;
1105
+ exports.resolveTheme = resolveTheme;
1070
1106
  //# sourceMappingURL=vanilla.cjs.map
1071
1107
  //# sourceMappingURL=vanilla.cjs.map