@reqdesk/widget 0.2.0 → 0.3.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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage-BG7rsgWE.js","names":[],"sources":["../src/ofetch-client.ts","../src/api-client.ts","../src/i18n/en.ts","../src/i18n/ar.ts","../src/theme.ts","../src/storage.ts"],"sourcesContent":["import { ofetch } from 'ofetch'\r\nimport type { WidgetError } from './types'\r\n\r\nlet apiKey = ''\r\nlet baseUrl = ''\r\nlet oidcTokenProvider: (() => Promise<{ accessToken: string }>) | null = null\r\n\r\nexport function configureWidgetClient(url: string, key: string) {\r\n baseUrl = url.replace(/\\/$/, '')\r\n apiKey = key\r\n}\r\n\r\nexport function setOidcTokenProvider(provider: (() => Promise<{ accessToken: string }>) | null) {\r\n oidcTokenProvider = provider\r\n}\r\n\r\nexport const widgetFetch = ofetch.create({\r\n timeout: 15000,\r\n retry: 2,\r\n retryStatusCodes: [408, 429, 500, 502, 503, 504],\r\n\r\n async onRequest({ options }) {\r\n options.baseURL = `${baseUrl}/api/v1`\r\n const headers = new Headers(options.headers as HeadersInit)\r\n headers.set('X-API-Key', apiKey)\r\n\r\n // Get fresh token on every request — oidc-spa handles refresh internally\r\n if (oidcTokenProvider) {\r\n try {\r\n const tokens = await oidcTokenProvider()\r\n headers.set('Authorization', `Bearer ${tokens.accessToken}`)\r\n } catch {\r\n // Auth unavailable — continue with API key only\r\n }\r\n }\r\n\r\n options.headers = headers\r\n },\r\n\r\n onResponseError({ response }) {\r\n const body = response._data as Record<string, unknown> | undefined\r\n const error: WidgetError = {\r\n code: (body?.responseCode as string) ?? `HTTP_${response.status}`,\r\n message: (body?.responseMessage as string) ?? response.statusText,\r\n }\r\n throw error\r\n },\r\n})\r\n\r\n// Upload helper using XHR for progress tracking (ofetch doesn't support upload progress)\r\nexport function uploadWithProgress(\r\n path: string,\r\n file: File,\r\n onProgress?: (percent: number) => void,\r\n): Promise<unknown> {\r\n return new Promise((resolve, reject) => {\r\n const xhr = new XMLHttpRequest()\r\n xhr.open('POST', `${baseUrl}${path}`)\r\n xhr.setRequestHeader('X-API-Key', apiKey)\r\n\r\n // For uploads, use cached token (can't await in XHR setup)\r\n // The token was set on the last ofetch request via the interceptor\r\n if (oidcTokenProvider) {\r\n oidcTokenProvider()\r\n .then(tokens => {\r\n xhr.setRequestHeader('Authorization', `Bearer ${tokens.accessToken}`)\r\n sendXhr()\r\n })\r\n .catch(() => sendXhr())\r\n } else {\r\n sendXhr()\r\n }\r\n\r\n function sendXhr() {\r\n if (onProgress) {\r\n xhr.upload.addEventListener('progress', (e) => {\r\n if (e.lengthComputable) onProgress(Math.round((e.loaded / e.total) * 100))\r\n })\r\n }\r\n\r\n xhr.addEventListener('load', () => {\r\n if (xhr.status >= 200 && xhr.status < 300) {\r\n try { resolve(JSON.parse(xhr.responseText)) } catch { resolve({}) }\r\n } else {\r\n reject({ code: `HTTP_${xhr.status}`, message: xhr.statusText } as WidgetError)\r\n }\r\n })\r\n\r\n xhr.addEventListener('error', () => {\r\n reject({ code: 'NETWORK_ERROR', message: 'Network error during upload' } as WidgetError)\r\n })\r\n\r\n const form = new FormData()\r\n form.append('file', file)\r\n xhr.send(form)\r\n }\r\n })\r\n}\r\n","import { widgetFetch, uploadWithProgress, configureWidgetClient, setOidcTokenProvider } from './ofetch-client'\r\nimport type { SubmitTicketData, TicketResult, TrackedTicketResult, PublicReply } from './types'\r\n\r\n// Re-export client configuration\r\nexport { configureWidgetClient as configureClient, setOidcTokenProvider }\r\n\r\n// Legacy compat — setBearerToken is no longer needed (ofetch interceptor handles it)\r\nexport function setBearerToken(_token: string | null) {\r\n // No-op: token is now fetched per-request via oidcTokenProvider\r\n}\r\n\r\nfunction generateIdempotencyKey(): string {\r\n return crypto.randomUUID()\r\n}\r\n\r\n// ── JSON:API response shape ──────────────────────────────────────────────────\r\n\r\ninterface ApiResponse<T> {\r\n responseCode: string\r\n responseMessage: string\r\n httpCode: number\r\n data: T\r\n meta?: Record<string, unknown>\r\n included?: unknown[]\r\n}\r\n\r\n// ── Ticket Submission ────────────────────────────────────────────────────────\r\n\r\nexport async function submitTicket(projectId: string, data: SubmitTicketData): Promise<TicketResult> {\r\n const res = await widgetFetch<ApiResponse<{ id: string; attributes: { ticketNumber: string; status: string } }> & { meta?: { trackingToken?: string } }>(\r\n `/projects/${projectId}/tickets`,\r\n {\r\n method: 'POST',\r\n body: {\r\n title: data.title,\r\n description: data.description,\r\n priority: data.priority,\r\n categoryId: data.categoryId,\r\n email: data.email,\r\n clientMetadata: data.clientMetadata,\r\n },\r\n headers: { 'Idempotency-Key': generateIdempotencyKey() },\r\n },\r\n )\r\n\r\n return {\r\n id: res.data.id,\r\n ticketNumber: res.data.attributes.ticketNumber,\r\n trackingToken: res.meta?.trackingToken as string | undefined,\r\n status: res.data.attributes.status,\r\n }\r\n}\r\n\r\n// ── Tracking ─────────────────────────────────────────────────────────────────\r\n\r\nexport async function trackTicket(token: string): Promise<TrackedTicketResult> {\r\n const res = await widgetFetch<ApiResponse<{ id: string; attributes: { ticketNumber: string; title: string; status: string; priority: string; createdAt: string } }>>(`/tickets/track?token=${encodeURIComponent(token)}`)\r\n\r\n const included = (res.included ?? []) as Array<{ id: string; type: string; attributes: Record<string, unknown> }>\r\n\r\n return {\r\n id: res.data.id,\r\n ticketNumber: res.data.attributes.ticketNumber,\r\n title: res.data.attributes.title,\r\n status: res.data.attributes.status,\r\n priority: res.data.attributes.priority,\r\n createdAt: res.data.attributes.createdAt,\r\n replies: included\r\n .filter(i => i.type === 'public-reply')\r\n .map(i => ({\r\n id: i.id,\r\n body: i.attributes.body as string,\r\n authorName: i.attributes.authorName as string,\r\n isStaff: i.attributes.isStaff as boolean,\r\n createdAt: i.attributes.createdAt as string,\r\n })),\r\n }\r\n}\r\n\r\nexport async function submitTrackingReply(token: string, body: string): Promise<void> {\r\n await widgetFetch('/tickets/track/reply', {\r\n method: 'POST',\r\n body: { trackingToken: token, body },\r\n })\r\n}\r\n\r\n// ── Categories ───────────────────────────────────────────────────────────────\r\n\r\nexport interface CategoryItem {\r\n id: string\r\n name: string\r\n parentId?: string\r\n hasChildren: boolean\r\n}\r\n\r\nexport async function getCategories(projectId: string, parentId?: string | null): Promise<CategoryItem[]> {\r\n const params = new URLSearchParams({ 'filter[active]': 'true' })\r\n if (parentId === null || parentId === undefined) params.set('filter[parentId]', 'root')\r\n else params.set('filter[parentId]', parentId)\r\n\r\n const res = await widgetFetch<ApiResponse<Array<{ id: string; attributes: { name: string; parentId?: string; hasChildren: boolean } }>>>(\r\n `/projects/${projectId}/categories?${params}`,\r\n )\r\n const items = Array.isArray(res.data) ? res.data : []\r\n return items.map(c => ({\r\n id: c.id,\r\n name: c.attributes.name,\r\n parentId: c.attributes.parentId,\r\n hasChildren: c.attributes.hasChildren,\r\n }))\r\n}\r\n\r\nexport async function closeTicket(ticketId: string): Promise<void> {\r\n await widgetFetch(`/tickets/${ticketId}/status`, {\r\n method: 'PUT',\r\n body: { status: 'resolved' },\r\n })\r\n}\r\n\r\n// ── Attachments ──────────────────────────────────────────────────────────────\r\n\r\nexport interface AttachmentMeta {\r\n id: string\r\n fileName: string\r\n contentType: string\r\n fileSize: number\r\n downloadUrl?: string\r\n downloadUrlExpiresAt?: string\r\n}\r\n\r\nexport function uploadAttachment(\r\n ticketId: string,\r\n file: File,\r\n onProgress?: (percent: number) => void,\r\n): Promise<AttachmentMeta> {\r\n return uploadWithProgress(`/api/v1/tickets/${ticketId}/attachments`, file, onProgress) as Promise<AttachmentMeta>\r\n}\r\n\r\nexport function uploadReplyAttachment(\r\n ticketId: string,\r\n replyId: string,\r\n file: File,\r\n onProgress?: (percent: number) => void,\r\n): Promise<AttachmentMeta> {\r\n return uploadWithProgress(`/api/v1/tickets/${ticketId}/replies/${replyId}/attachments`, file, onProgress) as Promise<AttachmentMeta>\r\n}\r\n\r\nexport async function getAttachmentDownloadUrl(attachmentId: string): Promise<{ downloadUrl: string; expiresAt: string }> {\r\n return widgetFetch(`/attachments/${attachmentId}/download`)\r\n}\r\n\r\n// ── Ticket Detail ────────────────────────────────────────────────────────────\r\n\r\nexport interface TicketDetail {\r\n id: string\r\n ticketNumber: string\r\n title: string\r\n description?: string\r\n status: string\r\n priority: string\r\n createdAt: string\r\n replies: PublicReply[]\r\n attachments: AttachmentMeta[]\r\n}\r\n\r\nexport async function getTicketDetail(ticketId: string, includeMedia = true): Promise<TicketDetail> {\r\n const include = includeMedia ? '?include=media' : ''\r\n const res = await widgetFetch<ApiResponse<{\r\n id: string\r\n attributes: {\r\n ticketNumber: string\r\n title: string\r\n description?: string\r\n status: string\r\n priority: string\r\n timestamps: { createdAt: string }\r\n }\r\n }>>(`/tickets/${ticketId}${include}`)\r\n\r\n const included = (res.included ?? []) as Array<{ id: string; type: string; attributes: Record<string, unknown> }>\r\n\r\n const replies = included\r\n .filter(i => i.type === 'ticket-reply')\r\n .map(i => ({\r\n id: i.id,\r\n body: i.attributes.body as string,\r\n authorName: i.attributes.authorName as string ?? 'Anonymous',\r\n isStaff: i.attributes.isStaff as boolean ?? false,\r\n createdAt: (i.attributes.timestamps as Record<string, string>)?.createdAt ?? i.attributes.createdAt as string ?? '',\r\n }))\r\n\r\n const attachments = included\r\n .filter(i => i.type === 'attachment')\r\n .map(i => ({\r\n id: i.id,\r\n fileName: i.attributes.fileName as string,\r\n contentType: i.attributes.contentType as string,\r\n fileSize: i.attributes.fileSize as number,\r\n downloadUrl: i.attributes.downloadUrl as string | undefined,\r\n downloadUrlExpiresAt: i.attributes.downloadUrlExpiresAt as string | undefined,\r\n }))\r\n\r\n return {\r\n id: res.data.id,\r\n ticketNumber: res.data.attributes.ticketNumber,\r\n title: res.data.attributes.title,\r\n description: res.data.attributes.description,\r\n status: res.data.attributes.status,\r\n priority: res.data.attributes.priority,\r\n createdAt: res.data.attributes.timestamps?.createdAt ?? '',\r\n replies,\r\n attachments,\r\n }\r\n}\r\n\r\nexport async function submitReply(ticketId: string, body: string): Promise<{ id: string }> {\r\n const res = await widgetFetch<ApiResponse<{ id: string }>>(`/tickets/${ticketId}/replies`, {\r\n method: 'POST',\r\n body: { body, isInternal: false },\r\n })\r\n return { id: res.data.id }\r\n}\r\n\r\n// ── My Tickets ───────────────────────────────────────────────────────────────\r\n\r\nexport interface WidgetUser {\r\n userId: string\r\n email: string\r\n displayName: string\r\n}\r\n\r\nexport async function resolveWidgetUser(projectId: string, email: string): Promise<WidgetUser | null> {\r\n try {\r\n return await widgetFetch<WidgetUser>(`/projects/${projectId}/widget-users?email=${encodeURIComponent(email)}`)\r\n } catch {\r\n return null\r\n }\r\n}\r\n\r\nexport interface TicketListItem {\r\n id: string\r\n ticketNumber: string\r\n title: string\r\n status: string\r\n priority: string\r\n createdAt: string\r\n}\r\n\r\nexport async function listMyTickets(\r\n projectId: string,\r\n userId: string,\r\n page = 1,\r\n pageSize = 20,\r\n): Promise<TicketListItem[]> {\r\n const params = new URLSearchParams({\r\n 'filter[createdBy]': userId,\r\n 'sort': '-created_at',\r\n 'page[number]': String(page),\r\n 'page[size]': String(pageSize),\r\n })\r\n\r\n const res = await widgetFetch<ApiResponse<Array<{\r\n id: string\r\n attributes: {\r\n ticketNumber: string\r\n title: string\r\n status: string\r\n priority: string\r\n timestamps: { createdAt: string }\r\n }\r\n }>>>(`/projects/${projectId}/tickets?${params}`)\r\n\r\n const items = Array.isArray(res.data) ? res.data : []\r\n return items.map(t => ({\r\n id: t.id,\r\n ticketNumber: t.attributes.ticketNumber,\r\n title: t.attributes.title,\r\n status: t.attributes.status,\r\n priority: t.attributes.priority,\r\n createdAt: t.attributes.timestamps?.createdAt ?? '',\r\n }))\r\n}\r\n","export const en: Record<string, string> = {\r\n 'widget.title': 'Support',\r\n 'widget.newTicket': 'New Ticket',\r\n 'widget.trackTicket': 'Track Ticket',\r\n 'form.title': 'Title',\r\n 'form.titlePlaceholder': 'Brief summary of your issue',\r\n 'form.description': 'Description',\r\n 'form.descriptionPlaceholder': 'Describe your issue in detail...',\r\n 'form.email': 'Email',\r\n 'form.emailPlaceholder': 'your@email.com',\r\n 'form.priority': 'Priority',\r\n 'form.priorityLow': 'Low',\r\n 'form.priorityMedium': 'Medium',\r\n 'form.priorityHigh': 'High',\r\n 'form.priorityUrgent': 'Urgent',\r\n 'form.category': 'Category',\r\n 'form.categoryNone': 'Select a category',\r\n 'form.submit': 'Submit Ticket',\r\n 'form.submitting': 'Submitting...',\r\n 'form.cancel': 'Cancel',\r\n 'form.attachment': 'Attach files',\r\n 'success.title': 'Ticket Submitted!',\r\n 'success.ticketNumber': 'Ticket #',\r\n 'success.trackingToken': 'Your tracking token:',\r\n 'success.trackingHint': 'Save this token to track your ticket status.',\r\n 'success.copyToken': 'Copy Token',\r\n 'success.copied': 'Copied!',\r\n 'success.close': 'Close',\r\n 'success.trackNow': 'Track Ticket',\r\n 'tracker.title': 'Track Your Ticket',\r\n 'tracker.tokenPlaceholder': 'Enter your tracking token (trk_...)',\r\n 'tracker.submit': 'Track',\r\n 'tracker.tracking': 'Tracking...',\r\n 'tracker.status': 'Status',\r\n 'tracker.priority': 'Priority',\r\n 'tracker.created': 'Created',\r\n 'tracker.replies': 'Replies',\r\n 'tracker.noReplies': 'No replies yet.',\r\n 'tracker.replyPlaceholder': 'Write a reply...',\r\n 'tracker.sendReply': 'Send Reply',\r\n 'tracker.sending': 'Sending...',\r\n 'tracker.back': 'Back',\r\n 'tracker.staff': 'Staff',\r\n 'error.generic': 'Something went wrong. Please try again.',\r\n 'error.network': 'Network error. Check your connection.',\r\n 'error.tokenInvalid': 'Invalid or expired tracking token.',\r\n 'error.required': 'This field is required.',\r\n 'error.emailInvalid': 'Please enter a valid email address.',\r\n 'error.titleMin': 'Title must be at least 5 characters.',\r\n 'portal.title': 'Support Portal',\r\n 'portal.myTickets': 'My Tickets',\r\n 'portal.noTickets': 'No tickets yet.',\r\n 'portal.newTicket': 'New Ticket',\r\n 'widget.close': 'Close',\r\n 'menu.newTicket': 'Submit a Ticket',\r\n 'menu.newTicketDesc': 'Create a new support request',\r\n 'menu.myTickets': 'My Tickets',\r\n 'menu.myTicketsDesc': 'View and manage your tickets',\r\n 'menu.trackTicket': 'Track a Ticket',\r\n 'menu.trackTicketDesc': 'Check status with your tracking token',\r\n 'menu.knowledgeBase': 'Knowledge Base',\r\n 'menu.knowledgeBaseDesc': 'Browse help articles and guides',\r\n 'menu.myTicketsPlaceholder': 'Sign in to view your tickets.',\r\n 'menu.trackPlaceholder': 'Ticket tracking is coming soon.',\r\n 'menu.kbPlaceholder': 'Knowledge base is coming soon.',\r\n 'menu.preferences': 'Preferences',\r\n 'menu.preferencesDesc': 'Language and appearance settings',\r\n 'prefs.title': 'Preferences',\r\n 'prefs.language': 'Language',\r\n 'prefs.theme': 'Theme',\r\n 'prefs.light': 'Light',\r\n 'prefs.dark': 'Dark',\r\n 'prefs.auto': 'System',\r\n 'branding.poweredBy': 'Powered by',\r\n 'attach.dropzone': 'Drop files here or click to browse',\r\n 'attach.dropzoneActive': 'Drop files here',\r\n 'attach.maxFiles': 'Maximum {max} files',\r\n 'attach.remove': 'Remove',\r\n 'attach.uploading': 'Uploading files...',\r\n 'attach.uploadProgress': 'Uploading {name}... {percent}%',\r\n 'attach.invalidType': 'File type not allowed',\r\n 'attach.tooLarge': 'File exceeds maximum size',\r\n 'attach.tooMany': 'Maximum number of files reached',\r\n 'attach.download': 'Download',\r\n 'mytickets.title': 'My Tickets',\r\n 'mytickets.emailPrompt': 'Enter your email to view your tickets',\r\n 'mytickets.emailPlaceholder': 'your@email.com',\r\n 'mytickets.rememberMe': 'Remember me',\r\n 'mytickets.lookup': 'Find My Tickets',\r\n 'mytickets.lookingUp': 'Looking up...',\r\n 'mytickets.noTickets': 'No tickets yet.',\r\n 'mytickets.submitFirst': 'Submit your first ticket',\r\n 'mytickets.noAccount': 'No tickets found for this email.',\r\n 'detail.description': 'Description',\r\n 'detail.attachments': 'Attachments',\r\n 'detail.noAttachments': 'No attachments',\r\n 'detail.replies': 'Conversation',\r\n 'detail.noReplies': 'No replies yet.',\r\n 'detail.replyPlaceholder': 'Write a reply...',\r\n 'detail.sendReply': 'Send',\r\n 'detail.sending': 'Sending...',\r\n 'detail.staff': 'Staff',\r\n 'detail.you': 'You',\r\n 'detail.loading': 'Loading ticket...',\r\n 'track.title': 'Track a Ticket',\r\n 'track.tokenPlaceholder': 'Enter tracking token (trk_...)',\r\n 'track.submit': 'Track',\r\n 'track.tracking': 'Tracking...',\r\n 'track.recentTickets': 'Recent Tickets',\r\n 'track.invalidToken': 'Invalid or expired tracking token.',\r\n 'prefs.clearEmail': 'Clear saved email',\r\n 'prefs.emailCleared': 'Email cleared',\r\n 'auth.login': 'Login',\r\n 'auth.logout': 'Logout',\r\n 'auth.sessionExpired': 'Session expired. Please log in again.',\r\n 'prefs.accentColor': 'Accent Color',\r\n 'form.categoryPlaceholder': 'Select a category',\r\n 'form.categoryBack': 'Back',\r\n 'form.categorySelected': 'Category',\r\n 'diag.title': 'Share diagnostic info',\r\n 'diag.hint': 'Help us resolve your issue faster',\r\n 'diag.screenResolution': 'Screen resolution',\r\n 'diag.deviceType': 'Device type',\r\n 'diag.timezone': 'Timezone',\r\n 'diag.referrerUrl': 'Referrer URL',\r\n 'diag.language': 'Browser language',\r\n 'diag.platform': 'Platform',\r\n 'detail.resolve': 'Mark as Resolved',\r\n 'detail.resolving': 'Resolving...',\r\n 'detail.resolved': 'Resolved',\r\n 'detail.sla': 'SLA',\r\n}\r\n","export const ar: Record<string, string> = {\r\n 'widget.title': 'الدعم',\r\n 'widget.newTicket': 'تذكرة جديدة',\r\n 'widget.trackTicket': 'تتبع التذكرة',\r\n 'form.title': 'العنوان',\r\n 'form.titlePlaceholder': 'ملخص موجز لمشكلتك',\r\n 'form.description': 'الوصف',\r\n 'form.descriptionPlaceholder': 'صف مشكلتك بالتفصيل...',\r\n 'form.email': 'البريد الإلكتروني',\r\n 'form.emailPlaceholder': 'your@email.com',\r\n 'form.priority': 'الأولوية',\r\n 'form.priorityLow': 'منخفضة',\r\n 'form.priorityMedium': 'متوسطة',\r\n 'form.priorityHigh': 'عالية',\r\n 'form.priorityUrgent': 'عاجلة',\r\n 'form.category': 'الفئة',\r\n 'form.categoryNone': 'اختر فئة',\r\n 'form.submit': 'إرسال التذكرة',\r\n 'form.submitting': 'جارِ الإرسال...',\r\n 'form.cancel': 'إلغاء',\r\n 'form.attachment': 'إرفاق ملفات',\r\n 'success.title': 'تم إرسال التذكرة!',\r\n 'success.ticketNumber': 'تذكرة #',\r\n 'success.trackingToken': 'رمز التتبع الخاص بك:',\r\n 'success.trackingHint': 'احفظ هذا الرمز لتتبع حالة تذكرتك.',\r\n 'success.copyToken': 'نسخ الرمز',\r\n 'success.copied': 'تم النسخ!',\r\n 'success.close': 'إغلاق',\r\n 'success.trackNow': 'تتبع التذكرة',\r\n 'tracker.title': 'تتبع تذكرتك',\r\n 'tracker.tokenPlaceholder': 'أدخل رمز التتبع الخاص بك (trk_...)',\r\n 'tracker.submit': 'تتبع',\r\n 'tracker.tracking': 'جارِ التتبع...',\r\n 'tracker.status': 'الحالة',\r\n 'tracker.priority': 'الأولوية',\r\n 'tracker.created': 'تاريخ الإنشاء',\r\n 'tracker.replies': 'الردود',\r\n 'tracker.noReplies': 'لا توجد ردود بعد.',\r\n 'tracker.replyPlaceholder': 'اكتب رداً...',\r\n 'tracker.sendReply': 'إرسال الرد',\r\n 'tracker.sending': 'جارِ الإرسال...',\r\n 'tracker.back': 'رجوع',\r\n 'tracker.staff': 'فريق الدعم',\r\n 'error.generic': 'حدث خطأ ما. يرجى المحاولة مرة أخرى.',\r\n 'error.network': 'خطأ في الشبكة. تحقق من اتصالك.',\r\n 'error.tokenInvalid': 'رمز التتبع غير صالح أو منتهي الصلاحية.',\r\n 'error.required': 'هذا الحقل مطلوب.',\r\n 'error.emailInvalid': 'يرجى إدخال بريد إلكتروني صالح.',\r\n 'error.titleMin': 'يجب أن يكون العنوان 5 أحرف على الأقل.',\r\n 'portal.title': 'بوابة الدعم',\r\n 'portal.myTickets': 'تذاكري',\r\n 'portal.noTickets': 'لا توجد تذاكر بعد.',\r\n 'portal.newTicket': 'تذكرة جديدة',\r\n 'widget.close': 'إغلاق',\r\n 'menu.newTicket': 'إرسال تذكرة',\r\n 'menu.newTicketDesc': 'إنشاء طلب ��عم جديد',\r\n 'menu.myTickets': 'تذاكري',\r\n 'menu.myTicketsDesc': 'عرض وإدارة تذاكرك',\r\n 'menu.trackTicket': 'تتبع تذكرة',\r\n 'menu.trackTicketDesc': 'تحقق من الحالة باستخدام رمز التتبع',\r\n 'menu.knowledgeBase': 'قاعدة المعرفة',\r\n 'menu.knowledgeBaseDesc': 'تصفح مقالات المساعدة والأدلة',\r\n 'menu.myTicketsPlaceholder': 'سجّل الدخول ��عرض تذاكرك.',\r\n 'menu.trackPlaceholder': 'تتبع التذاكر قريبًا.',\r\n 'menu.kbPlaceholder': 'قاعدة المعرفة قريبًا.',\r\n 'menu.preferences': 'التفضيلات',\r\n 'menu.preferencesDesc': 'إعدادات اللغة والمظهر',\r\n 'prefs.title': 'التفضيلات',\r\n 'prefs.language': 'اللغة',\r\n 'prefs.theme': 'المظهر',\r\n 'prefs.light': 'فاتح',\r\n 'prefs.dark': 'داكن',\r\n 'prefs.auto': 'النظام',\r\n 'branding.poweredBy': 'مدعوم من',\r\n 'attach.dropzone': 'اسحب الملفات هنا أو انقر للتصفح',\r\n 'attach.dropzoneActive': 'أفلت الملفات هنا',\r\n 'attach.maxFiles': 'بحد أقصى {max} ملفات',\r\n 'attach.remove': 'إزالة',\r\n 'attach.uploading': 'جارِ رفع الملفات...',\r\n 'attach.uploadProgress': 'جارِ رفع {name}... {percent}%',\r\n 'attach.invalidType': 'نوع الملف غير مسموح',\r\n 'attach.tooLarge': 'حجم الملف يتجاوز الحد الأقصى',\r\n 'attach.tooMany': 'تم الوصول للحد الأقصى لعدد الملفات',\r\n 'attach.download': 'تحميل',\r\n 'mytickets.title': 'تذاكري',\r\n 'mytickets.emailPrompt': 'أدخل بريدك الإلكتروني لعرض تذاكرك',\r\n 'mytickets.emailPlaceholder': 'your@email.com',\r\n 'mytickets.rememberMe': 'تذكرني',\r\n 'mytickets.lookup': 'البحث عن تذاكري',\r\n 'mytickets.lookingUp': 'جارِ البحث...',\r\n 'mytickets.noTickets': 'لا توجد تذاكر بعد.',\r\n 'mytickets.submitFirst': 'أرسل تذكرتك الأولى',\r\n 'mytickets.noAccount': 'لم يتم العثور على تذاكر لهذا البريد.',\r\n 'detail.description': 'الوصف',\r\n 'detail.attachments': 'المرفقات',\r\n 'detail.noAttachments': 'لا توجد مرفقات',\r\n 'detail.replies': 'المحادثة',\r\n 'detail.noReplies': 'لا توجد ردود بعد.',\r\n 'detail.replyPlaceholder': 'اكتب رداً...',\r\n 'detail.sendReply': 'إرسال',\r\n 'detail.sending': 'جارِ الإرسال...',\r\n 'detail.staff': 'فريق الدعم',\r\n 'detail.you': 'أنت',\r\n 'detail.loading': 'جارِ تحميل التذكرة...',\r\n 'track.title': 'تتبع تذكرة',\r\n 'track.tokenPlaceholder': 'أدخل رمز التتبع (trk_...)',\r\n 'track.submit': 'تتبع',\r\n 'track.tracking': 'جارِ التتبع...',\r\n 'track.recentTickets': 'التذاكر الأخيرة',\r\n 'track.invalidToken': 'رمز التتبع غير صالح أو منتهي.',\r\n 'prefs.clearEmail': 'مسح البريد المحفوظ',\r\n 'prefs.emailCleared': 'تم مسح البريد',\r\n 'auth.login': 'تسجيل الدخول',\r\n 'auth.logout': 'تسجيل الخروج',\r\n 'auth.sessionExpired': 'انتهت الجلسة. يرجى تسجيل الدخول مرة أخرى.',\r\n 'prefs.accentColor': 'لون التمييز',\r\n 'form.categoryPlaceholder': 'اختر فئة',\r\n 'form.categoryBack': 'رجوع',\r\n 'form.categorySelected': 'الفئة',\r\n 'diag.title': 'مشاركة معلومات التشخيص',\r\n 'diag.hint': 'ساعدنا في حل مشكلتك بشكل أسرع',\r\n 'diag.screenResolution': 'دقة الشاشة',\r\n 'diag.deviceType': 'نوع الجهاز',\r\n 'diag.timezone': 'المنطقة الزمنية',\r\n 'diag.referrerUrl': 'رابط المصدر',\r\n 'diag.language': 'لغة المتصفح',\r\n 'diag.platform': 'المنصة',\r\n 'detail.resolve': 'تحديد كمحلول',\r\n 'detail.resolving': 'جارِ الحل...',\r\n 'detail.resolved': 'محلول',\r\n 'detail.sla': 'اتفاقية الخدمة',\r\n}\r\n","import type { ThemeConfig } from './types'\r\n\r\nconst DEFAULT_THEME: Required<Omit<ThemeConfig, 'logo' | 'brandName' | 'hideBranding'>> & { logo?: string; brandName?: string; hideBranding?: boolean } = {\r\n primaryColor: '#42b983',\r\n mode: 'light',\r\n borderRadius: '8px',\r\n fontFamily: 'inherit',\r\n zIndex: 9999,\r\n}\r\n\r\nfunction resolveMode(mode: ThemeConfig['mode']): 'light' | 'dark' {\r\n if (mode === 'auto') {\r\n if (typeof window !== 'undefined' && window.matchMedia?.('(prefers-color-scheme: dark)').matches) {\r\n return 'dark'\r\n }\r\n return 'light'\r\n }\r\n return mode ?? 'light'\r\n}\r\n\r\nexport function themeToVars(theme: ThemeConfig = {}): string {\r\n const t = { ...DEFAULT_THEME, ...theme, mode: resolveMode(theme.mode ?? DEFAULT_THEME.mode) }\r\n const hsl = hexToHsl(t.primaryColor)\r\n const lightL = Math.min(hsl.l + 30, 95)\r\n const darkL = Math.max(hsl.l - 15, 10)\r\n return [\r\n `--rqd-primary: ${t.primaryColor}`,\r\n `--rqd-primary-h: ${hsl.h}`,\r\n `--rqd-primary-s: ${hsl.s}%`,\r\n `--rqd-primary-l: ${hsl.l}%`,\r\n `--rqd-primary-light: hsl(${hsl.h}, ${hsl.s}%, ${lightL}%)`,\r\n `--rqd-primary-dark: hsl(${hsl.h}, ${hsl.s}%, ${darkL}%)`,\r\n `--rqd-radius: ${t.borderRadius}`,\r\n `--rqd-font: ${t.fontFamily}`,\r\n `--rqd-z: ${t.zIndex}`,\r\n `--rqd-bg: ${t.mode === 'dark' ? '#1a1a2e' : '#ffffff'}`,\r\n `--rqd-bg-secondary: ${t.mode === 'dark' ? '#16213e' : '#f8f9fa'}`,\r\n `--rqd-text: ${t.mode === 'dark' ? '#e0e0e0' : '#1a1a2e'}`,\r\n `--rqd-text-secondary: ${t.mode === 'dark' ? '#a0a0b0' : '#6c757d'}`,\r\n `--rqd-border: ${t.mode === 'dark' ? '#2a2a4a' : '#e0e0e0'}`,\r\n `--rqd-input-bg: ${t.mode === 'dark' ? '#0f3460' : '#ffffff'}`,\r\n `--rqd-shadow: ${t.mode === 'dark' ? '0 8px 32px rgba(0,0,0,0.4)' : '0 8px 32px rgba(0,0,0,0.12)'}`,\r\n ].join('; ')\r\n}\r\n\r\n/**\r\n * Returns CSS custom properties as a React-compatible style object.\r\n * React's `style` prop supports custom properties (--var) as keys directly.\r\n */\r\nexport function themeToStyle(theme: ThemeConfig = {}): Record<string, string | number> {\r\n const t = { ...DEFAULT_THEME, ...theme, mode: resolveMode(theme.mode ?? DEFAULT_THEME.mode) }\r\n const hsl = hexToHsl(t.primaryColor)\r\n const lightL = Math.min(hsl.l + 30, 95)\r\n const darkL = Math.max(hsl.l - 15, 10)\r\n return {\r\n '--rqd-primary': t.primaryColor,\r\n '--rqd-primary-h': String(hsl.h),\r\n '--rqd-primary-s': `${hsl.s}%`,\r\n '--rqd-primary-l': `${hsl.l}%`,\r\n '--rqd-primary-light': `hsl(${hsl.h}, ${hsl.s}%, ${lightL}%)`,\r\n '--rqd-primary-dark': `hsl(${hsl.h}, ${hsl.s}%, ${darkL}%)`,\r\n '--rqd-radius': t.borderRadius,\r\n '--rqd-font': t.fontFamily,\r\n '--rqd-z': t.zIndex,\r\n '--rqd-bg': t.mode === 'dark' ? '#1a1a2e' : '#ffffff',\r\n '--rqd-bg-secondary': t.mode === 'dark' ? '#16213e' : '#f8f9fa',\r\n '--rqd-text': t.mode === 'dark' ? '#e0e0e0' : '#1a1a2e',\r\n '--rqd-text-secondary': t.mode === 'dark' ? '#a0a0b0' : '#6c757d',\r\n '--rqd-border': t.mode === 'dark' ? '#2a2a4a' : '#e0e0e0',\r\n '--rqd-input-bg': t.mode === 'dark' ? '#0f3460' : '#ffffff',\r\n '--rqd-shadow': t.mode === 'dark' ? '0 8px 32px rgba(0,0,0,0.4)' : '0 8px 32px rgba(0,0,0,0.12)',\r\n }\r\n}\r\n\r\nfunction hexToHsl(hex: string): { h: number; s: number; l: number } {\r\n const result = /^#?([a-f\\d]{2})([a-f\\d]{2})([a-f\\d]{2})$/i.exec(hex)\r\n if (!result) return { h: 160, s: 51, l: 49 }\r\n\r\n const r = parseInt(result[1], 16) / 255\r\n const g = parseInt(result[2], 16) / 255\r\n const b = parseInt(result[3], 16) / 255\r\n\r\n const max = Math.max(r, g, b)\r\n const min = Math.min(r, g, b)\r\n let h = 0\r\n let s = 0\r\n const l = (max + min) / 2\r\n\r\n if (max !== min) {\r\n const d = max - min\r\n s = l > 0.5 ? d / (2 - max - min) : d / (max + min)\r\n if (max === r) h = ((g - b) / d + (g < b ? 6 : 0)) / 6\r\n else if (max === g) h = ((b - r) / d + 2) / 6\r\n else h = ((r - g) / d + 4) / 6\r\n }\r\n\r\n return {\r\n h: Math.round(h * 360),\r\n s: Math.round(s * 100),\r\n l: Math.round(l * 100),\r\n }\r\n}\r\n\r\nexport function getWidgetStyles(): string {\r\n return WIDGET_CSS\r\n}\r\n\r\nconst WIDGET_CSS = `:host { all: initial; }\r\n.rqd-root { font-family: var(--rqd-font, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif); font-size: 14px; line-height: 1.5; color: var(--rqd-text); -webkit-font-smoothing: antialiased; }\r\n.rqd-fab { position: fixed; bottom: 20px; width: 56px; height: 56px; border-radius: 50%; background: var(--rqd-primary); color: #fff; border: none; cursor: pointer; display: flex; align-items: center; justify-content: center; box-shadow: 0 4px 16px rgba(0,0,0,0.2); transition: transform 0.2s, box-shadow 0.2s; pointer-events: auto; z-index: var(--rqd-z, 9999); }\r\n.rqd-fab:hover { transform: scale(1.08); box-shadow: 0 6px 20px rgba(0,0,0,0.25); }\r\n.rqd-fab.rqd-bottom-right { right: 20px; }\r\n.rqd-fab.rqd-bottom-left { left: 20px; }\r\n.rqd-fab svg { width: 24px; height: 24px; fill: currentColor; }\r\n.rqd-panel { position: fixed; bottom: 88px; width: 380px; max-height: calc(100vh - 120px); background: var(--rqd-bg); border-radius: var(--rqd-radius); box-shadow: var(--rqd-shadow); overflow: hidden; display: flex; flex-direction: column; pointer-events: auto; animation: rqd-slide-up 0.25s ease-out; border: 1px solid var(--rqd-border); }\r\n.rqd-panel.rqd-bottom-right { right: 20px; }\r\n.rqd-panel.rqd-bottom-left { left: 20px; }\r\n.rqd-panel.rqd-hidden { display: none; }\r\n@keyframes rqd-slide-up { from { opacity: 0; transform: translateY(16px); } to { opacity: 1; transform: translateY(0); } }\r\n.rqd-header { display: flex; align-items: center; justify-content: space-between; padding: 16px; background: var(--rqd-primary); color: #fff; }\r\n.rqd-header-title { font-size: 16px; font-weight: 600; }\r\n.rqd-header-close { background: none; border: none; color: #fff; cursor: pointer; width: 28px; height: 28px; display: flex; align-items: center; justify-content: center; border-radius: 50%; transition: background 0.15s; }\r\n.rqd-header-close:hover { background: rgba(255,255,255,0.2); }\r\n.rqd-tabs { display: flex; border-bottom: 1px solid var(--rqd-border); }\r\n.rqd-tab { flex: 1; padding: 10px; text-align: center; background: none; border: none; cursor: pointer; font-size: 13px; font-weight: 500; color: var(--rqd-text-secondary); border-bottom: 2px solid transparent; transition: color 0.15s, border-color 0.15s; }\r\n.rqd-tab.rqd-active { color: var(--rqd-primary); border-bottom-color: var(--rqd-primary); }\r\n.rqd-body { flex: 1; overflow-y: auto; padding: 16px; }\r\n.rqd-form-group { margin-bottom: 14px; }\r\n.rqd-label { display: block; font-size: 13px; font-weight: 500; margin-bottom: 4px; color: var(--rqd-text); }\r\n.rqd-input, .rqd-textarea, .rqd-select { width: 100%; padding: 8px 12px; border: 1px solid var(--rqd-border); border-radius: calc(var(--rqd-radius, 8px) / 2); font-size: 14px; font-family: inherit; background: var(--rqd-input-bg); color: var(--rqd-text); outline: none; transition: border-color 0.15s; box-sizing: border-box; }\r\n.rqd-input:focus, .rqd-textarea:focus, .rqd-select:focus { border-color: var(--rqd-primary); }\r\n.rqd-textarea { resize: vertical; min-height: 80px; }\r\n.rqd-error-text { color: #e74c3c; font-size: 12px; margin-top: 2px; }\r\n.rqd-btn { width: 100%; padding: 10px; border: none; border-radius: calc(var(--rqd-radius, 8px) / 2); font-size: 14px; font-weight: 600; cursor: pointer; transition: opacity 0.15s; }\r\n.rqd-btn:disabled { opacity: 0.6; cursor: not-allowed; }\r\n.rqd-btn-primary { background: var(--rqd-primary); color: #fff; }\r\n.rqd-btn-primary:hover:not(:disabled) { opacity: 0.9; }\r\n.rqd-btn-secondary { background: transparent; color: var(--rqd-primary); border: 1px solid var(--rqd-primary); }\r\n.rqd-success { text-align: center; padding: 24px 0; }\r\n.rqd-success-icon { font-size: 48px; margin-bottom: 12px; }\r\n.rqd-success h3 { margin: 0 0 8px; font-size: 18px; color: var(--rqd-text); }\r\n.rqd-token-box { background: var(--rqd-bg-secondary); border: 1px solid var(--rqd-border); border-radius: calc(var(--rqd-radius, 8px) / 2); padding: 8px 12px; font-family: monospace; font-size: 12px; word-break: break-all; margin: 8px 0; color: var(--rqd-text); }\r\n.rqd-ticket-info { background: var(--rqd-bg-secondary); border-radius: calc(var(--rqd-radius, 8px) / 2); padding: 12px; margin-bottom: 12px; }\r\n.rqd-ticket-row { display: flex; justify-content: space-between; margin-bottom: 4px; font-size: 13px; }\r\n.rqd-ticket-label { color: var(--rqd-text-secondary); }\r\n.rqd-badge { display: inline-block; padding: 2px 8px; border-radius: 12px; font-size: 11px; font-weight: 600; background: var(--rqd-primary-light); color: var(--rqd-primary-dark); }\r\n.rqd-reply { padding: 10px 0; border-bottom: 1px solid var(--rqd-border); }\r\n.rqd-reply:last-child { border-bottom: none; }\r\n.rqd-reply-header { display: flex; justify-content: space-between; font-size: 12px; color: var(--rqd-text-secondary); margin-bottom: 4px; }\r\n.rqd-reply-staff { color: var(--rqd-primary); font-weight: 600; }\r\n.rqd-reply-body { font-size: 14px; white-space: pre-wrap; }\r\n.rqd-inline { pointer-events: auto; background: var(--rqd-bg); border: 1px solid var(--rqd-border); border-radius: var(--rqd-radius); overflow: hidden; }\r\n.rqd-inline .rqd-body { max-height: none; }\r\n:host([dir=\"rtl\"]) .rqd-root { direction: rtl; text-align: right; }\r\n:host([dir=\"rtl\"]) .rqd-fab.rqd-bottom-right { right: auto; left: 20px; }\r\n:host([dir=\"rtl\"]) .rqd-fab.rqd-bottom-left { left: auto; right: 20px; }\r\n:host([dir=\"rtl\"]) .rqd-panel.rqd-bottom-right { right: auto; left: 20px; }\r\n:host([dir=\"rtl\"]) .rqd-panel.rqd-bottom-left { left: auto; right: 20px; }\r\n.rqd-menu { display: flex; flex-direction: column; gap: 6px; }\r\n.rqd-menu-item { display: flex; align-items: center; gap: 14px; width: 100%; padding: 14px 12px; background: var(--rqd-bg-secondary); border: 1px solid transparent; border-radius: calc(var(--rqd-radius, 8px) / 1.5); cursor: pointer; text-align: start; transition: border-color 0.15s, background 0.15s, transform 0.1s; font-family: inherit; color: var(--rqd-text); }\r\n.rqd-menu-item:hover { border-color: var(--rqd-primary); background: var(--rqd-bg); transform: translateY(-1px); }\r\n.rqd-menu-icon { width: 42px; height: 42px; display: flex; align-items: center; justify-content: center; border-radius: calc(var(--rqd-radius, 8px) / 2); background: var(--rqd-primary-light); color: var(--rqd-primary-dark); flex-shrink: 0; }\r\n.rqd-menu-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; }\r\n.rqd-menu-label { font-size: 14px; font-weight: 600; line-height: 1.3; }\r\n.rqd-menu-desc { font-size: 12px; color: var(--rqd-text-secondary); line-height: 1.3; }\r\n.rqd-placeholder { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 12px; padding: 40px 16px; color: var(--rqd-text-secondary); text-align: center; }\r\n.rqd-placeholder p { margin: 0; font-size: 14px; }\r\n.rqd-header-brand { display: flex; align-items: center; gap: 8px; min-width: 0; }\r\n.rqd-header-logo { width: 24px; height: 24px; border-radius: 4px; object-fit: contain; flex-shrink: 0; }\r\n.rqd-footer { display: flex; align-items: center; justify-content: center; gap: 4px; padding: 8px 16px; border-top: 1px solid var(--rqd-border); font-size: 11px; color: var(--rqd-text-secondary); }\r\n.rqd-footer a { color: var(--rqd-text-secondary); text-decoration: none; font-weight: 600; transition: color 0.15s; }\r\n.rqd-footer a:hover { color: var(--rqd-primary); }\r\n.rqd-prefs { display: flex; flex-direction: column; gap: 16px; }\r\n.rqd-prefs-group { display: flex; flex-direction: column; gap: 6px; }\r\n.rqd-prefs-label { font-size: 13px; font-weight: 600; color: var(--rqd-text); }\r\n.rqd-prefs-options { display: flex; gap: 6px; }\r\n.rqd-prefs-option { flex: 1; padding: 8px 12px; background: var(--rqd-bg-secondary); border: 1px solid var(--rqd-border); border-radius: calc(var(--rqd-radius, 8px) / 2); cursor: pointer; text-align: center; font-size: 13px; font-family: inherit; color: var(--rqd-text); transition: border-color 0.15s, background 0.15s; }\r\n.rqd-prefs-option:hover { border-color: var(--rqd-primary); }\r\n.rqd-prefs-option.rqd-active { border-color: var(--rqd-primary); background: var(--rqd-primary-light); color: var(--rqd-primary-dark); font-weight: 600; }\r\n.rqd-dropzone { border: 2px dashed var(--rqd-border); border-radius: calc(var(--rqd-radius, 8px) / 2); padding: 16px; text-align: center; cursor: pointer; transition: border-color 0.15s, background 0.15s; color: var(--rqd-text-secondary); font-size: 13px; margin-bottom: 14px; }\r\n.rqd-dropzone:hover { border-color: var(--rqd-primary); }\r\n.rqd-dropzone.rqd-dropzone-active { border-color: var(--rqd-primary); background: var(--rqd-primary-light); color: var(--rqd-primary-dark); }\r\n.rqd-file-list { display: flex; flex-direction: column; gap: 6px; margin-bottom: 14px; }\r\n.rqd-file-item { display: flex; align-items: center; justify-content: space-between; padding: 8px 10px; background: var(--rqd-bg-secondary); border-radius: calc(var(--rqd-radius, 8px) / 2); font-size: 13px; }\r\n.rqd-file-item-info { display: flex; flex-direction: column; gap: 1px; min-width: 0; }\r\n.rqd-file-item-name { font-weight: 500; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }\r\n.rqd-file-item-size { font-size: 11px; color: var(--rqd-text-secondary); }\r\n.rqd-file-remove { background: none; border: none; color: var(--rqd-text-secondary); cursor: pointer; padding: 4px; font-size: 16px; line-height: 1; flex-shrink: 0; }\r\n.rqd-file-remove:hover { color: #e74c3c; }\r\n.rqd-progress { width: 100%; height: 6px; background: var(--rqd-bg-secondary); border-radius: 3px; overflow: hidden; margin-top: 6px; }\r\n.rqd-progress-bar { height: 100%; background: var(--rqd-primary); border-radius: 3px; transition: width 0.2s ease; }\r\n.rqd-upload-status { font-size: 12px; color: var(--rqd-text-secondary); margin-top: 4px; }\r\n.rqd-attachment-list { display: flex; flex-direction: column; gap: 6px; margin-bottom: 12px; }\r\n.rqd-attachment-item { display: flex; align-items: center; justify-content: space-between; padding: 8px 10px; background: var(--rqd-bg-secondary); border-radius: calc(var(--rqd-radius, 8px) / 2); font-size: 13px; }\r\n.rqd-attachment-item a { color: var(--rqd-primary); text-decoration: none; font-weight: 500; font-size: 12px; flex-shrink: 0; }\r\n.rqd-attachment-item a:hover { text-decoration: underline; }\r\n.rqd-ticket-header { margin-bottom: 12px; }\r\n.rqd-ticket-header h3 { margin: 0 0 6px; font-size: 16px; font-weight: 600; color: var(--rqd-text); }\r\n.rqd-ticket-header-meta { display: flex; gap: 6px; flex-wrap: wrap; align-items: center; }\r\n.rqd-ticket-desc { font-size: 14px; color: var(--rqd-text); white-space: pre-wrap; margin-bottom: 16px; padding: 10px; background: var(--rqd-bg-secondary); border-radius: calc(var(--rqd-radius, 8px) / 2); }\r\n.rqd-section-title { font-size: 13px; font-weight: 600; color: var(--rqd-text); margin-bottom: 8px; }\r\n.rqd-email-form { display: flex; flex-direction: column; gap: 12px; padding: 24px 0; }\r\n.rqd-email-form p { margin: 0; font-size: 14px; color: var(--rqd-text-secondary); text-align: center; }\r\n.rqd-checkbox-label { display: flex; align-items: center; gap: 8px; font-size: 13px; color: var(--rqd-text-secondary); cursor: pointer; }\r\n.rqd-checkbox-label input[type=\"checkbox\"] { accent-color: var(--rqd-primary); width: 16px; height: 16px; cursor: pointer; }\r\n.rqd-ticket-card { display: flex; flex-direction: column; gap: 4px; padding: 12px; background: var(--rqd-bg-secondary); border: 1px solid transparent; border-radius: calc(var(--rqd-radius, 8px) / 1.5); cursor: pointer; transition: border-color 0.15s, transform 0.1s; }\r\n.rqd-ticket-card:hover { border-color: var(--rqd-primary); transform: translateY(-1px); }\r\n.rqd-ticket-card-top { display: flex; justify-content: space-between; align-items: center; }\r\n.rqd-ticket-card-number { font-size: 12px; font-weight: 600; color: var(--rqd-text-secondary); }\r\n.rqd-ticket-card-title { font-size: 14px; font-weight: 500; color: var(--rqd-text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }\r\n.rqd-ticket-card-date { font-size: 11px; color: var(--rqd-text-secondary); }\r\n.rqd-ticket-list { display: flex; flex-direction: column; gap: 6px; }\r\n.rqd-loading { text-align: center; padding: 24px 0; color: var(--rqd-text-secondary); font-size: 14px; }\r\n.rqd-reply-compose { margin-top: 12px; display: flex; flex-direction: column; gap: 8px; }\r\n.rqd-category-list { display: flex; flex-direction: column; gap: 4px; margin-bottom: 14px; }\r\n.rqd-category-item { display: flex; align-items: center; justify-content: space-between; padding: 10px 12px; background: var(--rqd-bg-secondary); border: 1px solid transparent; border-radius: calc(var(--rqd-radius, 8px) / 2); cursor: pointer; font-size: 14px; font-family: inherit; color: var(--rqd-text); transition: border-color 0.15s, background 0.15s; text-align: start; width: 100%; }\r\n.rqd-category-item:hover { border-color: var(--rqd-primary); }\r\n.rqd-category-item-chevron { color: var(--rqd-text-secondary); font-size: 16px; }\r\n.rqd-category-breadcrumb { display: flex; align-items: center; gap: 4px; flex-wrap: wrap; margin-bottom: 8px; font-size: 12px; color: var(--rqd-text-secondary); }\r\n.rqd-category-breadcrumb button { background: none; border: none; color: var(--rqd-primary); cursor: pointer; font-size: 12px; font-family: inherit; padding: 0; }\r\n.rqd-category-breadcrumb button:hover { text-decoration: underline; }\r\n.rqd-category-breadcrumb-sep { color: var(--rqd-text-secondary); }\r\n.rqd-category-selected { display: flex; align-items: center; gap: 8px; padding: 8px 12px; background: var(--rqd-primary-light); border-radius: calc(var(--rqd-radius, 8px) / 2); font-size: 13px; color: var(--rqd-primary-dark); margin-bottom: 14px; }\r\n.rqd-category-selected button { background: none; border: none; color: var(--rqd-primary-dark); cursor: pointer; font-size: 14px; margin-left: auto; padding: 0; }\r\n.rqd-diag-toggle { display: flex; align-items: center; justify-content: space-between; padding: 10px 12px; background: var(--rqd-bg-secondary); border-radius: calc(var(--rqd-radius, 8px) / 2); cursor: pointer; font-size: 13px; color: var(--rqd-text-secondary); border: none; width: 100%; font-family: inherit; text-align: start; margin-bottom: 8px; }\r\n.rqd-diag-toggle:hover { color: var(--rqd-text); }\r\n.rqd-diag-panel { display: flex; flex-direction: column; gap: 6px; padding: 8px 0; }\r\n.rqd-diag-item { display: flex; align-items: center; gap: 8px; font-size: 12px; color: var(--rqd-text-secondary); }\r\n.rqd-diag-item input[type=\"checkbox\"] { accent-color: var(--rqd-primary); width: 14px; height: 14px; }\r\n.rqd-diag-item-value { color: var(--rqd-text); font-family: monospace; font-size: 11px; margin-left: auto; max-width: 150px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\r\n.rqd-auth-btn { background: rgba(255,255,255,0.2); border: 1px solid rgba(255,255,255,0.4); color: #fff; cursor: pointer; padding: 4px 12px; border-radius: 12px; font-size: 12px; font-weight: 500; font-family: inherit; transition: background 0.15s; white-space: nowrap; }\r\n.rqd-auth-btn:hover { background: rgba(255,255,255,0.3); }\r\n.rqd-color-presets { display: flex; gap: 8px; flex-wrap: wrap; }\r\n.rqd-color-preset { width: 28px; height: 28px; border-radius: 50%; border: 2px solid transparent; cursor: pointer; transition: transform 0.15s, box-shadow 0.15s; padding: 0; }\r\n.rqd-color-preset:hover { transform: scale(1.15); }\r\n.rqd-color-preset.rqd-active { box-shadow: 0 0 0 3px var(--rqd-bg), 0 0 0 5px currentColor; }\r\n.rqd-contained.rqd-fab { position: absolute; }\r\n.rqd-contained.rqd-panel { position: absolute; }\r\n@media (max-width: 440px) { .rqd-panel { width: calc(100vw - 24px); right: 12px !important; left: 12px !important; bottom: 76px; } .rqd-fab { bottom: 12px; } .rqd-fab.rqd-bottom-right { right: 12px; } .rqd-fab.rqd-bottom-left { left: 12px; } }\r\n`\r\n","export interface WidgetConfigPersist {\r\n position?: 'bottom-right' | 'bottom-left'\r\n language?: string\r\n theme?: { primaryColor?: string; mode?: 'light' | 'dark' | 'auto' }\r\n widget?: 'ticket-form' | 'support-portal'\r\n}\r\n\r\nconst STORAGE_PREFIX = 'reqdesk_'\r\n\r\nfunction getStorage(): Storage | null {\r\n try {\r\n const s = window.localStorage\r\n s.setItem('__test__', '1')\r\n s.removeItem('__test__')\r\n return s\r\n } catch {\r\n try {\r\n return window.sessionStorage\r\n } catch {\r\n return null\r\n }\r\n }\r\n}\r\n\r\nexport function saveTrackingToken(projectSlug: string, token: string): void {\r\n const storage = getStorage()\r\n if (!storage) return\r\n\r\n const key = `${STORAGE_PREFIX}tokens_${projectSlug}`\r\n try {\r\n const existing = JSON.parse(storage.getItem(key) ?? '[]') as string[]\r\n if (!existing.includes(token)) {\r\n existing.push(token)\r\n storage.setItem(key, JSON.stringify(existing))\r\n }\r\n } catch {\r\n storage.setItem(key, JSON.stringify([token]))\r\n }\r\n}\r\n\r\nexport function getTrackingTokens(projectSlug: string): string[] {\r\n const storage = getStorage()\r\n if (!storage) return []\r\n\r\n const key = `${STORAGE_PREFIX}tokens_${projectSlug}`\r\n try {\r\n return JSON.parse(storage.getItem(key) ?? '[]') as string[]\r\n } catch {\r\n return []\r\n }\r\n}\r\n\r\nexport function saveWidgetConfig(apiKey: string, config: WidgetConfigPersist): void {\r\n const storage = getStorage()\r\n if (!storage) return\r\n\r\n const key = `${STORAGE_PREFIX}config_${apiKey}`\r\n try {\r\n const existing = loadWidgetConfig(apiKey)\r\n const merged = { ...existing, ...config }\r\n if (config.theme && existing?.theme) {\r\n merged.theme = { ...existing.theme, ...config.theme }\r\n }\r\n storage.setItem(key, JSON.stringify(merged))\r\n } catch {\r\n // Silently fail if storage is full or unavailable\r\n }\r\n}\r\n\r\nexport function saveWidgetEmail(apiKey: string, email: string): void {\r\n const storage = getStorage()\r\n if (!storage) return\r\n try {\r\n storage.setItem(`${STORAGE_PREFIX}email_${apiKey}`, email)\r\n } catch { /* ignore */ }\r\n}\r\n\r\nexport function loadWidgetEmail(apiKey: string): string | null {\r\n const storage = getStorage()\r\n if (!storage) return null\r\n try {\r\n return storage.getItem(`${STORAGE_PREFIX}email_${apiKey}`)\r\n } catch {\r\n return null\r\n }\r\n}\r\n\r\nexport function clearWidgetEmail(apiKey: string): void {\r\n const storage = getStorage()\r\n if (!storage) return\r\n try {\r\n storage.removeItem(`${STORAGE_PREFIX}email_${apiKey}`)\r\n } catch { /* ignore */ }\r\n}\r\n\r\nexport function loadWidgetConfig(apiKey: string): WidgetConfigPersist | null {\r\n const storage = getStorage()\r\n if (!storage) return null\r\n\r\n const key = `${STORAGE_PREFIX}config_${apiKey}`\r\n try {\r\n const raw = storage.getItem(key)\r\n if (!raw) return null\r\n return JSON.parse(raw) as WidgetConfigPersist\r\n } catch {\r\n return null\r\n }\r\n}\r\n"],"mappings":"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"}
@@ -524,6 +524,38 @@ function themeToVars(theme = {}) {
524
524
  `--rqd-shadow: ${t.mode === "dark" ? "0 8px 32px rgba(0,0,0,0.4)" : "0 8px 32px rgba(0,0,0,0.12)"}`
525
525
  ].join("; ");
526
526
  }
527
+ /**
528
+ * Returns CSS custom properties as a React-compatible style object.
529
+ * React's `style` prop supports custom properties (--var) as keys directly.
530
+ */
531
+ function themeToStyle(theme = {}) {
532
+ const t = {
533
+ ...DEFAULT_THEME,
534
+ ...theme,
535
+ mode: resolveMode(theme.mode ?? DEFAULT_THEME.mode)
536
+ };
537
+ const hsl = hexToHsl(t.primaryColor);
538
+ const lightL = Math.min(hsl.l + 30, 95);
539
+ const darkL = Math.max(hsl.l - 15, 10);
540
+ return {
541
+ "--rqd-primary": t.primaryColor,
542
+ "--rqd-primary-h": String(hsl.h),
543
+ "--rqd-primary-s": `${hsl.s}%`,
544
+ "--rqd-primary-l": `${hsl.l}%`,
545
+ "--rqd-primary-light": `hsl(${hsl.h}, ${hsl.s}%, ${lightL}%)`,
546
+ "--rqd-primary-dark": `hsl(${hsl.h}, ${hsl.s}%, ${darkL}%)`,
547
+ "--rqd-radius": t.borderRadius,
548
+ "--rqd-font": t.fontFamily,
549
+ "--rqd-z": t.zIndex,
550
+ "--rqd-bg": t.mode === "dark" ? "#1a1a2e" : "#ffffff",
551
+ "--rqd-bg-secondary": t.mode === "dark" ? "#16213e" : "#f8f9fa",
552
+ "--rqd-text": t.mode === "dark" ? "#e0e0e0" : "#1a1a2e",
553
+ "--rqd-text-secondary": t.mode === "dark" ? "#a0a0b0" : "#6c757d",
554
+ "--rqd-border": t.mode === "dark" ? "#2a2a4a" : "#e0e0e0",
555
+ "--rqd-input-bg": t.mode === "dark" ? "#0f3460" : "#ffffff",
556
+ "--rqd-shadow": t.mode === "dark" ? "0 8px 32px rgba(0,0,0,0.4)" : "0 8px 32px rgba(0,0,0,0.12)"
557
+ };
558
+ }
527
559
  function hexToHsl(hex) {
528
560
  const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
529
561
  if (!result) return {
@@ -902,6 +934,12 @@ Object.defineProperty(exports, "submitTrackingReply", {
902
934
  return submitTrackingReply;
903
935
  }
904
936
  });
937
+ Object.defineProperty(exports, "themeToStyle", {
938
+ enumerable: true,
939
+ get: function() {
940
+ return themeToStyle;
941
+ }
942
+ });
905
943
  Object.defineProperty(exports, "themeToVars", {
906
944
  enumerable: true,
907
945
  get: function() {
package/package.json CHANGED
@@ -1,81 +1,81 @@
1
- {
2
- "name": "@reqdesk/widget",
3
- "version": "0.2.0",
4
- "type": "module",
5
- "description": "Embeddable support widget SDK for Reqdesk — ticket submission, tracking, and support portal.",
6
- "exports": {
7
- ".": {
8
- "import": {
9
- "types": "./dist/index.d.ts",
10
- "default": "./dist/index.js"
11
- },
12
- "require": {
13
- "types": "./dist/index.d.cts",
14
- "default": "./dist/index.cjs"
15
- }
16
- },
17
- "./react": {
18
- "import": {
19
- "types": "./dist/react.d.ts",
20
- "default": "./dist/react.js"
21
- },
22
- "require": {
23
- "types": "./dist/react.d.cts",
24
- "default": "./dist/react.cjs"
25
- }
26
- },
27
- "./iife": "./dist/index.iife.js",
28
- "./package.json": "./package.json"
29
- },
30
- "main": "./dist/index.cjs",
31
- "module": "./dist/index.js",
32
- "types": "./dist/index.d.ts",
33
- "sideEffects": false,
34
- "files": [
35
- "dist",
36
- "README.md"
37
- ],
38
- "scripts": {
39
- "build": "tsdown && tsdown --config tsdown.iife.config.ts && node scripts/fix-dts.js",
40
- "dev": "tsdown --watch",
41
- "test": "vitest run",
42
- "test:watch": "vitest",
43
- "typecheck": "tsc --noEmit",
44
- "lint": "tsc --noEmit",
45
- "prepublishOnly": "bun run build"
46
- },
47
- "peerDependencies": {
48
- "react": "^18 || ^19",
49
- "react-dom": "^18 || ^19",
50
- "oidc-spa": "^10.0.0",
51
- "@tanstack/react-query": "^5.0.0"
52
- },
53
- "peerDependenciesMeta": {
54
- "react": {
55
- "optional": true
56
- },
57
- "react-dom": {
58
- "optional": true
59
- },
60
- "oidc-spa": {
61
- "optional": true
62
- },
63
- "@tanstack/react-query": {
64
- "optional": true
65
- }
66
- },
67
- "devDependencies": {
68
- "@tanstack/react-query": "^5.96.2",
69
- "@types/react": "^19.2.7",
70
- "@types/react-dom": "^19.2.3",
71
- "oidc-spa": "^10.0.8",
72
- "react": "^19.2.0",
73
- "react-dom": "^19.2.0",
74
- "tsdown": "^0.12.9",
75
- "typescript": "~5.9.3",
76
- "vitest": "^3.2.1"
77
- },
78
- "dependencies": {
79
- "ofetch": "^1.5.1"
80
- }
81
- }
1
+ {
2
+ "name": "@reqdesk/widget",
3
+ "version": "0.3.1",
4
+ "type": "module",
5
+ "description": "Embeddable support widget SDK for Reqdesk — ticket submission, tracking, and support portal.",
6
+ "exports": {
7
+ ".": {
8
+ "import": {
9
+ "types": "./dist/index.d.ts",
10
+ "default": "./dist/index.js"
11
+ },
12
+ "require": {
13
+ "types": "./dist/index.d.cts",
14
+ "default": "./dist/index.cjs"
15
+ }
16
+ },
17
+ "./react": {
18
+ "import": {
19
+ "types": "./dist/react.d.ts",
20
+ "default": "./dist/react.js"
21
+ },
22
+ "require": {
23
+ "types": "./dist/react.d.cts",
24
+ "default": "./dist/react.cjs"
25
+ }
26
+ },
27
+ "./iife": "./dist/index.iife.js",
28
+ "./package.json": "./package.json"
29
+ },
30
+ "main": "./dist/index.cjs",
31
+ "module": "./dist/index.js",
32
+ "types": "./dist/index.d.ts",
33
+ "sideEffects": false,
34
+ "files": [
35
+ "dist",
36
+ "README.md"
37
+ ],
38
+ "scripts": {
39
+ "build": "tsdown && tsdown --config tsdown.iife.config.ts && node scripts/fix-dts.js",
40
+ "dev": "tsdown --watch",
41
+ "test": "vitest run",
42
+ "test:watch": "vitest",
43
+ "typecheck": "tsc --noEmit",
44
+ "lint": "tsc --noEmit",
45
+ "prepublishOnly": "bun run build"
46
+ },
47
+ "peerDependencies": {
48
+ "react": "^18 || ^19",
49
+ "react-dom": "^18 || ^19",
50
+ "oidc-spa": "^10.0.0",
51
+ "@tanstack/react-query": "^5.0.0"
52
+ },
53
+ "peerDependenciesMeta": {
54
+ "react": {
55
+ "optional": true
56
+ },
57
+ "react-dom": {
58
+ "optional": true
59
+ },
60
+ "oidc-spa": {
61
+ "optional": true
62
+ },
63
+ "@tanstack/react-query": {
64
+ "optional": true
65
+ }
66
+ },
67
+ "devDependencies": {
68
+ "@tanstack/react-query": "^5.96.2",
69
+ "@types/react": "^19.2.7",
70
+ "@types/react-dom": "^19.2.3",
71
+ "oidc-spa": "^10.0.8",
72
+ "react": "^19.2.0",
73
+ "react-dom": "^19.2.0",
74
+ "tsdown": "^0.12.9",
75
+ "typescript": "~5.9.3",
76
+ "vitest": "^3.2.1"
77
+ },
78
+ "dependencies": {
79
+ "ofetch": "^1.5.1"
80
+ }
81
+ }
@@ -1 +0,0 @@
1
- {"version":3,"file":"storage-PjDHb5v7.js","names":[],"sources":["../src/ofetch-client.ts","../src/api-client.ts","../src/i18n/en.ts","../src/i18n/ar.ts","../src/theme.ts","../src/storage.ts"],"sourcesContent":["import { ofetch } from 'ofetch'\nimport type { WidgetError } from './types'\n\nlet apiKey = ''\nlet baseUrl = ''\nlet oidcTokenProvider: (() => Promise<{ accessToken: string }>) | null = null\n\nexport function configureWidgetClient(url: string, key: string) {\n baseUrl = url.replace(/\\/$/, '')\n apiKey = key\n}\n\nexport function setOidcTokenProvider(provider: (() => Promise<{ accessToken: string }>) | null) {\n oidcTokenProvider = provider\n}\n\nexport const widgetFetch = ofetch.create({\n timeout: 15000,\n retry: 2,\n retryStatusCodes: [408, 429, 500, 502, 503, 504],\n\n async onRequest({ options }) {\n options.baseURL = `${baseUrl}/api/v1`\n const headers = new Headers(options.headers as HeadersInit)\n headers.set('X-API-Key', apiKey)\n\n // Get fresh token on every request — oidc-spa handles refresh internally\n if (oidcTokenProvider) {\n try {\n const tokens = await oidcTokenProvider()\n headers.set('Authorization', `Bearer ${tokens.accessToken}`)\n } catch {\n // Auth unavailable — continue with API key only\n }\n }\n\n options.headers = headers\n },\n\n onResponseError({ response }) {\n const body = response._data as Record<string, unknown> | undefined\n const error: WidgetError = {\n code: (body?.responseCode as string) ?? `HTTP_${response.status}`,\n message: (body?.responseMessage as string) ?? response.statusText,\n }\n throw error\n },\n})\n\n// Upload helper using XHR for progress tracking (ofetch doesn't support upload progress)\nexport function uploadWithProgress(\n path: string,\n file: File,\n onProgress?: (percent: number) => void,\n): Promise<unknown> {\n return new Promise((resolve, reject) => {\n const xhr = new XMLHttpRequest()\n xhr.open('POST', `${baseUrl}${path}`)\n xhr.setRequestHeader('X-API-Key', apiKey)\n\n // For uploads, use cached token (can't await in XHR setup)\n // The token was set on the last ofetch request via the interceptor\n if (oidcTokenProvider) {\n oidcTokenProvider()\n .then(tokens => {\n xhr.setRequestHeader('Authorization', `Bearer ${tokens.accessToken}`)\n sendXhr()\n })\n .catch(() => sendXhr())\n } else {\n sendXhr()\n }\n\n function sendXhr() {\n if (onProgress) {\n xhr.upload.addEventListener('progress', (e) => {\n if (e.lengthComputable) onProgress(Math.round((e.loaded / e.total) * 100))\n })\n }\n\n xhr.addEventListener('load', () => {\n if (xhr.status >= 200 && xhr.status < 300) {\n try { resolve(JSON.parse(xhr.responseText)) } catch { resolve({}) }\n } else {\n reject({ code: `HTTP_${xhr.status}`, message: xhr.statusText } as WidgetError)\n }\n })\n\n xhr.addEventListener('error', () => {\n reject({ code: 'NETWORK_ERROR', message: 'Network error during upload' } as WidgetError)\n })\n\n const form = new FormData()\n form.append('file', file)\n xhr.send(form)\n }\n })\n}\n","import { widgetFetch, uploadWithProgress, configureWidgetClient, setOidcTokenProvider } from './ofetch-client'\nimport type { SubmitTicketData, TicketResult, TrackedTicketResult, PublicReply } from './types'\n\n// Re-export client configuration\nexport { configureWidgetClient as configureClient, setOidcTokenProvider }\n\n// Legacy compat — setBearerToken is no longer needed (ofetch interceptor handles it)\nexport function setBearerToken(_token: string | null) {\n // No-op: token is now fetched per-request via oidcTokenProvider\n}\n\nfunction generateIdempotencyKey(): string {\n return crypto.randomUUID()\n}\n\n// ── JSON:API response shape ──────────────────────────────────────────────────\n\ninterface ApiResponse<T> {\n responseCode: string\n responseMessage: string\n httpCode: number\n data: T\n meta?: Record<string, unknown>\n included?: unknown[]\n}\n\n// ── Ticket Submission ────────────────────────────────────────────────────────\n\nexport async function submitTicket(projectId: string, data: SubmitTicketData): Promise<TicketResult> {\n const res = await widgetFetch<ApiResponse<{ id: string; attributes: { ticketNumber: string; status: string } }> & { meta?: { trackingToken?: string } }>(\n `/projects/${projectId}/tickets`,\n {\n method: 'POST',\n body: {\n title: data.title,\n description: data.description,\n priority: data.priority,\n categoryId: data.categoryId,\n email: data.email,\n clientMetadata: data.clientMetadata,\n },\n headers: { 'Idempotency-Key': generateIdempotencyKey() },\n },\n )\n\n return {\n id: res.data.id,\n ticketNumber: res.data.attributes.ticketNumber,\n trackingToken: res.meta?.trackingToken as string | undefined,\n status: res.data.attributes.status,\n }\n}\n\n// ── Tracking ─────────────────────────────────────────────────────────────────\n\nexport async function trackTicket(token: string): Promise<TrackedTicketResult> {\n const res = await widgetFetch<ApiResponse<{ id: string; attributes: { ticketNumber: string; title: string; status: string; priority: string; createdAt: string } }>>(`/tickets/track?token=${encodeURIComponent(token)}`)\n\n const included = (res.included ?? []) as Array<{ id: string; type: string; attributes: Record<string, unknown> }>\n\n return {\n id: res.data.id,\n ticketNumber: res.data.attributes.ticketNumber,\n title: res.data.attributes.title,\n status: res.data.attributes.status,\n priority: res.data.attributes.priority,\n createdAt: res.data.attributes.createdAt,\n replies: included\n .filter(i => i.type === 'public-reply')\n .map(i => ({\n id: i.id,\n body: i.attributes.body as string,\n authorName: i.attributes.authorName as string,\n isStaff: i.attributes.isStaff as boolean,\n createdAt: i.attributes.createdAt as string,\n })),\n }\n}\n\nexport async function submitTrackingReply(token: string, body: string): Promise<void> {\n await widgetFetch('/tickets/track/reply', {\n method: 'POST',\n body: { trackingToken: token, body },\n })\n}\n\n// ── Categories ───────────────────────────────────────────────────────────────\n\nexport interface CategoryItem {\n id: string\n name: string\n parentId?: string\n hasChildren: boolean\n}\n\nexport async function getCategories(projectId: string, parentId?: string | null): Promise<CategoryItem[]> {\n const params = new URLSearchParams({ 'filter[active]': 'true' })\n if (parentId === null || parentId === undefined) params.set('filter[parentId]', 'root')\n else params.set('filter[parentId]', parentId)\n\n const res = await widgetFetch<ApiResponse<Array<{ id: string; attributes: { name: string; parentId?: string; hasChildren: boolean } }>>>(\n `/projects/${projectId}/categories?${params}`,\n )\n const items = Array.isArray(res.data) ? res.data : []\n return items.map(c => ({\n id: c.id,\n name: c.attributes.name,\n parentId: c.attributes.parentId,\n hasChildren: c.attributes.hasChildren,\n }))\n}\n\nexport async function closeTicket(ticketId: string): Promise<void> {\n await widgetFetch(`/tickets/${ticketId}/status`, {\n method: 'PUT',\n body: { status: 'resolved' },\n })\n}\n\n// ── Attachments ──────────────────────────────────────────────────────────────\n\nexport interface AttachmentMeta {\n id: string\n fileName: string\n contentType: string\n fileSize: number\n downloadUrl?: string\n downloadUrlExpiresAt?: string\n}\n\nexport function uploadAttachment(\n ticketId: string,\n file: File,\n onProgress?: (percent: number) => void,\n): Promise<AttachmentMeta> {\n return uploadWithProgress(`/api/v1/tickets/${ticketId}/attachments`, file, onProgress) as Promise<AttachmentMeta>\n}\n\nexport function uploadReplyAttachment(\n ticketId: string,\n replyId: string,\n file: File,\n onProgress?: (percent: number) => void,\n): Promise<AttachmentMeta> {\n return uploadWithProgress(`/api/v1/tickets/${ticketId}/replies/${replyId}/attachments`, file, onProgress) as Promise<AttachmentMeta>\n}\n\nexport async function getAttachmentDownloadUrl(attachmentId: string): Promise<{ downloadUrl: string; expiresAt: string }> {\n return widgetFetch(`/attachments/${attachmentId}/download`)\n}\n\n// ── Ticket Detail ────────────────────────────────────────────────────────────\n\nexport interface TicketDetail {\n id: string\n ticketNumber: string\n title: string\n description?: string\n status: string\n priority: string\n createdAt: string\n replies: PublicReply[]\n attachments: AttachmentMeta[]\n}\n\nexport async function getTicketDetail(ticketId: string, includeMedia = true): Promise<TicketDetail> {\n const include = includeMedia ? '?include=media' : ''\n const res = await widgetFetch<ApiResponse<{\n id: string\n attributes: {\n ticketNumber: string\n title: string\n description?: string\n status: string\n priority: string\n timestamps: { createdAt: string }\n }\n }>>(`/tickets/${ticketId}${include}`)\n\n const included = (res.included ?? []) as Array<{ id: string; type: string; attributes: Record<string, unknown> }>\n\n const replies = included\n .filter(i => i.type === 'ticket-reply')\n .map(i => ({\n id: i.id,\n body: i.attributes.body as string,\n authorName: i.attributes.authorName as string ?? 'Anonymous',\n isStaff: i.attributes.isStaff as boolean ?? false,\n createdAt: (i.attributes.timestamps as Record<string, string>)?.createdAt ?? i.attributes.createdAt as string ?? '',\n }))\n\n const attachments = included\n .filter(i => i.type === 'attachment')\n .map(i => ({\n id: i.id,\n fileName: i.attributes.fileName as string,\n contentType: i.attributes.contentType as string,\n fileSize: i.attributes.fileSize as number,\n downloadUrl: i.attributes.downloadUrl as string | undefined,\n downloadUrlExpiresAt: i.attributes.downloadUrlExpiresAt as string | undefined,\n }))\n\n return {\n id: res.data.id,\n ticketNumber: res.data.attributes.ticketNumber,\n title: res.data.attributes.title,\n description: res.data.attributes.description,\n status: res.data.attributes.status,\n priority: res.data.attributes.priority,\n createdAt: res.data.attributes.timestamps?.createdAt ?? '',\n replies,\n attachments,\n }\n}\n\nexport async function submitReply(ticketId: string, body: string): Promise<{ id: string }> {\n const res = await widgetFetch<ApiResponse<{ id: string }>>(`/tickets/${ticketId}/replies`, {\n method: 'POST',\n body: { body, isInternal: false },\n })\n return { id: res.data.id }\n}\n\n// ── My Tickets ───────────────────────────────────────────────────────────────\n\nexport interface WidgetUser {\n userId: string\n email: string\n displayName: string\n}\n\nexport async function resolveWidgetUser(projectId: string, email: string): Promise<WidgetUser | null> {\n try {\n return await widgetFetch<WidgetUser>(`/projects/${projectId}/widget-users?email=${encodeURIComponent(email)}`)\n } catch {\n return null\n }\n}\n\nexport interface TicketListItem {\n id: string\n ticketNumber: string\n title: string\n status: string\n priority: string\n createdAt: string\n}\n\nexport async function listMyTickets(\n projectId: string,\n userId: string,\n page = 1,\n pageSize = 20,\n): Promise<TicketListItem[]> {\n const params = new URLSearchParams({\n 'filter[createdBy]': userId,\n 'sort': '-created_at',\n 'page[number]': String(page),\n 'page[size]': String(pageSize),\n })\n\n const res = await widgetFetch<ApiResponse<Array<{\n id: string\n attributes: {\n ticketNumber: string\n title: string\n status: string\n priority: string\n timestamps: { createdAt: string }\n }\n }>>>(`/projects/${projectId}/tickets?${params}`)\n\n const items = Array.isArray(res.data) ? res.data : []\n return items.map(t => ({\n id: t.id,\n ticketNumber: t.attributes.ticketNumber,\n title: t.attributes.title,\n status: t.attributes.status,\n priority: t.attributes.priority,\n createdAt: t.attributes.timestamps?.createdAt ?? '',\n }))\n}\n","export const en: Record<string, string> = {\n 'widget.title': 'Support',\n 'widget.newTicket': 'New Ticket',\n 'widget.trackTicket': 'Track Ticket',\n 'form.title': 'Title',\n 'form.titlePlaceholder': 'Brief summary of your issue',\n 'form.description': 'Description',\n 'form.descriptionPlaceholder': 'Describe your issue in detail...',\n 'form.email': 'Email',\n 'form.emailPlaceholder': 'your@email.com',\n 'form.priority': 'Priority',\n 'form.priorityLow': 'Low',\n 'form.priorityMedium': 'Medium',\n 'form.priorityHigh': 'High',\n 'form.priorityUrgent': 'Urgent',\n 'form.category': 'Category',\n 'form.categoryNone': 'Select a category',\n 'form.submit': 'Submit Ticket',\n 'form.submitting': 'Submitting...',\n 'form.cancel': 'Cancel',\n 'form.attachment': 'Attach files',\n 'success.title': 'Ticket Submitted!',\n 'success.ticketNumber': 'Ticket #',\n 'success.trackingToken': 'Your tracking token:',\n 'success.trackingHint': 'Save this token to track your ticket status.',\n 'success.copyToken': 'Copy Token',\n 'success.copied': 'Copied!',\n 'success.close': 'Close',\n 'success.trackNow': 'Track Ticket',\n 'tracker.title': 'Track Your Ticket',\n 'tracker.tokenPlaceholder': 'Enter your tracking token (trk_...)',\n 'tracker.submit': 'Track',\n 'tracker.tracking': 'Tracking...',\n 'tracker.status': 'Status',\n 'tracker.priority': 'Priority',\n 'tracker.created': 'Created',\n 'tracker.replies': 'Replies',\n 'tracker.noReplies': 'No replies yet.',\n 'tracker.replyPlaceholder': 'Write a reply...',\n 'tracker.sendReply': 'Send Reply',\n 'tracker.sending': 'Sending...',\n 'tracker.back': 'Back',\n 'tracker.staff': 'Staff',\n 'error.generic': 'Something went wrong. Please try again.',\n 'error.network': 'Network error. Check your connection.',\n 'error.tokenInvalid': 'Invalid or expired tracking token.',\n 'error.required': 'This field is required.',\n 'error.emailInvalid': 'Please enter a valid email address.',\n 'error.titleMin': 'Title must be at least 5 characters.',\n 'portal.title': 'Support Portal',\n 'portal.myTickets': 'My Tickets',\n 'portal.noTickets': 'No tickets yet.',\n 'portal.newTicket': 'New Ticket',\n 'widget.close': 'Close',\n 'menu.newTicket': 'Submit a Ticket',\n 'menu.newTicketDesc': 'Create a new support request',\n 'menu.myTickets': 'My Tickets',\n 'menu.myTicketsDesc': 'View and manage your tickets',\n 'menu.trackTicket': 'Track a Ticket',\n 'menu.trackTicketDesc': 'Check status with your tracking token',\n 'menu.knowledgeBase': 'Knowledge Base',\n 'menu.knowledgeBaseDesc': 'Browse help articles and guides',\n 'menu.myTicketsPlaceholder': 'Sign in to view your tickets.',\n 'menu.trackPlaceholder': 'Ticket tracking is coming soon.',\n 'menu.kbPlaceholder': 'Knowledge base is coming soon.',\n 'menu.preferences': 'Preferences',\n 'menu.preferencesDesc': 'Language and appearance settings',\n 'prefs.title': 'Preferences',\n 'prefs.language': 'Language',\n 'prefs.theme': 'Theme',\n 'prefs.light': 'Light',\n 'prefs.dark': 'Dark',\n 'prefs.auto': 'System',\n 'branding.poweredBy': 'Powered by',\n 'attach.dropzone': 'Drop files here or click to browse',\n 'attach.dropzoneActive': 'Drop files here',\n 'attach.maxFiles': 'Maximum {max} files',\n 'attach.remove': 'Remove',\n 'attach.uploading': 'Uploading files...',\n 'attach.uploadProgress': 'Uploading {name}... {percent}%',\n 'attach.invalidType': 'File type not allowed',\n 'attach.tooLarge': 'File exceeds maximum size',\n 'attach.tooMany': 'Maximum number of files reached',\n 'attach.download': 'Download',\n 'mytickets.title': 'My Tickets',\n 'mytickets.emailPrompt': 'Enter your email to view your tickets',\n 'mytickets.emailPlaceholder': 'your@email.com',\n 'mytickets.rememberMe': 'Remember me',\n 'mytickets.lookup': 'Find My Tickets',\n 'mytickets.lookingUp': 'Looking up...',\n 'mytickets.noTickets': 'No tickets yet.',\n 'mytickets.submitFirst': 'Submit your first ticket',\n 'mytickets.noAccount': 'No tickets found for this email.',\n 'detail.description': 'Description',\n 'detail.attachments': 'Attachments',\n 'detail.noAttachments': 'No attachments',\n 'detail.replies': 'Conversation',\n 'detail.noReplies': 'No replies yet.',\n 'detail.replyPlaceholder': 'Write a reply...',\n 'detail.sendReply': 'Send',\n 'detail.sending': 'Sending...',\n 'detail.staff': 'Staff',\n 'detail.you': 'You',\n 'detail.loading': 'Loading ticket...',\n 'track.title': 'Track a Ticket',\n 'track.tokenPlaceholder': 'Enter tracking token (trk_...)',\n 'track.submit': 'Track',\n 'track.tracking': 'Tracking...',\n 'track.recentTickets': 'Recent Tickets',\n 'track.invalidToken': 'Invalid or expired tracking token.',\n 'prefs.clearEmail': 'Clear saved email',\n 'prefs.emailCleared': 'Email cleared',\n 'auth.login': 'Login',\n 'auth.logout': 'Logout',\n 'auth.sessionExpired': 'Session expired. Please log in again.',\n 'prefs.accentColor': 'Accent Color',\n 'form.categoryPlaceholder': 'Select a category',\n 'form.categoryBack': 'Back',\n 'form.categorySelected': 'Category',\n 'diag.title': 'Share diagnostic info',\n 'diag.hint': 'Help us resolve your issue faster',\n 'diag.screenResolution': 'Screen resolution',\n 'diag.deviceType': 'Device type',\n 'diag.timezone': 'Timezone',\n 'diag.referrerUrl': 'Referrer URL',\n 'diag.language': 'Browser language',\n 'diag.platform': 'Platform',\n 'detail.resolve': 'Mark as Resolved',\n 'detail.resolving': 'Resolving...',\n 'detail.resolved': 'Resolved',\n 'detail.sla': 'SLA',\n}\n","export const ar: Record<string, string> = {\n 'widget.title': 'الدعم',\n 'widget.newTicket': 'تذكرة جديدة',\n 'widget.trackTicket': 'تتبع التذكرة',\n 'form.title': 'العنوان',\n 'form.titlePlaceholder': 'ملخص موجز لمشكلتك',\n 'form.description': 'الوصف',\n 'form.descriptionPlaceholder': 'صف مشكلتك بالتفصيل...',\n 'form.email': 'البريد الإلكتروني',\n 'form.emailPlaceholder': 'your@email.com',\n 'form.priority': 'الأولوية',\n 'form.priorityLow': 'منخفضة',\n 'form.priorityMedium': 'متوسطة',\n 'form.priorityHigh': 'عالية',\n 'form.priorityUrgent': 'عاجلة',\n 'form.category': 'الفئة',\n 'form.categoryNone': 'اختر فئة',\n 'form.submit': 'إرسال التذكرة',\n 'form.submitting': 'جارِ الإرسال...',\n 'form.cancel': 'إلغاء',\n 'form.attachment': 'إرفاق ملفات',\n 'success.title': 'تم إرسال التذكرة!',\n 'success.ticketNumber': 'تذكرة #',\n 'success.trackingToken': 'رمز التتبع الخاص بك:',\n 'success.trackingHint': 'احفظ هذا الرمز لتتبع حالة تذكرتك.',\n 'success.copyToken': 'نسخ الرمز',\n 'success.copied': 'تم النسخ!',\n 'success.close': 'إغلاق',\n 'success.trackNow': 'تتبع التذكرة',\n 'tracker.title': 'تتبع تذكرتك',\n 'tracker.tokenPlaceholder': 'أدخل رمز التتبع الخاص بك (trk_...)',\n 'tracker.submit': 'تتبع',\n 'tracker.tracking': 'جارِ التتبع...',\n 'tracker.status': 'الحالة',\n 'tracker.priority': 'الأولوية',\n 'tracker.created': 'تاريخ الإنشاء',\n 'tracker.replies': 'الردود',\n 'tracker.noReplies': 'لا توجد ردود بعد.',\n 'tracker.replyPlaceholder': 'اكتب رداً...',\n 'tracker.sendReply': 'إرسال الرد',\n 'tracker.sending': 'جارِ الإرسال...',\n 'tracker.back': 'رجوع',\n 'tracker.staff': 'فريق الدعم',\n 'error.generic': 'حدث خطأ ما. يرجى المحاولة مرة أخرى.',\n 'error.network': 'خطأ في الشبكة. تحقق من اتصالك.',\n 'error.tokenInvalid': 'رمز التتبع غير صالح أو منتهي الصلاحية.',\n 'error.required': 'هذا الحقل مطلوب.',\n 'error.emailInvalid': 'يرجى إدخال بريد إلكتروني صالح.',\n 'error.titleMin': 'يجب أن يكون العنوان 5 أحرف على الأقل.',\n 'portal.title': 'بوابة الدعم',\n 'portal.myTickets': 'تذاكري',\n 'portal.noTickets': 'لا توجد تذاكر بعد.',\n 'portal.newTicket': 'تذكرة جديدة',\n 'widget.close': 'إغلاق',\n 'menu.newTicket': 'إرسال تذكرة',\n 'menu.newTicketDesc': 'إنشاء طلب ��عم جديد',\n 'menu.myTickets': 'تذاكري',\n 'menu.myTicketsDesc': 'عرض وإدارة تذاكرك',\n 'menu.trackTicket': 'تتبع تذكرة',\n 'menu.trackTicketDesc': 'تحقق من الحالة باستخدام رمز التتبع',\n 'menu.knowledgeBase': 'قاعدة المعرفة',\n 'menu.knowledgeBaseDesc': 'تصفح مقالات المساعدة والأدلة',\n 'menu.myTicketsPlaceholder': 'سجّل الدخول ��عرض تذاكرك.',\n 'menu.trackPlaceholder': 'تتبع التذاكر قريبًا.',\n 'menu.kbPlaceholder': 'قاعدة المعرفة قريبًا.',\n 'menu.preferences': 'التفضيلات',\n 'menu.preferencesDesc': 'إعدادات اللغة والمظهر',\n 'prefs.title': 'التفضيلات',\n 'prefs.language': 'اللغة',\n 'prefs.theme': 'المظهر',\n 'prefs.light': 'فاتح',\n 'prefs.dark': 'داكن',\n 'prefs.auto': 'النظام',\n 'branding.poweredBy': 'مدعوم من',\n 'attach.dropzone': 'اسحب الملفات هنا أو انقر للتصفح',\n 'attach.dropzoneActive': 'أفلت الملفات هنا',\n 'attach.maxFiles': 'بحد أقصى {max} ملفات',\n 'attach.remove': 'إزالة',\n 'attach.uploading': 'جارِ رفع الملفات...',\n 'attach.uploadProgress': 'جارِ رفع {name}... {percent}%',\n 'attach.invalidType': 'نوع الملف غير مسموح',\n 'attach.tooLarge': 'حجم الملف يتجاوز الحد الأقصى',\n 'attach.tooMany': 'تم الوصول للحد الأقصى لعدد الملفات',\n 'attach.download': 'تحميل',\n 'mytickets.title': 'تذاكري',\n 'mytickets.emailPrompt': 'أدخل بريدك الإلكتروني لعرض تذاكرك',\n 'mytickets.emailPlaceholder': 'your@email.com',\n 'mytickets.rememberMe': 'تذكرني',\n 'mytickets.lookup': 'البحث عن تذاكري',\n 'mytickets.lookingUp': 'جارِ البحث...',\n 'mytickets.noTickets': 'لا توجد تذاكر بعد.',\n 'mytickets.submitFirst': 'أرسل تذكرتك الأولى',\n 'mytickets.noAccount': 'لم يتم العثور على تذاكر لهذا البريد.',\n 'detail.description': 'الوصف',\n 'detail.attachments': 'المرفقات',\n 'detail.noAttachments': 'لا توجد مرفقات',\n 'detail.replies': 'المحادثة',\n 'detail.noReplies': 'لا توجد ردود بعد.',\n 'detail.replyPlaceholder': 'اكتب رداً...',\n 'detail.sendReply': 'إرسال',\n 'detail.sending': 'جارِ الإرسال...',\n 'detail.staff': 'فريق الدعم',\n 'detail.you': 'أنت',\n 'detail.loading': 'جارِ تحميل التذكرة...',\n 'track.title': 'تتبع تذكرة',\n 'track.tokenPlaceholder': 'أدخل رمز التتبع (trk_...)',\n 'track.submit': 'تتبع',\n 'track.tracking': 'جارِ التتبع...',\n 'track.recentTickets': 'التذاكر الأخيرة',\n 'track.invalidToken': 'رمز التتبع غير صالح أو منتهي.',\n 'prefs.clearEmail': 'مسح البريد المحفوظ',\n 'prefs.emailCleared': 'تم مسح البريد',\n 'auth.login': 'تسجيل الدخول',\n 'auth.logout': 'تسجيل الخروج',\n 'auth.sessionExpired': 'انتهت الجلسة. يرجى تسجيل الدخول مرة أخرى.',\n 'prefs.accentColor': 'لون التمييز',\n 'form.categoryPlaceholder': 'اختر فئة',\n 'form.categoryBack': 'رجوع',\n 'form.categorySelected': 'الفئة',\n 'diag.title': 'مشاركة معلومات التشخيص',\n 'diag.hint': 'ساعدنا في حل مشكلتك بشكل أسرع',\n 'diag.screenResolution': 'دقة الشاشة',\n 'diag.deviceType': 'نوع الجهاز',\n 'diag.timezone': 'المنطقة الزمنية',\n 'diag.referrerUrl': 'رابط المصدر',\n 'diag.language': 'لغة المتصفح',\n 'diag.platform': 'المنصة',\n 'detail.resolve': 'تحديد كمحلول',\n 'detail.resolving': 'جارِ الحل...',\n 'detail.resolved': 'محلول',\n 'detail.sla': 'اتفاقية الخدمة',\n}\n","import type { ThemeConfig } from './types'\n\nconst DEFAULT_THEME: Required<Omit<ThemeConfig, 'logo' | 'brandName' | 'hideBranding'>> & { logo?: string; brandName?: string; hideBranding?: boolean } = {\n primaryColor: '#42b983',\n mode: 'light',\n borderRadius: '8px',\n fontFamily: 'inherit',\n zIndex: 9999,\n}\n\nfunction resolveMode(mode: ThemeConfig['mode']): 'light' | 'dark' {\n if (mode === 'auto') {\n if (typeof window !== 'undefined' && window.matchMedia?.('(prefers-color-scheme: dark)').matches) {\n return 'dark'\n }\n return 'light'\n }\n return mode ?? 'light'\n}\n\nexport function themeToVars(theme: ThemeConfig = {}): string {\n const t = { ...DEFAULT_THEME, ...theme, mode: resolveMode(theme.mode ?? DEFAULT_THEME.mode) }\n const hsl = hexToHsl(t.primaryColor)\n const lightL = Math.min(hsl.l + 30, 95)\n const darkL = Math.max(hsl.l - 15, 10)\n return [\n `--rqd-primary: ${t.primaryColor}`,\n `--rqd-primary-h: ${hsl.h}`,\n `--rqd-primary-s: ${hsl.s}%`,\n `--rqd-primary-l: ${hsl.l}%`,\n `--rqd-primary-light: hsl(${hsl.h}, ${hsl.s}%, ${lightL}%)`,\n `--rqd-primary-dark: hsl(${hsl.h}, ${hsl.s}%, ${darkL}%)`,\n `--rqd-radius: ${t.borderRadius}`,\n `--rqd-font: ${t.fontFamily}`,\n `--rqd-z: ${t.zIndex}`,\n `--rqd-bg: ${t.mode === 'dark' ? '#1a1a2e' : '#ffffff'}`,\n `--rqd-bg-secondary: ${t.mode === 'dark' ? '#16213e' : '#f8f9fa'}`,\n `--rqd-text: ${t.mode === 'dark' ? '#e0e0e0' : '#1a1a2e'}`,\n `--rqd-text-secondary: ${t.mode === 'dark' ? '#a0a0b0' : '#6c757d'}`,\n `--rqd-border: ${t.mode === 'dark' ? '#2a2a4a' : '#e0e0e0'}`,\n `--rqd-input-bg: ${t.mode === 'dark' ? '#0f3460' : '#ffffff'}`,\n `--rqd-shadow: ${t.mode === 'dark' ? '0 8px 32px rgba(0,0,0,0.4)' : '0 8px 32px rgba(0,0,0,0.12)'}`,\n ].join('; ')\n}\n\nfunction hexToHsl(hex: string): { h: number; s: number; l: number } {\n const result = /^#?([a-f\\d]{2})([a-f\\d]{2})([a-f\\d]{2})$/i.exec(hex)\n if (!result) return { h: 160, s: 51, l: 49 }\n\n const r = parseInt(result[1], 16) / 255\n const g = parseInt(result[2], 16) / 255\n const b = parseInt(result[3], 16) / 255\n\n const max = Math.max(r, g, b)\n const min = Math.min(r, g, b)\n let h = 0\n let s = 0\n const l = (max + min) / 2\n\n if (max !== min) {\n const d = max - min\n s = l > 0.5 ? d / (2 - max - min) : d / (max + min)\n if (max === r) h = ((g - b) / d + (g < b ? 6 : 0)) / 6\n else if (max === g) h = ((b - r) / d + 2) / 6\n else h = ((r - g) / d + 4) / 6\n }\n\n return {\n h: Math.round(h * 360),\n s: Math.round(s * 100),\n l: Math.round(l * 100),\n }\n}\n\nexport function getWidgetStyles(): string {\n return WIDGET_CSS\n}\n\nconst WIDGET_CSS = `:host { all: initial; }\n.rqd-root { font-family: var(--rqd-font, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif); font-size: 14px; line-height: 1.5; color: var(--rqd-text); -webkit-font-smoothing: antialiased; }\n.rqd-fab { position: fixed; bottom: 20px; width: 56px; height: 56px; border-radius: 50%; background: var(--rqd-primary); color: #fff; border: none; cursor: pointer; display: flex; align-items: center; justify-content: center; box-shadow: 0 4px 16px rgba(0,0,0,0.2); transition: transform 0.2s, box-shadow 0.2s; pointer-events: auto; z-index: var(--rqd-z, 9999); }\n.rqd-fab:hover { transform: scale(1.08); box-shadow: 0 6px 20px rgba(0,0,0,0.25); }\n.rqd-fab.rqd-bottom-right { right: 20px; }\n.rqd-fab.rqd-bottom-left { left: 20px; }\n.rqd-fab svg { width: 24px; height: 24px; fill: currentColor; }\n.rqd-panel { position: fixed; bottom: 88px; width: 380px; max-height: calc(100vh - 120px); background: var(--rqd-bg); border-radius: var(--rqd-radius); box-shadow: var(--rqd-shadow); overflow: hidden; display: flex; flex-direction: column; pointer-events: auto; animation: rqd-slide-up 0.25s ease-out; border: 1px solid var(--rqd-border); }\n.rqd-panel.rqd-bottom-right { right: 20px; }\n.rqd-panel.rqd-bottom-left { left: 20px; }\n.rqd-panel.rqd-hidden { display: none; }\n@keyframes rqd-slide-up { from { opacity: 0; transform: translateY(16px); } to { opacity: 1; transform: translateY(0); } }\n.rqd-header { display: flex; align-items: center; justify-content: space-between; padding: 16px; background: var(--rqd-primary); color: #fff; }\n.rqd-header-title { font-size: 16px; font-weight: 600; }\n.rqd-header-close { background: none; border: none; color: #fff; cursor: pointer; width: 28px; height: 28px; display: flex; align-items: center; justify-content: center; border-radius: 50%; transition: background 0.15s; }\n.rqd-header-close:hover { background: rgba(255,255,255,0.2); }\n.rqd-tabs { display: flex; border-bottom: 1px solid var(--rqd-border); }\n.rqd-tab { flex: 1; padding: 10px; text-align: center; background: none; border: none; cursor: pointer; font-size: 13px; font-weight: 500; color: var(--rqd-text-secondary); border-bottom: 2px solid transparent; transition: color 0.15s, border-color 0.15s; }\n.rqd-tab.rqd-active { color: var(--rqd-primary); border-bottom-color: var(--rqd-primary); }\n.rqd-body { flex: 1; overflow-y: auto; padding: 16px; }\n.rqd-form-group { margin-bottom: 14px; }\n.rqd-label { display: block; font-size: 13px; font-weight: 500; margin-bottom: 4px; color: var(--rqd-text); }\n.rqd-input, .rqd-textarea, .rqd-select { width: 100%; padding: 8px 12px; border: 1px solid var(--rqd-border); border-radius: calc(var(--rqd-radius, 8px) / 2); font-size: 14px; font-family: inherit; background: var(--rqd-input-bg); color: var(--rqd-text); outline: none; transition: border-color 0.15s; box-sizing: border-box; }\n.rqd-input:focus, .rqd-textarea:focus, .rqd-select:focus { border-color: var(--rqd-primary); }\n.rqd-textarea { resize: vertical; min-height: 80px; }\n.rqd-error-text { color: #e74c3c; font-size: 12px; margin-top: 2px; }\n.rqd-btn { width: 100%; padding: 10px; border: none; border-radius: calc(var(--rqd-radius, 8px) / 2); font-size: 14px; font-weight: 600; cursor: pointer; transition: opacity 0.15s; }\n.rqd-btn:disabled { opacity: 0.6; cursor: not-allowed; }\n.rqd-btn-primary { background: var(--rqd-primary); color: #fff; }\n.rqd-btn-primary:hover:not(:disabled) { opacity: 0.9; }\n.rqd-btn-secondary { background: transparent; color: var(--rqd-primary); border: 1px solid var(--rqd-primary); }\n.rqd-success { text-align: center; padding: 24px 0; }\n.rqd-success-icon { font-size: 48px; margin-bottom: 12px; }\n.rqd-success h3 { margin: 0 0 8px; font-size: 18px; color: var(--rqd-text); }\n.rqd-token-box { background: var(--rqd-bg-secondary); border: 1px solid var(--rqd-border); border-radius: calc(var(--rqd-radius, 8px) / 2); padding: 8px 12px; font-family: monospace; font-size: 12px; word-break: break-all; margin: 8px 0; color: var(--rqd-text); }\n.rqd-ticket-info { background: var(--rqd-bg-secondary); border-radius: calc(var(--rqd-radius, 8px) / 2); padding: 12px; margin-bottom: 12px; }\n.rqd-ticket-row { display: flex; justify-content: space-between; margin-bottom: 4px; font-size: 13px; }\n.rqd-ticket-label { color: var(--rqd-text-secondary); }\n.rqd-badge { display: inline-block; padding: 2px 8px; border-radius: 12px; font-size: 11px; font-weight: 600; background: var(--rqd-primary-light); color: var(--rqd-primary-dark); }\n.rqd-reply { padding: 10px 0; border-bottom: 1px solid var(--rqd-border); }\n.rqd-reply:last-child { border-bottom: none; }\n.rqd-reply-header { display: flex; justify-content: space-between; font-size: 12px; color: var(--rqd-text-secondary); margin-bottom: 4px; }\n.rqd-reply-staff { color: var(--rqd-primary); font-weight: 600; }\n.rqd-reply-body { font-size: 14px; white-space: pre-wrap; }\n.rqd-inline { pointer-events: auto; background: var(--rqd-bg); border: 1px solid var(--rqd-border); border-radius: var(--rqd-radius); overflow: hidden; }\n.rqd-inline .rqd-body { max-height: none; }\n:host([dir=\"rtl\"]) .rqd-root { direction: rtl; text-align: right; }\n:host([dir=\"rtl\"]) .rqd-fab.rqd-bottom-right { right: auto; left: 20px; }\n:host([dir=\"rtl\"]) .rqd-fab.rqd-bottom-left { left: auto; right: 20px; }\n:host([dir=\"rtl\"]) .rqd-panel.rqd-bottom-right { right: auto; left: 20px; }\n:host([dir=\"rtl\"]) .rqd-panel.rqd-bottom-left { left: auto; right: 20px; }\n.rqd-menu { display: flex; flex-direction: column; gap: 6px; }\n.rqd-menu-item { display: flex; align-items: center; gap: 14px; width: 100%; padding: 14px 12px; background: var(--rqd-bg-secondary); border: 1px solid transparent; border-radius: calc(var(--rqd-radius, 8px) / 1.5); cursor: pointer; text-align: start; transition: border-color 0.15s, background 0.15s, transform 0.1s; font-family: inherit; color: var(--rqd-text); }\n.rqd-menu-item:hover { border-color: var(--rqd-primary); background: var(--rqd-bg); transform: translateY(-1px); }\n.rqd-menu-icon { width: 42px; height: 42px; display: flex; align-items: center; justify-content: center; border-radius: calc(var(--rqd-radius, 8px) / 2); background: var(--rqd-primary-light); color: var(--rqd-primary-dark); flex-shrink: 0; }\n.rqd-menu-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; }\n.rqd-menu-label { font-size: 14px; font-weight: 600; line-height: 1.3; }\n.rqd-menu-desc { font-size: 12px; color: var(--rqd-text-secondary); line-height: 1.3; }\n.rqd-placeholder { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 12px; padding: 40px 16px; color: var(--rqd-text-secondary); text-align: center; }\n.rqd-placeholder p { margin: 0; font-size: 14px; }\n.rqd-header-brand { display: flex; align-items: center; gap: 8px; min-width: 0; }\n.rqd-header-logo { width: 24px; height: 24px; border-radius: 4px; object-fit: contain; flex-shrink: 0; }\n.rqd-footer { display: flex; align-items: center; justify-content: center; gap: 4px; padding: 8px 16px; border-top: 1px solid var(--rqd-border); font-size: 11px; color: var(--rqd-text-secondary); }\n.rqd-footer a { color: var(--rqd-text-secondary); text-decoration: none; font-weight: 600; transition: color 0.15s; }\n.rqd-footer a:hover { color: var(--rqd-primary); }\n.rqd-prefs { display: flex; flex-direction: column; gap: 16px; }\n.rqd-prefs-group { display: flex; flex-direction: column; gap: 6px; }\n.rqd-prefs-label { font-size: 13px; font-weight: 600; color: var(--rqd-text); }\n.rqd-prefs-options { display: flex; gap: 6px; }\n.rqd-prefs-option { flex: 1; padding: 8px 12px; background: var(--rqd-bg-secondary); border: 1px solid var(--rqd-border); border-radius: calc(var(--rqd-radius, 8px) / 2); cursor: pointer; text-align: center; font-size: 13px; font-family: inherit; color: var(--rqd-text); transition: border-color 0.15s, background 0.15s; }\n.rqd-prefs-option:hover { border-color: var(--rqd-primary); }\n.rqd-prefs-option.rqd-active { border-color: var(--rqd-primary); background: var(--rqd-primary-light); color: var(--rqd-primary-dark); font-weight: 600; }\n.rqd-dropzone { border: 2px dashed var(--rqd-border); border-radius: calc(var(--rqd-radius, 8px) / 2); padding: 16px; text-align: center; cursor: pointer; transition: border-color 0.15s, background 0.15s; color: var(--rqd-text-secondary); font-size: 13px; margin-bottom: 14px; }\n.rqd-dropzone:hover { border-color: var(--rqd-primary); }\n.rqd-dropzone.rqd-dropzone-active { border-color: var(--rqd-primary); background: var(--rqd-primary-light); color: var(--rqd-primary-dark); }\n.rqd-file-list { display: flex; flex-direction: column; gap: 6px; margin-bottom: 14px; }\n.rqd-file-item { display: flex; align-items: center; justify-content: space-between; padding: 8px 10px; background: var(--rqd-bg-secondary); border-radius: calc(var(--rqd-radius, 8px) / 2); font-size: 13px; }\n.rqd-file-item-info { display: flex; flex-direction: column; gap: 1px; min-width: 0; }\n.rqd-file-item-name { font-weight: 500; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }\n.rqd-file-item-size { font-size: 11px; color: var(--rqd-text-secondary); }\n.rqd-file-remove { background: none; border: none; color: var(--rqd-text-secondary); cursor: pointer; padding: 4px; font-size: 16px; line-height: 1; flex-shrink: 0; }\n.rqd-file-remove:hover { color: #e74c3c; }\n.rqd-progress { width: 100%; height: 6px; background: var(--rqd-bg-secondary); border-radius: 3px; overflow: hidden; margin-top: 6px; }\n.rqd-progress-bar { height: 100%; background: var(--rqd-primary); border-radius: 3px; transition: width 0.2s ease; }\n.rqd-upload-status { font-size: 12px; color: var(--rqd-text-secondary); margin-top: 4px; }\n.rqd-attachment-list { display: flex; flex-direction: column; gap: 6px; margin-bottom: 12px; }\n.rqd-attachment-item { display: flex; align-items: center; justify-content: space-between; padding: 8px 10px; background: var(--rqd-bg-secondary); border-radius: calc(var(--rqd-radius, 8px) / 2); font-size: 13px; }\n.rqd-attachment-item a { color: var(--rqd-primary); text-decoration: none; font-weight: 500; font-size: 12px; flex-shrink: 0; }\n.rqd-attachment-item a:hover { text-decoration: underline; }\n.rqd-ticket-header { margin-bottom: 12px; }\n.rqd-ticket-header h3 { margin: 0 0 6px; font-size: 16px; font-weight: 600; color: var(--rqd-text); }\n.rqd-ticket-header-meta { display: flex; gap: 6px; flex-wrap: wrap; align-items: center; }\n.rqd-ticket-desc { font-size: 14px; color: var(--rqd-text); white-space: pre-wrap; margin-bottom: 16px; padding: 10px; background: var(--rqd-bg-secondary); border-radius: calc(var(--rqd-radius, 8px) / 2); }\n.rqd-section-title { font-size: 13px; font-weight: 600; color: var(--rqd-text); margin-bottom: 8px; }\n.rqd-email-form { display: flex; flex-direction: column; gap: 12px; padding: 24px 0; }\n.rqd-email-form p { margin: 0; font-size: 14px; color: var(--rqd-text-secondary); text-align: center; }\n.rqd-checkbox-label { display: flex; align-items: center; gap: 8px; font-size: 13px; color: var(--rqd-text-secondary); cursor: pointer; }\n.rqd-checkbox-label input[type=\"checkbox\"] { accent-color: var(--rqd-primary); width: 16px; height: 16px; cursor: pointer; }\n.rqd-ticket-card { display: flex; flex-direction: column; gap: 4px; padding: 12px; background: var(--rqd-bg-secondary); border: 1px solid transparent; border-radius: calc(var(--rqd-radius, 8px) / 1.5); cursor: pointer; transition: border-color 0.15s, transform 0.1s; }\n.rqd-ticket-card:hover { border-color: var(--rqd-primary); transform: translateY(-1px); }\n.rqd-ticket-card-top { display: flex; justify-content: space-between; align-items: center; }\n.rqd-ticket-card-number { font-size: 12px; font-weight: 600; color: var(--rqd-text-secondary); }\n.rqd-ticket-card-title { font-size: 14px; font-weight: 500; color: var(--rqd-text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }\n.rqd-ticket-card-date { font-size: 11px; color: var(--rqd-text-secondary); }\n.rqd-ticket-list { display: flex; flex-direction: column; gap: 6px; }\n.rqd-loading { text-align: center; padding: 24px 0; color: var(--rqd-text-secondary); font-size: 14px; }\n.rqd-reply-compose { margin-top: 12px; display: flex; flex-direction: column; gap: 8px; }\n.rqd-category-list { display: flex; flex-direction: column; gap: 4px; margin-bottom: 14px; }\n.rqd-category-item { display: flex; align-items: center; justify-content: space-between; padding: 10px 12px; background: var(--rqd-bg-secondary); border: 1px solid transparent; border-radius: calc(var(--rqd-radius, 8px) / 2); cursor: pointer; font-size: 14px; font-family: inherit; color: var(--rqd-text); transition: border-color 0.15s, background 0.15s; text-align: start; width: 100%; }\n.rqd-category-item:hover { border-color: var(--rqd-primary); }\n.rqd-category-item-chevron { color: var(--rqd-text-secondary); font-size: 16px; }\n.rqd-category-breadcrumb { display: flex; align-items: center; gap: 4px; flex-wrap: wrap; margin-bottom: 8px; font-size: 12px; color: var(--rqd-text-secondary); }\n.rqd-category-breadcrumb button { background: none; border: none; color: var(--rqd-primary); cursor: pointer; font-size: 12px; font-family: inherit; padding: 0; }\n.rqd-category-breadcrumb button:hover { text-decoration: underline; }\n.rqd-category-breadcrumb-sep { color: var(--rqd-text-secondary); }\n.rqd-category-selected { display: flex; align-items: center; gap: 8px; padding: 8px 12px; background: var(--rqd-primary-light); border-radius: calc(var(--rqd-radius, 8px) / 2); font-size: 13px; color: var(--rqd-primary-dark); margin-bottom: 14px; }\n.rqd-category-selected button { background: none; border: none; color: var(--rqd-primary-dark); cursor: pointer; font-size: 14px; margin-left: auto; padding: 0; }\n.rqd-diag-toggle { display: flex; align-items: center; justify-content: space-between; padding: 10px 12px; background: var(--rqd-bg-secondary); border-radius: calc(var(--rqd-radius, 8px) / 2); cursor: pointer; font-size: 13px; color: var(--rqd-text-secondary); border: none; width: 100%; font-family: inherit; text-align: start; margin-bottom: 8px; }\n.rqd-diag-toggle:hover { color: var(--rqd-text); }\n.rqd-diag-panel { display: flex; flex-direction: column; gap: 6px; padding: 8px 0; }\n.rqd-diag-item { display: flex; align-items: center; gap: 8px; font-size: 12px; color: var(--rqd-text-secondary); }\n.rqd-diag-item input[type=\"checkbox\"] { accent-color: var(--rqd-primary); width: 14px; height: 14px; }\n.rqd-diag-item-value { color: var(--rqd-text); font-family: monospace; font-size: 11px; margin-left: auto; max-width: 150px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.rqd-auth-btn { background: rgba(255,255,255,0.2); border: 1px solid rgba(255,255,255,0.4); color: #fff; cursor: pointer; padding: 4px 12px; border-radius: 12px; font-size: 12px; font-weight: 500; font-family: inherit; transition: background 0.15s; white-space: nowrap; }\n.rqd-auth-btn:hover { background: rgba(255,255,255,0.3); }\n.rqd-color-presets { display: flex; gap: 8px; flex-wrap: wrap; }\n.rqd-color-preset { width: 28px; height: 28px; border-radius: 50%; border: 2px solid transparent; cursor: pointer; transition: transform 0.15s, box-shadow 0.15s; padding: 0; }\n.rqd-color-preset:hover { transform: scale(1.15); }\n.rqd-color-preset.rqd-active { box-shadow: 0 0 0 3px var(--rqd-bg), 0 0 0 5px currentColor; }\n.rqd-contained.rqd-fab { position: absolute; }\n.rqd-contained.rqd-panel { position: absolute; }\n@media (max-width: 440px) { .rqd-panel { width: calc(100vw - 24px); right: 12px !important; left: 12px !important; bottom: 76px; } .rqd-fab { bottom: 12px; } .rqd-fab.rqd-bottom-right { right: 12px; } .rqd-fab.rqd-bottom-left { left: 12px; } }\n`\n","export interface WidgetConfigPersist {\n position?: 'bottom-right' | 'bottom-left'\n language?: string\n theme?: { primaryColor?: string; mode?: 'light' | 'dark' | 'auto' }\n widget?: 'ticket-form' | 'support-portal'\n}\n\nconst STORAGE_PREFIX = 'reqdesk_'\n\nfunction getStorage(): Storage | null {\n try {\n const s = window.localStorage\n s.setItem('__test__', '1')\n s.removeItem('__test__')\n return s\n } catch {\n try {\n return window.sessionStorage\n } catch {\n return null\n }\n }\n}\n\nexport function saveTrackingToken(projectSlug: string, token: string): void {\n const storage = getStorage()\n if (!storage) return\n\n const key = `${STORAGE_PREFIX}tokens_${projectSlug}`\n try {\n const existing = JSON.parse(storage.getItem(key) ?? '[]') as string[]\n if (!existing.includes(token)) {\n existing.push(token)\n storage.setItem(key, JSON.stringify(existing))\n }\n } catch {\n storage.setItem(key, JSON.stringify([token]))\n }\n}\n\nexport function getTrackingTokens(projectSlug: string): string[] {\n const storage = getStorage()\n if (!storage) return []\n\n const key = `${STORAGE_PREFIX}tokens_${projectSlug}`\n try {\n return JSON.parse(storage.getItem(key) ?? '[]') as string[]\n } catch {\n return []\n }\n}\n\nexport function saveWidgetConfig(apiKey: string, config: WidgetConfigPersist): void {\n const storage = getStorage()\n if (!storage) return\n\n const key = `${STORAGE_PREFIX}config_${apiKey}`\n try {\n const existing = loadWidgetConfig(apiKey)\n const merged = { ...existing, ...config }\n if (config.theme && existing?.theme) {\n merged.theme = { ...existing.theme, ...config.theme }\n }\n storage.setItem(key, JSON.stringify(merged))\n } catch {\n // Silently fail if storage is full or unavailable\n }\n}\n\nexport function saveWidgetEmail(apiKey: string, email: string): void {\n const storage = getStorage()\n if (!storage) return\n try {\n storage.setItem(`${STORAGE_PREFIX}email_${apiKey}`, email)\n } catch { /* ignore */ }\n}\n\nexport function loadWidgetEmail(apiKey: string): string | null {\n const storage = getStorage()\n if (!storage) return null\n try {\n return storage.getItem(`${STORAGE_PREFIX}email_${apiKey}`)\n } catch {\n return null\n }\n}\n\nexport function clearWidgetEmail(apiKey: string): void {\n const storage = getStorage()\n if (!storage) return\n try {\n storage.removeItem(`${STORAGE_PREFIX}email_${apiKey}`)\n } catch { /* ignore */ }\n}\n\nexport function loadWidgetConfig(apiKey: string): WidgetConfigPersist | null {\n const storage = getStorage()\n if (!storage) return null\n\n const key = `${STORAGE_PREFIX}config_${apiKey}`\n try {\n const raw = storage.getItem(key)\n if (!raw) return null\n return JSON.parse(raw) as WidgetConfigPersist\n } catch {\n return null\n }\n}\n"],"mappings":"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"}