@uservibesos/web-component 1.0.1 → 1.0.3

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.
Files changed (3) hide show
  1. package/README.md +52 -496
  2. package/dist/widget.js +38 -7
  3. package/package.json +13 -3
package/README.md CHANGED
@@ -1,533 +1,89 @@
1
- # @uservibeos/web-component
1
+ # @uservibesos/web-component
2
2
 
3
- **Authenticated** Feature Request Widget as a Web Component (Custom Element).
4
-
5
- ⚠️ **API KEY REQUIRED** - This widget requires authentication. Only compatible with server-rendered applications.
3
+ Feature request and bug report widget as a Web Component for UserVibesOS.
6
4
 
7
5
  ## Installation
8
6
 
9
- ### Via CDN (Recommended)
10
-
11
- Add the script tag to your HTML:
12
-
13
- ```html
14
- <script src="https://app.uservibeos.com/widget-assets/v1.0.0/widget.js"></script>
15
- ```
16
-
17
- ### Via NPM
18
-
19
7
  ```bash
20
- npm install @uservibeos/web-component
8
+ npm install @uservibesos/web-component
21
9
  ```
22
10
 
23
- Then import in your JavaScript:
11
+ Or use via CDN:
24
12
 
25
- ```javascript
26
- import '@uservibeos/web-component';
13
+ ```html
14
+ <script src="https://app.uservibesos.com/widget-assets/v1.0.0/widget.js"></script>
27
15
  ```
28
16
 
29
17
  ## Usage
30
18
 
31
- ### Prerequisites
32
- - Server-rendered application (Next.js, Remix, SvelteKit, etc.)
33
- - UserVibeOS API key stored in environment variables
34
-
35
- **NOT compatible with:**
36
- - Static HTML sites
37
- - Client-only React/Vue apps
38
- - WordPress (unless using custom server integration)
39
-
40
- ### Required Attributes
41
-
42
- | Attribute | Type | Required | Description |
43
- |-----------|------|----------|-------------|
44
- | `project` | string | **YES** | Your project slug from UserVibeOS dashboard |
45
- | `api-key` | string | **YES** | Your UserVibeOS API key from environment variables |
46
-
47
- ### Optional Attributes
48
-
49
- | Attribute | Type | Default | Description |
50
- |-----------|------|---------|-------------|
51
- | `theme` | `'light' \| 'dark'` | `'light'` | Color theme |
52
- | `height` | string | `'600px'` | Widget height (CSS value) |
53
- | `base-url` | string | Auto-detected | Custom base URL (for development/self-hosting) |
54
-
55
- ### Security Requirements 🔒
56
-
57
- **API keys are MANDATORY:**
58
- - ✅ Widgets will NOT load without a valid API key
59
- - ✅ API key must come from environment variables
60
- - ✅ Only use in server-rendered applications
61
- - ❌ Never hardcode API keys in HTML or JavaScript
62
- - ❌ Not compatible with static sites or client-only apps
63
-
64
- ### Events
19
+ ### Basic Usage (with JWT)
65
20
 
66
- The widget emits custom events you can listen to:
67
-
68
- ```javascript
69
- const widget = document.querySelector('uservibe-widget');
70
-
71
- widget.addEventListener('request-submitted', (event) => {
72
- console.log('New request submitted:', event.detail);
73
- });
74
-
75
- widget.addEventListener('vote-added', (event) => {
76
- console.log('Vote added:', event.detail);
77
- });
78
- ```
79
-
80
- ## Examples
81
-
82
- ### Next.js App Router (Recommended)
83
-
84
- **Step 1:** Add your API key to `.env.local`:
85
- ```bash
86
- USERVIBE_API_KEY=uv_live_your_key_here
87
- ```
88
-
89
- **Step 2:** Add to `.gitignore`:
90
- ```
91
- .env.local
92
- ```
93
-
94
- **Step 3:** Use in your server component:
95
- ```tsx
96
- // app/feedback/page.tsx (Next.js App Router Server Component)
97
- export default function FeedbackPage() {
98
- return (
99
- <div>
100
- <h1>Feature Requests</h1>
101
- <script src="https://app.uservibeos.com/widget-assets/v1.0.0/widget.js"></script>
102
- <uservibe-widget
103
- project="my-app"
104
- api-key={process.env.USERVIBE_API_KEY}
105
- theme="light"
106
- />
107
- </div>
108
- );
109
- }
21
+ ```html
22
+ <uservibes-widget
23
+ project="your-project-slug"
24
+ jwt="your-jwt-token"
25
+ theme="light"
26
+ ></uservibes-widget>
110
27
  ```
111
28
 
112
- ### With Custom Styling
29
+ ### With Custom Height
113
30
 
114
- ```tsx
115
- <uservibe-widget
116
- project="my-app"
117
- api-key={process.env.USERVIBE_API_KEY}
118
- theme="dark"
31
+ ```html
32
+ <uservibes-widget
33
+ project="your-project-slug"
34
+ jwt="your-jwt-token"
119
35
  height="800px"
120
- />
121
-
122
- ### Development/Local Testing
123
-
124
- When testing locally with UserVibeOS running on a different port:
125
-
126
- ```tsx
127
- // Test app on localhost:3002, UserVibeOS on localhost:3000
128
- export default function TestPage() {
129
- return (
130
- <div>
131
- <script src="http://localhost:3000/widget-assets/v1.0.0/widget.js"></script>
132
- <uservibe-widget
133
- project="my-app"
134
- base-url="http://localhost:3000"
135
- api-key={process.env.USERVIBE_API_KEY}
136
- />
137
- </div>
138
- );
139
- }
140
- ```
141
-
142
- Don't forget to add TypeScript types:
143
-
144
- ```typescript
145
- declare global {
146
- namespace JSX {
147
- interface IntrinsicElements {
148
- 'uservibe-widget': {
149
- project: string;
150
- 'api-key': string; // REQUIRED
151
- theme?: 'light' | 'dark';
152
- height?: string;
153
- 'base-url'?: string;
154
- };
155
- }
156
- }
157
- }
36
+ ></uservibes-widget>
158
37
  ```
159
38
 
160
- ## 🔐 Two-Tier Security Model
161
-
162
- UserVibeOS offers two security tiers for widget authentication. Choose based on your security requirements.
163
-
164
- ---
39
+ ### Attributes
165
40
 
166
- ### TIER 1: Public Keys (PK) - Recommended for Most Users
41
+ - `project` (required): Your project slug or ID
42
+ - `jwt` (optional): JWT token for authenticated users
43
+ - `theme` (optional): `light` or `dark` (default: `light`)
44
+ - `height` (optional): Widget height (default: `90vh`)
45
+ - `mode` (optional): `feature-request` or `roadmap` (default: `feature-request`)
46
+ - `base-url` (optional): Custom API base URL (for development)
167
47
 
168
- **Best for:** Standard web applications with good security needs.
48
+ ## Features
169
49
 
170
- **How it works:**
171
- - Create a Public Key (PK) in the API Keys dashboard
172
- - Configure allowed origins (up to 5 domains)
173
- - Use server-side rendering to inject the key
174
- - Widget validates origin against your whitelist
50
+ - Feature request submission with AI-powered chat extraction (max 4 questions)
51
+ - Bug report submission with structured field extraction (max 7 questions)
52
+ - Roadmap view
53
+ - Changelog view
54
+ - Voting and commenting
55
+ - ✅ JWT authentication support
56
+ - ✅ Dark mode support
57
+ - ✅ Responsive design
58
+ - ✅ Zero dependencies (vanilla web component)
175
59
 
176
- **Security features:**
177
- - ✅ Origin/referrer enforcement
178
- - ✅ Rate limiting (5 req/min per IP/fingerprint)
179
- - ✅ Advanced browser fingerprinting
180
- - ✅ Behavioral anomaly detection
181
- - ✅ Real-time security event logging
60
+ ## Development
182
61
 
183
- **Setup:**
184
62
  ```bash
185
- # .env.local
186
- USERVIBE_PUBLIC_KEY=pk_live_your_key_here
187
- ```
188
-
189
- ```tsx
190
- // app/feedback/page.tsx (Next.js Server Component)
191
- export default function FeedbackPage() {
192
- return (
193
- <div>
194
- <script src="https://app.uservibeos.com/widget-assets/v1.0.0/widget.js"></script>
195
- <uservibe-widget
196
- project="my-app"
197
- api-key={process.env.USERVIBE_PUBLIC_KEY}
198
- theme="light"
199
- />
200
- </div>
201
- );
202
- }
203
- ```
204
-
205
- **Limitations:**
206
- - Keys visible in browser's view source
207
- - Relies on origin validation (can be bypassed by determined attackers)
208
- - Not suitable for high-security applications
209
-
210
- ---
211
-
212
- ### TIER 2: JWT Proxy - Maximum Security
213
-
214
- **Best for:** Financial apps, healthcare, or maximum security requirements.
215
-
216
- **How it works:**
217
- 1. Your backend securely holds your Secret Key (SK)
218
- 2. Widget calls YOUR backend proxy endpoint (not UserVibeOS directly)
219
- 3. Your backend exchanges SK for a short-lived JWT token (5 min expiry)
220
- 4. Backend forwards request to UserVibeOS with JWT
221
- 5. **Secret Key never leaves your server**
222
-
223
- **Security features:**
224
- - ✅ All Tier 1 features PLUS:
225
- - ✅ Secret Key (SK) never exposed to client
226
- - ✅ JWT tokens expire in 5 minutes
227
- - ✅ One-time use tokens (replay protection)
228
- - ✅ Backend request validation
229
- - ✅ Full cryptographic signing
230
- - ✅ Complete audit trail
231
-
232
- ---
233
-
234
- ### Tier 2 Implementation Guide
235
-
236
- #### Step 1: Enable JWT in Project Settings
237
-
238
- 1. Go to your UserVibeOS Dashboard → Projects → [Your Project]
239
- 2. Navigate to "Project Settings"
240
- 3. Enable "JWT Authentication (Tier 2)"
241
- 4. Select or create a Secret Key (SK)
242
- 5. Set token expiry (default: 5 minutes)
243
-
244
- ---
245
-
246
- #### Step 2: Create Backend Proxy
247
-
248
- Choose your framework:
249
-
250
- **Next.js App Router:**
251
-
252
- ```typescript
253
- // app/api/uservibe-proxy/route.ts
254
- import { NextRequest, NextResponse } from 'next/server';
255
-
256
- // Token cache (use Redis in production for multi-instance)
257
- let tokenCache: { token: string; expiresAt: number } | null = null;
258
-
259
- async function getValidToken(projectId: string): Promise<string> {
260
- // Check cache
261
- if (tokenCache && tokenCache.expiresAt > Date.now() + 60000) {
262
- return tokenCache.token;
263
- }
264
-
265
- // Exchange Secret Key for JWT token
266
- const response = await fetch('https://app.uservibeos.com/api/token/exchange', {
267
- method: 'POST',
268
- headers: { 'Content-Type': 'application/json' },
269
- body: JSON.stringify({
270
- secretKey: process.env.USERVIBE_SECRET_KEY, // SK never exposed
271
- projectId,
272
- origin: process.env.NEXT_PUBLIC_SITE_URL || 'https://yourapp.com',
273
- }),
274
- });
275
-
276
- if (!response.ok) {
277
- throw new Error('Token exchange failed');
278
- }
279
-
280
- const data = await response.json();
63
+ # Install dependencies
64
+ npm install
281
65
 
282
- // Cache token
283
- tokenCache = {
284
- token: data.token,
285
- expiresAt: data.expiresAt,
286
- };
66
+ # Build the widget
67
+ npm run build
287
68
 
288
- return data.token;
289
- }
290
-
291
- export async function POST(req: NextRequest) {
292
- try {
293
- const body = await req.json();
294
- const { projectId, action, ...payload } = body;
295
-
296
- // Get valid JWT token
297
- const jwtToken = await getValidToken(projectId);
298
-
299
- // Forward to Convex with JWT
300
- const convexUrl = process.env.CONVEX_URL || 'https://your-deployment.convex.cloud';
301
- const response = await fetch(`${convexUrl}/api/function/${action}`, {
302
- method: 'POST',
303
- headers: {
304
- 'Content-Type': 'application/json',
305
- 'Authorization': `Bearer ${jwtToken}`,
306
- },
307
- body: JSON.stringify(payload),
308
- });
309
-
310
- const data = await response.json();
311
- return NextResponse.json(data);
312
-
313
- } catch (error) {
314
- console.error('Proxy error:', error);
315
- return NextResponse.json(
316
- { error: 'Proxy request failed' },
317
- { status: 500 }
318
- );
319
- }
320
- }
321
- ```
322
-
323
- **Express.js:**
324
-
325
- ```javascript
326
- // server.js
327
- const express = require('express');
328
- const app = express();
329
-
330
- let tokenCache = null;
331
-
332
- async function getValidToken(projectId) {
333
- if (tokenCache && tokenCache.expiresAt > Date.now() + 60000) {
334
- return tokenCache.token;
335
- }
336
-
337
- const response = await fetch('https://app.uservibeos.com/api/token/exchange', {
338
- method: 'POST',
339
- headers: { 'Content-Type': 'application/json' },
340
- body: JSON.stringify({
341
- secretKey: process.env.USERVIBE_SECRET_KEY,
342
- projectId,
343
- origin: process.env.SITE_URL || 'https://yourapp.com',
344
- }),
345
- });
346
-
347
- const data = await response.json();
348
- tokenCache = { token: data.token, expiresAt: data.expiresAt };
349
- return data.token;
350
- }
351
-
352
- app.post('/api/uservibe-proxy', async (req, res) => {
353
- try {
354
- const { projectId, action, ...payload } = req.body;
355
- const token = await getValidToken(projectId);
356
-
357
- const convexUrl = process.env.CONVEX_URL;
358
- const response = await fetch(`${convexUrl}/api/function/${action}`, {
359
- method: 'POST',
360
- headers: {
361
- 'Content-Type': 'application/json',
362
- 'Authorization': `Bearer ${token}`,
363
- },
364
- body: JSON.stringify(payload),
365
- });
366
-
367
- const data = await response.json();
368
- res.json(data);
369
- } catch (error) {
370
- console.error('Proxy error:', error);
371
- res.status(500).json({ error: 'Proxy request failed' });
372
- }
373
- });
374
-
375
- app.listen(3000);
69
+ # Watch mode for development
70
+ npm run dev
376
71
  ```
377
72
 
378
- ---
73
+ ## Publishing
379
74
 
380
- #### Step 3: Configure Widget for JWT Mode
381
-
382
- ```html
383
- <script src="https://app.uservibeos.com/widget-assets/v1.0.0/widget.js"></script>
384
- <uservibe-widget
385
- project="my-app"
386
- jwt-mode="true"
387
- proxy-url="/api/uservibe-proxy"
388
- theme="light"
389
- ></uservibe-widget>
390
- ```
391
-
392
- **Key attributes for Tier 2:**
393
- - `jwt-mode="true"` - Enables JWT authentication mode
394
- - `proxy-url="/api/uservibe-proxy"` - Your backend proxy endpoint
395
-
396
- ---
397
-
398
- #### Step 4: Environment Variables
75
+ The widget is automatically published to NPM when a new release is created on GitHub. You can also publish manually:
399
76
 
400
77
  ```bash
401
- # .env.local (Backend only - NEVER commit)
402
-
403
- # Tier 2: Secret Key (SK) for JWT signing
404
- USERVIBE_SECRET_KEY=sk_live_your_secret_key_here
405
-
406
- # Your Convex deployment URL
407
- CONVEX_URL=https://your-deployment.convex.cloud
408
-
409
- # Your site URL (for origin validation)
410
- NEXT_PUBLIC_SITE_URL=https://yourapp.com
78
+ npm run publish:npm
411
79
  ```
412
80
 
413
- ---
414
-
415
- ### Production Considerations for Tier 2
416
-
417
- #### Token Caching
418
-
419
- **Single-instance deployments** (Vercel Hobby, single server):
420
- - Use in-memory caching (as shown above)
421
-
422
- **Multi-instance deployments** (Vercel Pro, load-balanced):
423
- - Use Redis or similar distributed cache
424
- - Share tokens across instances
425
-
426
- ```typescript
427
- // Example with Upstash Redis
428
- import { Redis } from '@upstash/redis';
429
-
430
- const redis = new Redis({
431
- url: process.env.UPSTASH_REDIS_URL!,
432
- token: process.env.UPSTASH_REDIS_TOKEN!,
433
- });
434
-
435
- async function getValidToken(projectId: string): Promise<string> {
436
- const cacheKey = `uservibe:jwt:${projectId}`;
437
-
438
- // Check cache
439
- const cached = await redis.get<{ token: string; expiresAt: number }>(cacheKey);
440
- if (cached && cached.expiresAt > Date.now() + 60000) {
441
- return cached.token;
442
- }
443
-
444
- // Exchange for new token
445
- const token = await exchangeToken(projectId);
446
-
447
- // Cache with TTL (4 min, tokens expire in 5)
448
- await redis.setex(cacheKey, 240, token);
449
-
450
- return token.token;
451
- }
452
- ```
453
-
454
- #### Error Handling
455
-
456
- Always implement graceful error handling:
457
-
458
- ```typescript
459
- try {
460
- const token = await getValidToken(projectId);
461
- // Use token...
462
- } catch (error) {
463
- console.error('JWT token exchange failed:', error);
464
-
465
- // Return user-friendly error
466
- return NextResponse.json(
467
- { error: 'Authentication failed. Please try again.' },
468
- { status: 503 }
469
- );
470
- }
471
- ```
472
-
473
- ---
474
-
475
- ### Complete Implementation Guides
476
-
477
- For detailed, framework-specific guides:
478
-
479
- - 📘 [Next.js App Router Proxy Guide](https://github.com/uservibeOS/uservibeOS/blob/main/docs/jwt-proxy-nextjs.md)
480
- - 📗 [React + Express Proxy Guide](https://github.com/uservibeOS/uservibeOS/blob/main/docs/jwt-proxy-react-express.md)
481
- - 📕 [Standalone Express Proxy Guide](https://github.com/uservibeOS/uservibeOS/blob/main/docs/jwt-proxy-express.md)
482
- - 📙 [Migration Guide: Upgrading to Two-Tier Security](https://github.com/uservibeOS/uservibeOS/blob/main/docs/migration-guide-two-tier-security.md)
483
-
484
- ---
485
-
486
- ## API Key Security Best Practices 🔒
487
-
488
- ### Required Security Practices (Both Tiers)
489
-
490
- ✅ **MUST DO:**
491
- - Store API keys in `.env.local` (never in code)
492
- - Add `.env.local` to `.gitignore`
493
- - Use server-side rendering only (Next.js Server Components, Remix loaders, etc.)
494
- - Use different API keys for development and production
495
- - Rotate API keys if compromised
496
-
497
- ❌ **NEVER DO:**
498
- - Hardcode API keys in HTML, JavaScript, or TSX files
499
- - Commit API keys to version control (GitHub, GitLab, etc.)
500
- - Use this widget in static HTML sites
501
- - Use this widget in client-only React/Vue apps
502
- - Expose API keys in client-side environment variables (NEXT_PUBLIC_, VITE_, etc.)
503
- - Share API keys in public repositories or screenshots
504
-
505
- ### Tier 1 vs Tier 2: When to Use
506
-
507
- | Use Case | Recommended Tier | Reason |
508
- |----------|-----------------|---------|
509
- | Standard web app | Tier 1 (PK) | Good security, easier setup |
510
- | Financial services | Tier 2 (JWT) | Regulatory compliance |
511
- | Healthcare (HIPAA) | Tier 2 (JWT) | Maximum security required |
512
- | E-commerce | Tier 1 or 2 | Depends on sensitivity |
513
- | Internal tools | Tier 1 (PK) | Lower risk, simpler |
514
- | Public-facing SaaS | Tier 2 (JWT) | Higher attack surface |
515
-
516
- ### Important Notes
517
-
518
- ⚠️ **Tier 1 Limitation**: Public Keys visible in browser's view source (but origin-restricted)
519
-
520
- ⚠️ **Tier 2 Requirement**: Requires backend infrastructure to run proxy
521
-
522
- ⚠️ **Not for Static Sites**: Both tiers require server-side rendering
523
-
524
- ## Browser Support
525
-
526
- - Chrome/Edge: ✅
527
- - Firefox: ✅
528
- - Safari: ✅
529
- - IE11: ❌ (Custom Elements not supported)
530
-
531
81
  ## License
532
82
 
533
83
  MIT
84
+
85
+ ## Links
86
+
87
+ - [NPM Package](https://www.npmjs.com/package/@uservibesos/web-component)
88
+ - [GitHub Repository](https://github.com/harperaa/uservibesOS)
89
+ - [UserVibesOS Website](https://uservibesos.com)
package/dist/widget.js CHANGED
@@ -1,4 +1,4 @@
1
- function f(o){if(typeof document>"u"||document.querySelector(`link[href="${o}"]`))return;let s=document.createElement("link");s.rel="preconnect",s.href=o,document.head.appendChild(s)}var c=class extends HTMLElement{constructor(){super();this.baseUrl="https://app.uservibesos.com";this.iframe=null;this.skeleton=null;this.loaded=!1;this.attachShadow({mode:"open"})}static get observedAttributes(){return["project","jwt","theme","height","mode","base-url"]}connectedCallback(){this.render()}attributeChangedCallback(e,t,r){t!==r&&this.render()}render(){let e=this.getAttribute("project"),t=this.getAttribute("jwt"),r=this.getAttribute("theme")||"light",d=this.getAttribute("height")||"600px",l=this.getAttribute("mode")||"feature-request",a=this.getAttribute("base-url");if(!e){this.renderError('Missing required "project" attribute (project ID)');return}a?this.baseUrl=a:typeof window<"u"&&window.location.hostname==="localhost"&&(this.baseUrl=window.location.origin),f(this.baseUrl);let n=`${this.baseUrl}/embed?projectId=${encodeURIComponent(e)}&mode=${l}`;t&&(n+=`&jwt=${encodeURIComponent(t)}`);let i=`
1
+ function g(n){if(typeof document>"u"||document.querySelector(`link[href="${n}"]`))return;let s=document.createElement("link");s.rel="preconnect",s.href=n,document.head.appendChild(s)}var m=class extends HTMLElement{constructor(){super();this.baseUrl="https://app.uservibesos.com";this.iframe=null;this.skeleton=null;this.loaded=!1;this.isFullscreen=!1;this.originalIframeStyles="";this.attachShadow({mode:"open"})}static get observedAttributes(){return["project","jwt","theme","height","mode","base-url"]}connectedCallback(){this.render()}attributeChangedCallback(e,t,r){t!==r&&this.render()}render(){let e=this.getAttribute("project"),t=this.getAttribute("jwt"),r=this.getAttribute("theme")||"light",a=this.getAttribute("height")||"90vh",l=this.getAttribute("mode")||"feature-request",d=this.getAttribute("base-url");if(!e){this.renderError('Missing required "project" attribute (project ID)');return}d?this.baseUrl=d:typeof window<"u"&&window.location.hostname==="localhost"&&(this.baseUrl=window.location.origin),g(this.baseUrl);let o=`${this.baseUrl}/embed?projectId=${encodeURIComponent(e)}&mode=${l}`;t&&(o+=`&jwt=${encodeURIComponent(t)}`);let i=`
2
2
  <style>
3
3
  :host {
4
4
  display: block;
@@ -6,11 +6,11 @@ function f(o){if(typeof document>"u"||document.querySelector(`link[href="${o}"]`
6
6
  }
7
7
  .widget-container {
8
8
  position: relative;
9
- min-height: ${d};
9
+ min-height: ${a};
10
10
  }
11
11
  iframe {
12
12
  width: 100%;
13
- height: ${d};
13
+ height: ${a};
14
14
  border: none;
15
15
  border-radius: 8px;
16
16
  overflow: hidden;
@@ -20,6 +20,21 @@ function f(o){if(typeof document>"u"||document.querySelector(`link[href="${o}"]`
20
20
  iframe.loaded {
21
21
  opacity: 1;
22
22
  }
23
+ iframe.fullscreen {
24
+ position: fixed !important;
25
+ top: 0 !important;
26
+ left: 0 !important;
27
+ right: 0 !important;
28
+ bottom: 0 !important;
29
+ width: 100vw !important;
30
+ height: 100vh !important;
31
+ max-width: 100vw !important;
32
+ max-height: 100vh !important;
33
+ border-radius: 0 !important;
34
+ z-index: 999999 !important;
35
+ margin: 0 !important;
36
+ padding: 0 !important;
37
+ }
23
38
  .skeleton {
24
39
  position: absolute;
25
40
  top: 0;
@@ -108,7 +123,7 @@ function f(o){if(typeof document>"u"||document.querySelector(`link[href="${o}"]`
108
123
  font-family: system-ui, sans-serif;
109
124
  }
110
125
  </style>
111
- `,u=`
126
+ `,f=`
112
127
  <div class="widget-container">
113
128
 
114
129
  <div class="skeleton" id="skeleton">
@@ -136,13 +151,13 @@ function f(o){if(typeof document>"u"||document.querySelector(`link[href="${o}"]`
136
151
  </div>
137
152
 
138
153
  <iframe
139
- src="${n}"
154
+ src="${o}"
140
155
  title="Feature Requests"
141
156
  loading="lazy"
142
157
  allow="clipboard-write"
143
158
  ></iframe>
144
159
  </div>
145
- `;this.shadowRoot&&(this.shadowRoot.innerHTML=i+u,this.iframe=this.shadowRoot.querySelector("iframe"),this.skeleton=this.shadowRoot.querySelector("#skeleton"),this.setupLoadHandler(),this.setupMessageListener())}setupLoadHandler(){this.iframe&&(this.iframe.addEventListener("load",()=>{this.loaded=!0,this.iframe.classList.add("loaded"),this.skeleton&&this.skeleton.classList.add("hidden"),this.iframe.contentWindow&&this.iframe.contentWindow.postMessage({type:"USERVIBES_INIT"},this.baseUrl)}),this.iframe.addEventListener("error",()=>{this.renderError("Failed to load widget. Please try again later.")}),setTimeout(()=>{!this.loaded&&this.iframe&&(this.iframe.classList.add("loaded"),this.skeleton&&this.skeleton.classList.add("hidden"))},1e4))}renderError(e){let r=`
160
+ `;this.shadowRoot&&(this.shadowRoot.innerHTML=i+f,this.iframe=this.shadowRoot.querySelector("iframe"),this.skeleton=this.shadowRoot.querySelector("#skeleton"),this.setupLoadHandler(),this.setupMessageListener())}setupLoadHandler(){this.iframe&&(this.iframe.addEventListener("load",()=>{this.loaded=!0,this.iframe.classList.add("loaded"),this.skeleton&&this.skeleton.classList.add("hidden"),console.log("[Widget] DEBUG: Iframe loaded, sending USERVIBES_INIT to:",this.baseUrl),this.iframe.contentWindow?(this.iframe.contentWindow.postMessage({type:"USERVIBES_INIT"},this.baseUrl),console.log("[Widget] DEBUG: \u2705 USERVIBES_INIT message sent")):console.error("[Widget] DEBUG: \u274C No contentWindow, cannot send USERVIBES_INIT")}),this.iframe.addEventListener("error",()=>{this.renderError("Failed to load widget. Please try again later.")}),setTimeout(()=>{!this.loaded&&this.iframe&&(this.iframe.classList.add("loaded"),this.skeleton&&this.skeleton.classList.add("hidden"))},1e4))}renderError(e){let r=`
146
161
  <style>
147
162
  .error {
148
163
  padding: 2rem;
@@ -157,4 +172,20 @@ function f(o){if(typeof document>"u"||document.querySelector(`link[href="${o}"]`
157
172
  <div class="error">
158
173
  <strong>Widget Error:</strong> ${this.escapeHtml(e)}
159
174
  </div>
160
- `;this.shadowRoot&&(this.shadowRoot.innerHTML=r)}escapeHtml(e){if(!e||typeof e!="string")return"";let t=document.createElement("div");return t.textContent=e,t.innerHTML}setupMessageListener(){window.addEventListener("message",e=>{e.origin===this.baseUrl&&(e.data.type==="USERVIBES_HEIGHT_UPDATE"&&this.iframe&&(this.iframe.style.height=`${e.data.height}px`),e.data.type==="USERVIBES_REQUEST_SUBMITTED"&&this.dispatchEvent(new CustomEvent("request-submitted",{detail:e.data.payload})),e.data.type==="USERVIBES_VOTE_ADDED"&&this.dispatchEvent(new CustomEvent("vote-added",{detail:e.data.payload})))})}setJWT(e){this.setAttribute("jwt",e)}};typeof window<"u"&&typeof customElements<"u"&&!customElements.get("uservibes-widget")&&customElements.define("uservibes-widget",c);function b(o){let{projectId:s,jwt:e,container:t,theme:r,height:d,mode:l,baseUrl:a}=o;if(!s)return console.error('[UserVibesOS] Missing required "projectId" in config'),null;if(!t)return console.error('[UserVibesOS] Missing required "container" in config'),null;let n=typeof t=="string"?document.querySelector(t):t;if(!n)return console.error(`[UserVibesOS] Container "${t}" not found`),null;let i=document.createElement("uservibes-widget");return i.setAttribute("project",s),e&&i.setAttribute("jwt",e),r&&i.setAttribute("theme",r),d&&i.setAttribute("height",d),l&&i.setAttribute("mode",l),a&&i.setAttribute("base-url",a),n.innerHTML="",n.appendChild(i),{element:i,setJWT:m=>i.setJWT(m),destroy:()=>n.removeChild(i)}}var h={init:b};var g=h;typeof window<"u"&&(window.UserVibesOS=h);export{h as UserVibesOS,c as UserVibesWidget,g as default,b as init};
175
+ `;this.shadowRoot&&(this.shadowRoot.innerHTML=r)}escapeHtml(e){if(!e||typeof e!="string")return"";let t=document.createElement("div");return t.textContent=e,t.innerHTML}setupMessageListener(){window.addEventListener("message",e=>{console.log("[Widget] Received message:",e.data?.type,"from:",e.origin,"expected:",this.baseUrl),e.origin===this.baseUrl&&(e.data.type==="USERVIBES_READY"&&this.iframe?.contentWindow&&(console.log("[Widget] DEBUG: Received USERVIBES_READY, resending USERVIBES_INIT"),this.iframe.contentWindow.postMessage({type:"USERVIBES_INIT"},this.baseUrl)),e.data.type==="USERVIBES_HEIGHT_UPDATE"&&this.iframe&&(this.iframe.style.height=`${e.data.height}px`),e.data.type==="USERVIBES_REQUEST_SUBMITTED"&&this.dispatchEvent(new CustomEvent("request-submitted",{detail:e.data.payload})),e.data.type==="USERVIBES_VOTE_ADDED"&&this.dispatchEvent(new CustomEvent("vote-added",{detail:e.data.payload})),e.data.type==="USERVIBES_ENTER_FULLSCREEN"&&(console.log("[Widget] Received ENTER_FULLSCREEN, applying fullscreen styles to iframe"),this.isFullscreen=!0,this.iframe&&(this.originalIframeStyles=this.iframe.getAttribute("style")||""),this.iframe&&(this.iframe.style.cssText=`
176
+ position: fixed !important;
177
+ top: 0 !important;
178
+ left: 0 !important;
179
+ right: 0 !important;
180
+ bottom: 0 !important;
181
+ width: 100vw !important;
182
+ height: 100vh !important;
183
+ max-width: 100vw !important;
184
+ max-height: 100vh !important;
185
+ border: none !important;
186
+ border-radius: 0 !important;
187
+ z-index: 999999 !important;
188
+ margin: 0 !important;
189
+ padding: 0 !important;
190
+ opacity: 1 !important;
191
+ `),document.body.style.overflow="hidden",console.log("[Widget] Applied fullscreen styles to iframe")),e.data.type==="USERVIBES_EXIT_FULLSCREEN"&&(console.log("[Widget] Received EXIT_FULLSCREEN, removing fullscreen styles"),this.iframe&&(this.originalIframeStyles?this.iframe.setAttribute("style",this.originalIframeStyles):this.iframe.removeAttribute("style")),document.body.style.overflow="",this.isFullscreen=!1,console.log("[Widget] Removed fullscreen styles")))})}setJWT(e){this.setAttribute("jwt",e)}};typeof window<"u"&&typeof customElements<"u"&&!customElements.get("uservibes-widget")&&customElements.define("uservibes-widget",m);function p(n){let{projectId:s,jwt:e,container:t,theme:r,height:a,mode:l,baseUrl:d}=n;if(!s)return console.error('[UserVibesOS] Missing required "projectId" in config'),null;if(!t)return console.error('[UserVibesOS] Missing required "container" in config'),null;let o=typeof t=="string"?document.querySelector(t):t;if(!o)return console.error(`[UserVibesOS] Container "${t}" not found`),null;let i=document.createElement("uservibes-widget");return i.setAttribute("project",s),e&&i.setAttribute("jwt",e),r&&i.setAttribute("theme",r),a&&i.setAttribute("height",a),l&&i.setAttribute("mode",l),d&&i.setAttribute("base-url",d),o.innerHTML="",o.appendChild(i),{element:i,setJWT:c=>i.setJWT(c),destroy:()=>o.removeChild(i)}}var h={init:p};var u=h;typeof window<"u"&&(window.UserVibesOS=h);export{h as UserVibesOS,m as UserVibesWidget,u as default,p as init};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uservibesos/web-component",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "description": "Feature request widget as a Web Component",
5
5
  "type": "module",
6
6
  "main": "dist/widget.js",
@@ -18,7 +18,17 @@
18
18
  ],
19
19
  "scripts": {
20
20
  "build": "node build.js",
21
- "dev": "node build.js --watch"
21
+ "dev": "node build.js --watch",
22
+ "prepublishOnly": "npm run build",
23
+ "publish:npm": "npm publish --access public"
24
+ },
25
+ "repository": {
26
+ "type": "git",
27
+ "url": "https://github.com/harperaa/uservibesOS.git",
28
+ "directory": "packages/web-component"
29
+ },
30
+ "publishConfig": {
31
+ "access": "public"
22
32
  },
23
33
  "keywords": [
24
34
  "feature-request",
@@ -30,6 +40,6 @@
30
40
  "author": "UserVibesOS",
31
41
  "license": "MIT",
32
42
  "devDependencies": {
33
- "esbuild": "^0.24.0"
43
+ "esbuild": "^0.25.0"
34
44
  }
35
45
  }