@reqdesk/widget 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,334 +1,334 @@
1
- # @reqdesk/widget
2
-
3
- Embeddable support widget SDK for [Reqdesk](https://reqdesk.com). Add a floating support button to any website — ticket submission with file attachments, ticket tracking, My Tickets, and optional Keycloak SSO authentication.
4
-
5
- ## Features
6
-
7
- - **Floating Action Button** — circular FAB that opens a slide-up support panel
8
- - **Submit Tickets** — form with title, description, email, priority, and file attachments (drag-drop)
9
- - **My Tickets** — view ticket history by email or automatically when authenticated
10
- - **Ticket Detail** — full ticket view with reply thread, attachments, and reply composer
11
- - **Track by Token** — anonymous ticket tracking via tracking tokens
12
- - **Dual Auth** — API key mode (anonymous/email) + optional Keycloak OIDC login
13
- - **Preferences** — end-user language (EN/AR with RTL), theme (light/dark/system), accent color
14
- - **Branding** — custom logo, brand name, and "Powered by Reqdesk" footer (hideable)
15
- - **Shadow DOM** — fully isolated styles, no CSS conflicts with host page
16
- - **i18n** — English and Arabic built-in, custom translations supported
17
-
18
- ## Installation
19
-
20
- ```bash
21
- npm install @reqdesk/widget
22
- # or
23
- bun add @reqdesk/widget
24
- # or
25
- yarn add @reqdesk/widget
26
- ```
27
-
28
- ## Quick Start
29
-
30
- ### Script Tag (any website)
31
-
32
- ```html
33
- <script src="https://unpkg.com/@reqdesk/widget/dist/index.iife.js"></script>
34
- <script>
35
- ReqdeskWidget.init({
36
- apiKey: 'rqd_live_your_api_key_here',
37
- position: 'bottom-right',
38
- language: 'en',
39
- theme: {
40
- primaryColor: '#42b983',
41
- mode: 'light',
42
- brandName: 'Acme Support',
43
- logo: 'https://example.com/logo.png',
44
- },
45
- });
46
- </script>
47
- ```
48
-
49
- ### React Component
50
-
51
- ```tsx
52
- import { ReqdeskProvider, FloatingWidget } from '@reqdesk/widget/react';
53
-
54
- function App() {
55
- return (
56
- <ReqdeskProvider
57
- apiKey="rqd_live_your_api_key_here"
58
- language="en"
59
- theme={{ primaryColor: '#42b983', mode: 'light' }}
60
- >
61
- <FloatingWidget position="bottom-right" />
62
- </ReqdeskProvider>
63
- );
64
- }
65
- ```
66
-
67
- ## Configuration
68
-
69
- ### `ReqdeskWidgetConfig`
70
-
71
- | Property | Type | Required | Default | Description |
72
- |----------|------|----------|---------|-------------|
73
- | `apiKey` | `string` | Yes | — | Project API key from Reqdesk dashboard |
74
- | `auth` | `OidcAuthConfig` | No | — | Keycloak OIDC config for authenticated mode |
75
- | `position` | `'bottom-right' \| 'bottom-left'` | No | `'bottom-right'` | FAB button position |
76
- | `language` | `string` | No | `'en'` | Language code (`'en'` or `'ar'`) |
77
- | `theme` | `ThemeConfig` | No | — | Visual customization |
78
- | `customer` | `CustomerConfig` | No | — | Pre-fill customer info |
79
- | `translations` | `Record<string, string>` | No | — | Override built-in translations |
80
- | `inline` | `boolean` | No | `false` | Render inline instead of floating |
81
- | `container` | `string \| HTMLElement` | No | — | Target element for inline mode |
82
-
83
- ### `ThemeConfig`
84
-
85
- | Property | Type | Default | Description |
86
- |----------|------|---------|-------------|
87
- | `primaryColor` | `string` | `'#42b983'` | Accent color (hex) |
88
- | `mode` | `'light' \| 'dark' \| 'auto'` | `'light'` | Color scheme |
89
- | `borderRadius` | `string` | `'8px'` | Border radius for panels and inputs |
90
- | `fontFamily` | `string` | `'inherit'` | Font family |
91
- | `zIndex` | `number` | `9999` | Z-index for FAB and panel |
92
- | `logo` | `string` | — | URL to brand logo (shown in header) |
93
- | `brandName` | `string` | — | Brand name (shown in header instead of "Support") |
94
- | `hideBranding` | `boolean` | `false` | Hide "Powered by Reqdesk" footer |
95
-
96
- ### `OidcAuthConfig`
97
-
98
- | Property | Type | Description |
99
- |----------|------|-------------|
100
- | `issuerUri` | `string` | Keycloak realm URL (e.g., `https://auth.example.com/realms/reqdesk`) |
101
- | `clientId` | `string` | Keycloak client ID (e.g., `reqdesk-widget`) |
102
-
103
- ## Authentication
104
-
105
- The widget supports two authentication modes that can be used together:
106
-
107
- ### API Key Mode (default)
108
-
109
- Every widget requires an API key. In this mode:
110
-
111
- - Visitors submit tickets anonymously or with an email address
112
- - Email identifies the visitor for "My Tickets" (trust-based, like a contact form)
113
- - Tracking tokens allow anonymous ticket access from any browser
114
- - A "Remember me" checkbox saves the email to localStorage
115
-
116
- ```ts
117
- ReqdeskWidget.init({
118
- apiKey: 'rqd_live_your_api_key_here',
119
- });
120
- ```
121
-
122
- ### Keycloak OIDC Mode (optional)
123
-
124
- When `auth` config is provided, the widget shows a Login button. After authenticating via Keycloak:
125
-
126
- - User is automatically identified (no email entry needed)
127
- - "My Tickets" shows all tickets instantly
128
- - Tickets are attributed to the real Keycloak user account
129
- - Session persists via silent refresh (one-time login)
130
-
131
- ```ts
132
- ReqdeskWidget.init({
133
- apiKey: 'rqd_live_your_api_key_here',
134
- auth: {
135
- issuerUri: 'https://auth.example.com/realms/reqdesk',
136
- clientId: 'reqdesk-widget',
137
- },
138
- });
139
- ```
140
-
141
- The widget's OIDC session is **completely independent** from your host app's auth. It uses its own Keycloak client (`reqdesk-widget`) with separate tokens.
142
-
143
- #### Keycloak Client Setup
144
-
145
- Add a `reqdesk-widget` client to your Keycloak realm:
146
-
147
- - **Client Type**: Public (`publicClient: true`)
148
- - **Flow**: Authorization Code with PKCE (`standardFlowEnabled: true`)
149
- - **Redirect URIs**: `*` (or restrict to your domains)
150
- - **Web Origins**: `+` (CORS for any origin)
151
- - **Client Scopes**: `basic, openid, profile, email`
152
-
153
- ## Events
154
-
155
- ```ts
156
- // Vanilla JS
157
- ReqdeskWidget.on('ticket:created', (ticket) => {
158
- console.log('Ticket created:', ticket.ticketNumber);
159
- });
160
-
161
- ReqdeskWidget.on('open', () => console.log('Widget opened'));
162
- ReqdeskWidget.on('close', () => console.log('Widget closed'));
163
- ReqdeskWidget.on('error', (err) => console.error(err));
164
- ```
165
-
166
- | Event | Payload | Description |
167
- |-------|---------|-------------|
168
- | `open` | — | Widget panel opened |
169
- | `close` | — | Widget panel closed |
170
- | `ticket:created` | `TicketResult` | Ticket submitted successfully |
171
- | `ticket:tracked` | `TrackedTicketResult` | Ticket tracked by token |
172
- | `reply:sent` | — | Reply submitted |
173
- | `error` | `WidgetError` | Error occurred |
174
-
175
- ## Vanilla JS API
176
-
177
- ```ts
178
- ReqdeskWidget.init(config) // Initialize the widget
179
- ReqdeskWidget.open() // Open the panel
180
- ReqdeskWidget.close() // Close the panel
181
- ReqdeskWidget.toggle() // Toggle open/close
182
- ReqdeskWidget.setLanguage('ar') // Change language (supports RTL)
183
- ReqdeskWidget.setTheme({ mode: 'dark' }) // Update theme
184
- ReqdeskWidget.identify({ email: 'user@example.com', name: 'Alice' })
185
- ReqdeskWidget.on(event, callback)
186
- ReqdeskWidget.destroy() // Remove widget from DOM
187
- ```
188
-
189
- ## React API
190
-
191
- ### Components
192
-
193
- ```tsx
194
- import {
195
- ReqdeskProvider, // Context provider (required wrapper)
196
- FloatingWidget, // FAB + slide-up panel
197
- TicketForm, // Standalone ticket form (inline mode)
198
- SupportPortal, // Standalone support portal
199
- ShadowRoot, // Shadow DOM wrapper for custom components
200
- } from '@reqdesk/widget/react';
201
- ```
202
-
203
- ### `ReqdeskProvider` Props
204
-
205
- | Prop | Type | Required | Description |
206
- |------|------|----------|-------------|
207
- | `apiKey` | `string` | Yes | Project API key |
208
- | `auth` | `OidcAuthConfig` | No | Keycloak OIDC config |
209
- | `theme` | `ThemeConfig` | No | Visual customization |
210
- | `language` | `string` | No | Language code |
211
- | `customer` | `CustomerConfig` | No | Pre-fill customer info |
212
- | `translations` | `Record<string, string>` | No | Custom translations |
213
-
214
- ### `FloatingWidget` Props
215
-
216
- | Prop | Type | Default | Description |
217
- |------|------|---------|-------------|
218
- | `position` | `'bottom-right' \| 'bottom-left'` | `'bottom-right'` | FAB position |
219
- | `contained` | `boolean` | `false` | Use absolute positioning (for previews) |
220
- | `onTicketCreated` | `(ticket: TicketResult) => void` | — | Callback on ticket creation |
221
- | `onError` | `(error: WidgetError) => void` | — | Callback on error |
222
-
223
- ### Hooks
224
-
225
- ```tsx
226
- import { useReqdesk } from '@reqdesk/widget/react';
227
-
228
- function CustomForm() {
229
- const { submitTicket, trackTicket, isLoading, error } = useReqdesk();
230
-
231
- const handleSubmit = async () => {
232
- const result = await submitTicket({
233
- title: 'Bug report',
234
- email: 'user@example.com',
235
- priority: 'high',
236
- });
237
- console.log('Created:', result.ticketNumber);
238
- };
239
- }
240
- ```
241
-
242
- ## File Attachments
243
-
244
- The widget supports file attachments on ticket submission and replies:
245
-
246
- - **Drag-and-drop** or click to browse
247
- - **Max 5 files** per submission, 5 per reply
248
- - **Max 10MB** per file (configurable per project)
249
- - **Allowed types**: Images (JPEG, PNG, GIF, WebP), PDF, Office documents, text, CSV, ZIP
250
- - **Blocked**: Executables (.exe, .bat, .cmd, .sh, .ps1, .msi, .dll, .scr)
251
- - **Upload progress** shown per-file after submission
252
-
253
- Files are uploaded sequentially after the ticket/reply is created.
254
-
255
- ## Customization
256
-
257
- ### Custom Translations
258
-
259
- Override any built-in string:
260
-
261
- ```ts
262
- ReqdeskWidget.init({
263
- apiKey: '...',
264
- translations: {
265
- 'widget.title': 'Help Center',
266
- 'form.submit': 'Send Request',
267
- 'menu.newTicket': 'New Request',
268
- },
269
- });
270
- ```
271
-
272
- See `src/i18n/en.ts` for all available translation keys.
273
-
274
- ### RTL Support
275
-
276
- Set `language: 'ar'` for full RTL layout. The widget automatically mirrors all UI elements.
277
-
278
- ### End-User Preferences
279
-
280
- Visitors can customize their experience from the Preferences menu:
281
-
282
- - **Language**: English / Arabic toggle
283
- - **Theme**: Light / Dark / System
284
- - **Accent Color**: 5 preset colors (green, blue, purple, orange, red)
285
-
286
- Preferences are persisted to `localStorage` per API key.
287
-
288
- ## Architecture
289
-
290
- - **Shadow DOM**: Complete CSS isolation — host page styles cannot affect the widget
291
- - **Zero global state**: Widget context is self-contained, no window globals
292
- - **ofetch**: HTTP client with automatic retry, timeout, and auth interceptors
293
- - **TanStack Query**: Data fetching with caching, optimistic updates, and loading states (React only)
294
- - **Optional dependencies**: React, oidc-spa, and TanStack Query are all optional peer deps
295
-
296
- ### Bundle Sizes
297
-
298
- | Entry | Size (gzip) |
299
- |-------|-------------|
300
- | `index.iife.js` (vanilla) | ~12 KB |
301
- | `react.js` (ESM) | ~6 KB |
302
-
303
- React, oidc-spa, and TanStack Query are **not bundled** — they're peer dependencies.
304
-
305
- ## Environment Variables
306
-
307
- See `.env.example` for all available environment variables. These are for use in your application's build process — the widget SDK receives configuration via props/init, not directly from env vars.
308
-
309
- ## TypeScript
310
-
311
- Full TypeScript support with exported types:
312
-
313
- ```ts
314
- import type {
315
- ReqdeskWidgetConfig,
316
- ThemeConfig,
317
- OidcAuthConfig,
318
- CustomerConfig,
319
- TicketResult,
320
- WidgetError,
321
- TrackedTicketResult,
322
- PublicReply,
323
- SubmitTicketData,
324
- WidgetConfigPersist,
325
- } from '@reqdesk/widget/react';
326
- ```
327
-
328
- ## Browser Support
329
-
330
- Chrome, Firefox, Safari, and Edge (latest versions). Requires Shadow DOM support.
331
-
332
- ## License
333
-
334
- Proprietary. See LICENSE file for details.
1
+ # @reqdesk/widget
2
+
3
+ Embeddable support widget SDK for [Reqdesk](https://reqdesk.com). Add a floating support button to any website — ticket submission with file attachments, ticket tracking, My Tickets, and optional Keycloak SSO authentication.
4
+
5
+ ## Features
6
+
7
+ - **Floating Action Button** — circular FAB that opens a slide-up support panel
8
+ - **Submit Tickets** — form with title, description, email, priority, and file attachments (drag-drop)
9
+ - **My Tickets** — view ticket history by email or automatically when authenticated
10
+ - **Ticket Detail** — full ticket view with reply thread, attachments, and reply composer
11
+ - **Track by Token** — anonymous ticket tracking via tracking tokens
12
+ - **Dual Auth** — API key mode (anonymous/email) + optional Keycloak OIDC login
13
+ - **Preferences** — end-user language (EN/AR with RTL), theme (light/dark/system), accent color
14
+ - **Branding** — custom logo, brand name, and "Powered by Reqdesk" footer (hideable)
15
+ - **Shadow DOM** — fully isolated styles, no CSS conflicts with host page
16
+ - **i18n** — English and Arabic built-in, custom translations supported
17
+
18
+ ## Installation
19
+
20
+ ```bash
21
+ npm install @reqdesk/widget
22
+ # or
23
+ bun add @reqdesk/widget
24
+ # or
25
+ yarn add @reqdesk/widget
26
+ ```
27
+
28
+ ## Quick Start
29
+
30
+ ### Script Tag (any website)
31
+
32
+ ```html
33
+ <script src="https://unpkg.com/@reqdesk/widget/dist/index.iife.js"></script>
34
+ <script>
35
+ ReqdeskWidget.init({
36
+ apiKey: 'rqd_live_your_api_key_here',
37
+ position: 'bottom-right',
38
+ language: 'en',
39
+ theme: {
40
+ primaryColor: '#42b983',
41
+ mode: 'light',
42
+ brandName: 'Acme Support',
43
+ logo: 'https://example.com/logo.png',
44
+ },
45
+ });
46
+ </script>
47
+ ```
48
+
49
+ ### React Component
50
+
51
+ ```tsx
52
+ import { ReqdeskProvider, FloatingWidget } from '@reqdesk/widget/react';
53
+
54
+ function App() {
55
+ return (
56
+ <ReqdeskProvider
57
+ apiKey="rqd_live_your_api_key_here"
58
+ language="en"
59
+ theme={{ primaryColor: '#42b983', mode: 'light' }}
60
+ >
61
+ <FloatingWidget position="bottom-right" />
62
+ </ReqdeskProvider>
63
+ );
64
+ }
65
+ ```
66
+
67
+ ## Configuration
68
+
69
+ ### `ReqdeskWidgetConfig`
70
+
71
+ | Property | Type | Required | Default | Description |
72
+ |----------|------|----------|---------|-------------|
73
+ | `apiKey` | `string` | Yes | — | Project API key from Reqdesk dashboard |
74
+ | `auth` | `OidcAuthConfig` | No | — | Keycloak OIDC config for authenticated mode |
75
+ | `position` | `'bottom-right' \| 'bottom-left'` | No | `'bottom-right'` | FAB button position |
76
+ | `language` | `string` | No | `'en'` | Language code (`'en'` or `'ar'`) |
77
+ | `theme` | `ThemeConfig` | No | — | Visual customization |
78
+ | `customer` | `CustomerConfig` | No | — | Pre-fill customer info |
79
+ | `translations` | `Record<string, string>` | No | — | Override built-in translations |
80
+ | `inline` | `boolean` | No | `false` | Render inline instead of floating |
81
+ | `container` | `string \| HTMLElement` | No | — | Target element for inline mode |
82
+
83
+ ### `ThemeConfig`
84
+
85
+ | Property | Type | Default | Description |
86
+ |----------|------|---------|-------------|
87
+ | `primaryColor` | `string` | `'#42b983'` | Accent color (hex) |
88
+ | `mode` | `'light' \| 'dark' \| 'auto'` | `'light'` | Color scheme |
89
+ | `borderRadius` | `string` | `'8px'` | Border radius for panels and inputs |
90
+ | `fontFamily` | `string` | `'inherit'` | Font family |
91
+ | `zIndex` | `number` | `9999` | Z-index for FAB and panel |
92
+ | `logo` | `string` | — | URL to brand logo (shown in header) |
93
+ | `brandName` | `string` | — | Brand name (shown in header instead of "Support") |
94
+ | `hideBranding` | `boolean` | `false` | Hide "Powered by Reqdesk" footer |
95
+
96
+ ### `OidcAuthConfig`
97
+
98
+ | Property | Type | Description |
99
+ |----------|------|-------------|
100
+ | `issuerUri` | `string` | Keycloak realm URL (e.g., `https://auth.example.com/realms/reqdesk`) |
101
+ | `clientId` | `string` | Keycloak client ID (e.g., `reqdesk-widget`) |
102
+
103
+ ## Authentication
104
+
105
+ The widget supports two authentication modes that can be used together:
106
+
107
+ ### API Key Mode (default)
108
+
109
+ Every widget requires an API key. In this mode:
110
+
111
+ - Visitors submit tickets anonymously or with an email address
112
+ - Email identifies the visitor for "My Tickets" (trust-based, like a contact form)
113
+ - Tracking tokens allow anonymous ticket access from any browser
114
+ - A "Remember me" checkbox saves the email to localStorage
115
+
116
+ ```ts
117
+ ReqdeskWidget.init({
118
+ apiKey: 'rqd_live_your_api_key_here',
119
+ });
120
+ ```
121
+
122
+ ### Keycloak OIDC Mode (optional)
123
+
124
+ When `auth` config is provided, the widget shows a Login button. After authenticating via Keycloak:
125
+
126
+ - User is automatically identified (no email entry needed)
127
+ - "My Tickets" shows all tickets instantly
128
+ - Tickets are attributed to the real Keycloak user account
129
+ - Session persists via silent refresh (one-time login)
130
+
131
+ ```ts
132
+ ReqdeskWidget.init({
133
+ apiKey: 'rqd_live_your_api_key_here',
134
+ auth: {
135
+ issuerUri: 'https://auth.example.com/realms/reqdesk',
136
+ clientId: 'reqdesk-widget',
137
+ },
138
+ });
139
+ ```
140
+
141
+ The widget's OIDC session is **completely independent** from your host app's auth. It uses its own Keycloak client (`reqdesk-widget`) with separate tokens.
142
+
143
+ #### Keycloak Client Setup
144
+
145
+ Add a `reqdesk-widget` client to your Keycloak realm:
146
+
147
+ - **Client Type**: Public (`publicClient: true`)
148
+ - **Flow**: Authorization Code with PKCE (`standardFlowEnabled: true`)
149
+ - **Redirect URIs**: `*` (or restrict to your domains)
150
+ - **Web Origins**: `+` (CORS for any origin)
151
+ - **Client Scopes**: `basic, openid, profile, email`
152
+
153
+ ## Events
154
+
155
+ ```ts
156
+ // Vanilla JS
157
+ ReqdeskWidget.on('ticket:created', (ticket) => {
158
+ console.log('Ticket created:', ticket.ticketNumber);
159
+ });
160
+
161
+ ReqdeskWidget.on('open', () => console.log('Widget opened'));
162
+ ReqdeskWidget.on('close', () => console.log('Widget closed'));
163
+ ReqdeskWidget.on('error', (err) => console.error(err));
164
+ ```
165
+
166
+ | Event | Payload | Description |
167
+ |-------|---------|-------------|
168
+ | `open` | — | Widget panel opened |
169
+ | `close` | — | Widget panel closed |
170
+ | `ticket:created` | `TicketResult` | Ticket submitted successfully |
171
+ | `ticket:tracked` | `TrackedTicketResult` | Ticket tracked by token |
172
+ | `reply:sent` | — | Reply submitted |
173
+ | `error` | `WidgetError` | Error occurred |
174
+
175
+ ## Vanilla JS API
176
+
177
+ ```ts
178
+ ReqdeskWidget.init(config) // Initialize the widget
179
+ ReqdeskWidget.open() // Open the panel
180
+ ReqdeskWidget.close() // Close the panel
181
+ ReqdeskWidget.toggle() // Toggle open/close
182
+ ReqdeskWidget.setLanguage('ar') // Change language (supports RTL)
183
+ ReqdeskWidget.setTheme({ mode: 'dark' }) // Update theme
184
+ ReqdeskWidget.identify({ email: 'user@example.com', name: 'Alice' })
185
+ ReqdeskWidget.on(event, callback)
186
+ ReqdeskWidget.destroy() // Remove widget from DOM
187
+ ```
188
+
189
+ ## React API
190
+
191
+ ### Components
192
+
193
+ ```tsx
194
+ import {
195
+ ReqdeskProvider, // Context provider (required wrapper)
196
+ FloatingWidget, // FAB + slide-up panel
197
+ TicketForm, // Standalone ticket form (inline mode)
198
+ SupportPortal, // Standalone support portal
199
+ ShadowRoot, // Shadow DOM wrapper for custom components
200
+ } from '@reqdesk/widget/react';
201
+ ```
202
+
203
+ ### `ReqdeskProvider` Props
204
+
205
+ | Prop | Type | Required | Description |
206
+ |------|------|----------|-------------|
207
+ | `apiKey` | `string` | Yes | Project API key |
208
+ | `auth` | `OidcAuthConfig` | No | Keycloak OIDC config |
209
+ | `theme` | `ThemeConfig` | No | Visual customization |
210
+ | `language` | `string` | No | Language code |
211
+ | `customer` | `CustomerConfig` | No | Pre-fill customer info |
212
+ | `translations` | `Record<string, string>` | No | Custom translations |
213
+
214
+ ### `FloatingWidget` Props
215
+
216
+ | Prop | Type | Default | Description |
217
+ |------|------|---------|-------------|
218
+ | `position` | `'bottom-right' \| 'bottom-left'` | `'bottom-right'` | FAB position |
219
+ | `contained` | `boolean` | `false` | Use absolute positioning (for previews) |
220
+ | `onTicketCreated` | `(ticket: TicketResult) => void` | — | Callback on ticket creation |
221
+ | `onError` | `(error: WidgetError) => void` | — | Callback on error |
222
+
223
+ ### Hooks
224
+
225
+ ```tsx
226
+ import { useReqdesk } from '@reqdesk/widget/react';
227
+
228
+ function CustomForm() {
229
+ const { submitTicket, trackTicket, isLoading, error } = useReqdesk();
230
+
231
+ const handleSubmit = async () => {
232
+ const result = await submitTicket({
233
+ title: 'Bug report',
234
+ email: 'user@example.com',
235
+ priority: 'high',
236
+ });
237
+ console.log('Created:', result.ticketNumber);
238
+ };
239
+ }
240
+ ```
241
+
242
+ ## File Attachments
243
+
244
+ The widget supports file attachments on ticket submission and replies:
245
+
246
+ - **Drag-and-drop** or click to browse
247
+ - **Max 5 files** per submission, 5 per reply
248
+ - **Max 10MB** per file (configurable per project)
249
+ - **Allowed types**: Images (JPEG, PNG, GIF, WebP), PDF, Office documents, text, CSV, ZIP
250
+ - **Blocked**: Executables (.exe, .bat, .cmd, .sh, .ps1, .msi, .dll, .scr)
251
+ - **Upload progress** shown per-file after submission
252
+
253
+ Files are uploaded sequentially after the ticket/reply is created.
254
+
255
+ ## Customization
256
+
257
+ ### Custom Translations
258
+
259
+ Override any built-in string:
260
+
261
+ ```ts
262
+ ReqdeskWidget.init({
263
+ apiKey: '...',
264
+ translations: {
265
+ 'widget.title': 'Help Center',
266
+ 'form.submit': 'Send Request',
267
+ 'menu.newTicket': 'New Request',
268
+ },
269
+ });
270
+ ```
271
+
272
+ See `src/i18n/en.ts` for all available translation keys.
273
+
274
+ ### RTL Support
275
+
276
+ Set `language: 'ar'` for full RTL layout. The widget automatically mirrors all UI elements.
277
+
278
+ ### End-User Preferences
279
+
280
+ Visitors can customize their experience from the Preferences menu:
281
+
282
+ - **Language**: English / Arabic toggle
283
+ - **Theme**: Light / Dark / System
284
+ - **Accent Color**: 5 preset colors (green, blue, purple, orange, red)
285
+
286
+ Preferences are persisted to `localStorage` per API key.
287
+
288
+ ## Architecture
289
+
290
+ - **Shadow DOM**: Complete CSS isolation — host page styles cannot affect the widget
291
+ - **Zero global state**: Widget context is self-contained, no window globals
292
+ - **ofetch**: HTTP client with automatic retry, timeout, and auth interceptors
293
+ - **TanStack Query**: Data fetching with caching, optimistic updates, and loading states (React only)
294
+ - **Optional dependencies**: React, oidc-spa, and TanStack Query are all optional peer deps
295
+
296
+ ### Bundle Sizes
297
+
298
+ | Entry | Size (gzip) |
299
+ |-------|-------------|
300
+ | `index.iife.js` (vanilla) | ~12 KB |
301
+ | `react.js` (ESM) | ~6 KB |
302
+
303
+ React, oidc-spa, and TanStack Query are **not bundled** — they're peer dependencies.
304
+
305
+ ## Environment Variables
306
+
307
+ See `.env.example` for all available environment variables. These are for use in your application's build process — the widget SDK receives configuration via props/init, not directly from env vars.
308
+
309
+ ## TypeScript
310
+
311
+ Full TypeScript support with exported types:
312
+
313
+ ```ts
314
+ import type {
315
+ ReqdeskWidgetConfig,
316
+ ThemeConfig,
317
+ OidcAuthConfig,
318
+ CustomerConfig,
319
+ TicketResult,
320
+ WidgetError,
321
+ TrackedTicketResult,
322
+ PublicReply,
323
+ SubmitTicketData,
324
+ WidgetConfigPersist,
325
+ } from '@reqdesk/widget/react';
326
+ ```
327
+
328
+ ## Browser Support
329
+
330
+ Chrome, Firefox, Safari, and Edge (latest versions). Requires Shadow DOM support.
331
+
332
+ ## License
333
+
334
+ Proprietary. See LICENSE file for details.