@ourroadmaps/web-sdk 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.
@@ -0,0 +1,389 @@
1
+ "use strict";var OurRoadmaps=(()=>{var d=Object.defineProperty;var u=Object.getOwnPropertyDescriptor;var g=Object.getOwnPropertyNames;var f=Object.prototype.hasOwnProperty;var x=(r,e,t)=>e in r?d(r,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):r[e]=t;var m=(r,e)=>{for(var t in e)d(r,t,{get:e[t],enumerable:!0})},v=(r,e,t,s)=>{if(e&&typeof e=="object"||typeof e=="function")for(let o of g(e))!f.call(r,o)&&o!==t&&d(r,o,{get:()=>e[o],enumerable:!(s=u(e,o))||s.enumerable});return r};var y=r=>v(d({},"__esModule",{value:!0}),r);var i=(r,e,t)=>x(r,typeof e!="symbol"?e+"":e,t);var S={};m(S,{Feedback:()=>l});var k={},w=(typeof k<"u","https://api.ourroadmaps.com");async function p(r,e){let t=e.slice(0,100).trim()||"Feedback",s=await fetch(`${w}/v1/ideas`,{method:"POST",headers:{Authorization:`Bearer ${r}`,"Content-Type":"application/json"},body:JSON.stringify({title:t,description:e,source:"feedback-widget"})});if(!s.ok){let o=await s.json().catch(()=>({error:"Request failed"}));throw new Error(o.message||o.error||"Failed to submit feedback")}return s.json()}var n={lightbulb:`<svg viewBox="0 0 24 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
2
+ <path d="M9 21c0 .55.45 1 1 1h4c.55 0 1-.45 1-1v-1H9v1zm3-19C8.14 2 5 5.14 5 9c0 2.38 1.19 4.47 3 5.74V17c0 .55.45 1 1 1h6c.55 0 1-.45 1-1v-2.26c1.81-1.27 3-3.36 3-5.74 0-3.86-3.14-7-7-7zm2.85 11.1l-.85.6V16h-4v-2.3l-.85-.6A4.997 4.997 0 0 1 7 9c0-2.76 2.24-5 5-5s5 2.24 5 5c0 1.63-.8 3.16-2.15 4.1z"/>
3
+ </svg>`,close:`<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" xmlns="http://www.w3.org/2000/svg">
4
+ <path d="M18 6L6 18M6 6l12 12"/>
5
+ </svg>`,send:`<svg viewBox="0 0 24 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
6
+ <path d="M2.01 21L23 12 2.01 3 2 10l15 2-15 2z"/>
7
+ </svg>`,check:`<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" xmlns="http://www.w3.org/2000/svg">
8
+ <path d="M20 6L9 17l-5-5"/>
9
+ </svg>`};var h=`
10
+ :host {
11
+ --or-primary: #2563eb;
12
+ --or-primary-hover: #1d4ed8;
13
+ --or-success: #10b981;
14
+ --or-error: #ef4444;
15
+ --or-text: #1f2937;
16
+ --or-text-muted: #6b7280;
17
+ --or-bg: #ffffff;
18
+ --or-border: #e5e7eb;
19
+ --or-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.1), 0 8px 10px -6px rgba(0, 0, 0, 0.1);
20
+
21
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
22
+ font-size: 14px;
23
+ line-height: 1.5;
24
+ color: var(--or-text);
25
+ }
26
+
27
+ * {
28
+ box-sizing: border-box;
29
+ margin: 0;
30
+ padding: 0;
31
+ }
32
+
33
+ .trigger {
34
+ position: fixed;
35
+ bottom: 20px;
36
+ right: 20px;
37
+ width: 48px;
38
+ height: 48px;
39
+ border-radius: 50%;
40
+ background: var(--or-primary);
41
+ border: none;
42
+ cursor: pointer;
43
+ display: flex;
44
+ align-items: center;
45
+ justify-content: center;
46
+ box-shadow: var(--or-shadow);
47
+ transition: transform 0.15s ease, background-color 0.15s ease;
48
+ z-index: 2147483647;
49
+ }
50
+
51
+ .trigger:hover {
52
+ background: var(--or-primary-hover);
53
+ transform: scale(1.05);
54
+ }
55
+
56
+ .trigger svg {
57
+ width: 24px;
58
+ height: 24px;
59
+ fill: white;
60
+ }
61
+
62
+ .trigger.hidden {
63
+ display: none;
64
+ }
65
+
66
+ .panel {
67
+ position: fixed;
68
+ bottom: 80px;
69
+ right: 20px;
70
+ width: 320px;
71
+ background: var(--or-bg);
72
+ border-radius: 12px;
73
+ box-shadow: var(--or-shadow);
74
+ display: none;
75
+ flex-direction: column;
76
+ overflow: hidden;
77
+ z-index: 2147483647;
78
+ animation: slideIn 0.15s ease;
79
+ }
80
+
81
+ .panel.open {
82
+ display: flex;
83
+ }
84
+
85
+ @keyframes slideIn {
86
+ from {
87
+ opacity: 0;
88
+ transform: translateY(10px) scale(0.95);
89
+ }
90
+ to {
91
+ opacity: 1;
92
+ transform: translateY(0) scale(1);
93
+ }
94
+ }
95
+
96
+ .panel-header {
97
+ display: flex;
98
+ align-items: center;
99
+ justify-content: space-between;
100
+ padding: 12px 16px;
101
+ border-bottom: 1px solid var(--or-border);
102
+ }
103
+
104
+ .panel-title {
105
+ font-weight: 600;
106
+ font-size: 14px;
107
+ }
108
+
109
+ .close-btn {
110
+ width: 28px;
111
+ height: 28px;
112
+ border: none;
113
+ background: transparent;
114
+ cursor: pointer;
115
+ border-radius: 6px;
116
+ display: flex;
117
+ align-items: center;
118
+ justify-content: center;
119
+ color: var(--or-text-muted);
120
+ transition: background-color 0.15s ease;
121
+ }
122
+
123
+ .close-btn:hover {
124
+ background: var(--or-border);
125
+ }
126
+
127
+ .panel-body {
128
+ padding: 16px;
129
+ display: flex;
130
+ flex-direction: column;
131
+ gap: 12px;
132
+ }
133
+
134
+ .error-banner {
135
+ background: #fef2f2;
136
+ border: 1px solid #fecaca;
137
+ border-radius: 8px;
138
+ padding: 12px;
139
+ display: none;
140
+ }
141
+
142
+ .error-banner.visible {
143
+ display: block;
144
+ }
145
+
146
+ .error-text {
147
+ color: var(--or-error);
148
+ font-size: 13px;
149
+ margin-bottom: 8px;
150
+ }
151
+
152
+ .retry-btn {
153
+ background: transparent;
154
+ border: 1px solid var(--or-error);
155
+ color: var(--or-error);
156
+ padding: 4px 12px;
157
+ border-radius: 6px;
158
+ font-size: 13px;
159
+ cursor: pointer;
160
+ transition: background-color 0.15s ease;
161
+ }
162
+
163
+ .retry-btn:hover {
164
+ background: #fef2f2;
165
+ }
166
+
167
+ textarea {
168
+ width: 100%;
169
+ min-height: 80px;
170
+ max-height: 200px;
171
+ padding: 12px;
172
+ border: 1px solid var(--or-border);
173
+ border-radius: 8px;
174
+ resize: none;
175
+ font-family: inherit;
176
+ font-size: 14px;
177
+ line-height: 1.5;
178
+ outline: none;
179
+ transition: border-color 0.15s ease;
180
+ }
181
+
182
+ textarea:focus {
183
+ border-color: var(--or-primary);
184
+ }
185
+
186
+ textarea:disabled {
187
+ background: #f9fafb;
188
+ cursor: not-allowed;
189
+ }
190
+
191
+ .panel-footer {
192
+ display: flex;
193
+ align-items: center;
194
+ justify-content: space-between;
195
+ padding: 12px 16px;
196
+ border-top: 1px solid var(--or-border);
197
+ }
198
+
199
+ .hint {
200
+ font-size: 12px;
201
+ color: var(--or-text-muted);
202
+ }
203
+
204
+ .hint kbd {
205
+ background: #f3f4f6;
206
+ padding: 2px 6px;
207
+ border-radius: 4px;
208
+ font-family: inherit;
209
+ font-size: 11px;
210
+ }
211
+
212
+ .submit-btn {
213
+ display: flex;
214
+ align-items: center;
215
+ gap: 6px;
216
+ padding: 8px 16px;
217
+ background: var(--or-primary);
218
+ color: white;
219
+ border: none;
220
+ border-radius: 8px;
221
+ font-size: 14px;
222
+ font-weight: 500;
223
+ cursor: pointer;
224
+ transition: background-color 0.15s ease;
225
+ }
226
+
227
+ .submit-btn:hover:not(:disabled) {
228
+ background: var(--or-primary-hover);
229
+ }
230
+
231
+ .submit-btn:disabled {
232
+ opacity: 0.6;
233
+ cursor: not-allowed;
234
+ }
235
+
236
+ .submit-btn svg {
237
+ width: 16px;
238
+ height: 16px;
239
+ fill: currentColor;
240
+ }
241
+
242
+ .spinner {
243
+ width: 16px;
244
+ height: 16px;
245
+ border: 2px solid transparent;
246
+ border-top-color: currentColor;
247
+ border-radius: 50%;
248
+ animation: spin 0.6s linear infinite;
249
+ }
250
+
251
+ @keyframes spin {
252
+ to { transform: rotate(360deg); }
253
+ }
254
+
255
+ .success-content {
256
+ display: none;
257
+ flex-direction: column;
258
+ align-items: center;
259
+ justify-content: center;
260
+ padding: 32px 16px;
261
+ text-align: center;
262
+ }
263
+
264
+ .success-content.visible {
265
+ display: flex;
266
+ }
267
+
268
+ .success-icon {
269
+ width: 48px;
270
+ height: 48px;
271
+ background: #d1fae5;
272
+ border-radius: 50%;
273
+ display: flex;
274
+ align-items: center;
275
+ justify-content: center;
276
+ margin-bottom: 12px;
277
+ }
278
+
279
+ .success-icon svg {
280
+ width: 24px;
281
+ height: 24px;
282
+ stroke: var(--or-success);
283
+ fill: none;
284
+ stroke-width: 3;
285
+ }
286
+
287
+ .success-title {
288
+ font-weight: 600;
289
+ font-size: 16px;
290
+ margin-bottom: 4px;
291
+ }
292
+
293
+ .success-subtitle {
294
+ color: var(--or-text-muted);
295
+ font-size: 13px;
296
+ }
297
+
298
+ .form-content {
299
+ display: flex;
300
+ flex-direction: column;
301
+ }
302
+
303
+ .form-content.hidden {
304
+ display: none;
305
+ }
306
+
307
+ /* Mobile bottom sheet */
308
+ @media (max-width: 640px) {
309
+ .panel {
310
+ bottom: 0;
311
+ right: 0;
312
+ left: 0;
313
+ width: 100%;
314
+ border-radius: 16px 16px 0 0;
315
+ animation: slideUp 0.2s ease;
316
+ }
317
+
318
+ @keyframes slideUp {
319
+ from {
320
+ opacity: 0;
321
+ transform: translateY(100%);
322
+ }
323
+ to {
324
+ opacity: 1;
325
+ transform: translateY(0);
326
+ }
327
+ }
328
+
329
+ .panel::before {
330
+ content: '';
331
+ display: block;
332
+ width: 36px;
333
+ height: 4px;
334
+ background: var(--or-border);
335
+ border-radius: 2px;
336
+ margin: 8px auto;
337
+ }
338
+
339
+ .trigger {
340
+ bottom: 16px;
341
+ right: 16px;
342
+ }
343
+ }
344
+ `;var l=class{constructor(e){i(this,"config");i(this,"root",null);i(this,"shadow",null);i(this,"store",{state:"closed",content:"",error:null});this.config=e}mount(){this.root||(this.root=document.createElement("div"),this.root.id="ourroadmaps-widget-root",document.body.appendChild(this.root),this.shadow=this.root.attachShadow({mode:"open"}),this.render(),this.attachEventListeners())}unmount(){this.root&&(this.root.remove(),this.root=null,this.shadow=null)}setState(e){this.store={...this.store,...e},this.render()}render(){if(!this.shadow)return;let{state:e,content:t,error:s}=this.store,o=e!=="closed",a=e==="submitting",c=e==="success",b=e==="error";this.shadow.innerHTML=`
345
+ <style>${h}</style>
346
+
347
+ <button class="trigger ${o?"hidden":""}" aria-label="Open feedback">
348
+ ${n.lightbulb}
349
+ </button>
350
+
351
+ <div class="panel ${o?"open":""}" role="dialog" aria-label="Share feedback">
352
+ <div class="panel-header">
353
+ <span class="panel-title">Share feedback</span>
354
+ <button class="close-btn" aria-label="Close">
355
+ ${n.close}
356
+ </button>
357
+ </div>
358
+
359
+ <div class="success-content ${c?"visible":""}">
360
+ <div class="success-icon">
361
+ ${n.check}
362
+ </div>
363
+ <div class="success-title">Feedback sent!</div>
364
+ <div class="success-subtitle">Thanks for sharing your thoughts.</div>
365
+ </div>
366
+
367
+ <div class="form-content ${c?"hidden":""}">
368
+ <div class="panel-body">
369
+ <div class="error-banner ${b?"visible":""}">
370
+ <div class="error-text">${s||"Couldn't save. Your note is preserved."}</div>
371
+ <button class="retry-btn">Try again</button>
372
+ </div>
373
+ <textarea
374
+ placeholder="What's on your mind?"
375
+ ${a?"disabled":""}
376
+ >${t}</textarea>
377
+ </div>
378
+
379
+ <div class="panel-footer">
380
+ <span class="hint"><kbd>\u2318</kbd> <kbd>\u21B5</kbd> to send</span>
381
+ <button class="submit-btn" ${a||!t.trim()?"disabled":""}>
382
+ ${a?'<span class="spinner"></span>':n.send}
383
+ ${a?"Sending...":"Send"}
384
+ </button>
385
+ </div>
386
+ </div>
387
+ </div>
388
+ `}attachEventListeners(){this.shadow&&(this.shadow.addEventListener("click",e=>{let t=e.target;t.closest(".trigger")?this.open():t.closest(".close-btn")?this.close():t.closest(".submit-btn")?this.submit():t.closest(".retry-btn")&&this.submit()}),this.shadow.addEventListener("input",e=>{let t=e.target;t.tagName==="TEXTAREA"&&this.setState({content:t.value,error:null,state:"open"})}),this.shadow.addEventListener("keydown",e=>{let t=e;t.key==="Escape"?this.close():(t.metaKey||t.ctrlKey)&&t.key==="Enter"&&this.submit()}),document.addEventListener("click",e=>{this.store.state!=="closed"&&this.root&&!this.root.contains(e.target)&&this.close()}))}open(){this.setState({state:"open"}),setTimeout(()=>{this.shadow?.querySelector("textarea")?.focus()},50)}close(){this.setState({state:"closed",error:null})}async submit(){let{content:e}=this.store;if(e.trim()){this.setState({state:"submitting",error:null});try{await p(this.config.apiKey,e),this.setState({state:"success",content:""}),setTimeout(()=>this.close(),1500)}catch(t){let s=t instanceof Error?t.message:"Failed to submit";this.setState({state:"error",error:s})}}}};return y(S);})();
389
+ //# sourceMappingURL=feedback.global.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/feedback/index.ts","../src/feedback/api.ts","../src/feedback/icons.ts","../src/feedback/styles.ts","../src/feedback/Feedback.ts"],"sourcesContent":["export { Feedback } from './Feedback'\nexport type { WidgetConfig, WidgetState, WidgetStore } from './types'\n","import type { ApiError, ApiResponse } from './types'\n\n// Default to production, override with env var for local dev\nconst API_URL = (() => {\n // Vite dev mode\n if (typeof import.meta !== 'undefined' && import.meta.env?.VITE_API_URL) {\n return import.meta.env.VITE_API_URL\n }\n return 'https://api.ourroadmaps.com'\n})()\n\nexport async function submitFeedback(apiKey: string, content: string): Promise<ApiResponse> {\n const title = content.slice(0, 100).trim() || 'Feedback'\n\n const response = await fetch(`${API_URL}/v1/ideas`, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${apiKey}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n title,\n description: content,\n source: 'feedback-widget',\n }),\n })\n\n if (!response.ok) {\n const error: ApiError = await response.json().catch(() => ({\n error: 'Request failed',\n }))\n throw new Error(error.message || error.error || 'Failed to submit feedback')\n }\n\n return response.json()\n}\n","export const ICONS = {\n lightbulb: `<svg viewBox=\"0 0 24 24\" fill=\"currentColor\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M9 21c0 .55.45 1 1 1h4c.55 0 1-.45 1-1v-1H9v1zm3-19C8.14 2 5 5.14 5 9c0 2.38 1.19 4.47 3 5.74V17c0 .55.45 1 1 1h6c.55 0 1-.45 1-1v-2.26c1.81-1.27 3-3.36 3-5.74 0-3.86-3.14-7-7-7zm2.85 11.1l-.85.6V16h-4v-2.3l-.85-.6A4.997 4.997 0 0 1 7 9c0-2.76 2.24-5 5-5s5 2.24 5 5c0 1.63-.8 3.16-2.15 4.1z\"/>\n </svg>`,\n\n close: `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M18 6L6 18M6 6l12 12\"/>\n </svg>`,\n\n send: `<svg viewBox=\"0 0 24 24\" fill=\"currentColor\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M2.01 21L23 12 2.01 3 2 10l15 2-15 2z\"/>\n </svg>`,\n\n check: `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M20 6L9 17l-5-5\"/>\n </svg>`,\n}\n","export const WIDGET_STYLES = `\n :host {\n --or-primary: #2563eb;\n --or-primary-hover: #1d4ed8;\n --or-success: #10b981;\n --or-error: #ef4444;\n --or-text: #1f2937;\n --or-text-muted: #6b7280;\n --or-bg: #ffffff;\n --or-border: #e5e7eb;\n --or-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.1), 0 8px 10px -6px rgba(0, 0, 0, 0.1);\n\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n font-size: 14px;\n line-height: 1.5;\n color: var(--or-text);\n }\n\n * {\n box-sizing: border-box;\n margin: 0;\n padding: 0;\n }\n\n .trigger {\n position: fixed;\n bottom: 20px;\n right: 20px;\n width: 48px;\n height: 48px;\n border-radius: 50%;\n background: var(--or-primary);\n border: none;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n box-shadow: var(--or-shadow);\n transition: transform 0.15s ease, background-color 0.15s ease;\n z-index: 2147483647;\n }\n\n .trigger:hover {\n background: var(--or-primary-hover);\n transform: scale(1.05);\n }\n\n .trigger svg {\n width: 24px;\n height: 24px;\n fill: white;\n }\n\n .trigger.hidden {\n display: none;\n }\n\n .panel {\n position: fixed;\n bottom: 80px;\n right: 20px;\n width: 320px;\n background: var(--or-bg);\n border-radius: 12px;\n box-shadow: var(--or-shadow);\n display: none;\n flex-direction: column;\n overflow: hidden;\n z-index: 2147483647;\n animation: slideIn 0.15s ease;\n }\n\n .panel.open {\n display: flex;\n }\n\n @keyframes slideIn {\n from {\n opacity: 0;\n transform: translateY(10px) scale(0.95);\n }\n to {\n opacity: 1;\n transform: translateY(0) scale(1);\n }\n }\n\n .panel-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 12px 16px;\n border-bottom: 1px solid var(--or-border);\n }\n\n .panel-title {\n font-weight: 600;\n font-size: 14px;\n }\n\n .close-btn {\n width: 28px;\n height: 28px;\n border: none;\n background: transparent;\n cursor: pointer;\n border-radius: 6px;\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--or-text-muted);\n transition: background-color 0.15s ease;\n }\n\n .close-btn:hover {\n background: var(--or-border);\n }\n\n .panel-body {\n padding: 16px;\n display: flex;\n flex-direction: column;\n gap: 12px;\n }\n\n .error-banner {\n background: #fef2f2;\n border: 1px solid #fecaca;\n border-radius: 8px;\n padding: 12px;\n display: none;\n }\n\n .error-banner.visible {\n display: block;\n }\n\n .error-text {\n color: var(--or-error);\n font-size: 13px;\n margin-bottom: 8px;\n }\n\n .retry-btn {\n background: transparent;\n border: 1px solid var(--or-error);\n color: var(--or-error);\n padding: 4px 12px;\n border-radius: 6px;\n font-size: 13px;\n cursor: pointer;\n transition: background-color 0.15s ease;\n }\n\n .retry-btn:hover {\n background: #fef2f2;\n }\n\n textarea {\n width: 100%;\n min-height: 80px;\n max-height: 200px;\n padding: 12px;\n border: 1px solid var(--or-border);\n border-radius: 8px;\n resize: none;\n font-family: inherit;\n font-size: 14px;\n line-height: 1.5;\n outline: none;\n transition: border-color 0.15s ease;\n }\n\n textarea:focus {\n border-color: var(--or-primary);\n }\n\n textarea:disabled {\n background: #f9fafb;\n cursor: not-allowed;\n }\n\n .panel-footer {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 12px 16px;\n border-top: 1px solid var(--or-border);\n }\n\n .hint {\n font-size: 12px;\n color: var(--or-text-muted);\n }\n\n .hint kbd {\n background: #f3f4f6;\n padding: 2px 6px;\n border-radius: 4px;\n font-family: inherit;\n font-size: 11px;\n }\n\n .submit-btn {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 8px 16px;\n background: var(--or-primary);\n color: white;\n border: none;\n border-radius: 8px;\n font-size: 14px;\n font-weight: 500;\n cursor: pointer;\n transition: background-color 0.15s ease;\n }\n\n .submit-btn:hover:not(:disabled) {\n background: var(--or-primary-hover);\n }\n\n .submit-btn:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n }\n\n .submit-btn svg {\n width: 16px;\n height: 16px;\n fill: currentColor;\n }\n\n .spinner {\n width: 16px;\n height: 16px;\n border: 2px solid transparent;\n border-top-color: currentColor;\n border-radius: 50%;\n animation: spin 0.6s linear infinite;\n }\n\n @keyframes spin {\n to { transform: rotate(360deg); }\n }\n\n .success-content {\n display: none;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 32px 16px;\n text-align: center;\n }\n\n .success-content.visible {\n display: flex;\n }\n\n .success-icon {\n width: 48px;\n height: 48px;\n background: #d1fae5;\n border-radius: 50%;\n display: flex;\n align-items: center;\n justify-content: center;\n margin-bottom: 12px;\n }\n\n .success-icon svg {\n width: 24px;\n height: 24px;\n stroke: var(--or-success);\n fill: none;\n stroke-width: 3;\n }\n\n .success-title {\n font-weight: 600;\n font-size: 16px;\n margin-bottom: 4px;\n }\n\n .success-subtitle {\n color: var(--or-text-muted);\n font-size: 13px;\n }\n\n .form-content {\n display: flex;\n flex-direction: column;\n }\n\n .form-content.hidden {\n display: none;\n }\n\n /* Mobile bottom sheet */\n @media (max-width: 640px) {\n .panel {\n bottom: 0;\n right: 0;\n left: 0;\n width: 100%;\n border-radius: 16px 16px 0 0;\n animation: slideUp 0.2s ease;\n }\n\n @keyframes slideUp {\n from {\n opacity: 0;\n transform: translateY(100%);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n }\n\n .panel::before {\n content: '';\n display: block;\n width: 36px;\n height: 4px;\n background: var(--or-border);\n border-radius: 2px;\n margin: 8px auto;\n }\n\n .trigger {\n bottom: 16px;\n right: 16px;\n }\n }\n`\n","import { submitFeedback } from './api'\nimport { ICONS } from './icons'\nimport { WIDGET_STYLES } from './styles'\nimport type { WidgetConfig, WidgetStore } from './types'\n\nexport class Feedback {\n private config: WidgetConfig\n private root: HTMLElement | null = null\n private shadow: ShadowRoot | null = null\n private store: WidgetStore = {\n state: 'closed',\n content: '',\n error: null,\n }\n\n constructor(config: WidgetConfig) {\n this.config = config\n }\n\n mount(): void {\n if (this.root) return\n\n this.root = document.createElement('div')\n this.root.id = 'ourroadmaps-widget-root'\n document.body.appendChild(this.root)\n\n this.shadow = this.root.attachShadow({ mode: 'open' })\n this.render()\n this.attachEventListeners()\n }\n\n unmount(): void {\n if (this.root) {\n this.root.remove()\n this.root = null\n this.shadow = null\n }\n }\n\n private setState(partial: Partial<WidgetStore>): void {\n this.store = { ...this.store, ...partial }\n this.render()\n }\n\n private render(): void {\n if (!this.shadow) return\n\n const { state, content, error } = this.store\n const isOpen = state !== 'closed'\n const isSubmitting = state === 'submitting'\n const isSuccess = state === 'success'\n const isError = state === 'error'\n\n this.shadow.innerHTML = `\n <style>${WIDGET_STYLES}</style>\n\n <button class=\"trigger ${isOpen ? 'hidden' : ''}\" aria-label=\"Open feedback\">\n ${ICONS.lightbulb}\n </button>\n\n <div class=\"panel ${isOpen ? 'open' : ''}\" role=\"dialog\" aria-label=\"Share feedback\">\n <div class=\"panel-header\">\n <span class=\"panel-title\">Share feedback</span>\n <button class=\"close-btn\" aria-label=\"Close\">\n ${ICONS.close}\n </button>\n </div>\n\n <div class=\"success-content ${isSuccess ? 'visible' : ''}\">\n <div class=\"success-icon\">\n ${ICONS.check}\n </div>\n <div class=\"success-title\">Feedback sent!</div>\n <div class=\"success-subtitle\">Thanks for sharing your thoughts.</div>\n </div>\n\n <div class=\"form-content ${isSuccess ? 'hidden' : ''}\">\n <div class=\"panel-body\">\n <div class=\"error-banner ${isError ? 'visible' : ''}\">\n <div class=\"error-text\">${error || \"Couldn't save. Your note is preserved.\"}</div>\n <button class=\"retry-btn\">Try again</button>\n </div>\n <textarea\n placeholder=\"What's on your mind?\"\n ${isSubmitting ? 'disabled' : ''}\n >${content}</textarea>\n </div>\n\n <div class=\"panel-footer\">\n <span class=\"hint\"><kbd>⌘</kbd> <kbd>↵</kbd> to send</span>\n <button class=\"submit-btn\" ${isSubmitting || !content.trim() ? 'disabled' : ''}>\n ${isSubmitting ? '<span class=\"spinner\"></span>' : ICONS.send}\n ${isSubmitting ? 'Sending...' : 'Send'}\n </button>\n </div>\n </div>\n </div>\n `\n }\n\n private attachEventListeners(): void {\n if (!this.shadow) return\n\n this.shadow.addEventListener('click', (e) => {\n const target = e.target as HTMLElement\n\n if (target.closest('.trigger')) {\n this.open()\n } else if (target.closest('.close-btn')) {\n this.close()\n } else if (target.closest('.submit-btn')) {\n this.submit()\n } else if (target.closest('.retry-btn')) {\n this.submit()\n }\n })\n\n this.shadow.addEventListener('input', (e) => {\n const target = e.target as HTMLTextAreaElement\n if (target.tagName === 'TEXTAREA') {\n this.setState({ content: target.value, error: null, state: 'open' })\n }\n })\n\n this.shadow.addEventListener('keydown', (e) => {\n const ke = e as KeyboardEvent\n if (ke.key === 'Escape') {\n this.close()\n } else if ((ke.metaKey || ke.ctrlKey) && ke.key === 'Enter') {\n this.submit()\n }\n })\n\n document.addEventListener('click', (e) => {\n if (this.store.state !== 'closed' && this.root && !this.root.contains(e.target as Node)) {\n this.close()\n }\n })\n }\n\n private open(): void {\n this.setState({ state: 'open' })\n setTimeout(() => {\n const textarea = this.shadow?.querySelector('textarea')\n textarea?.focus()\n }, 50)\n }\n\n private close(): void {\n this.setState({ state: 'closed', error: null })\n }\n\n private async submit(): Promise<void> {\n const { content } = this.store\n if (!content.trim()) return\n\n this.setState({ state: 'submitting', error: null })\n\n try {\n await submitFeedback(this.config.apiKey, content)\n this.setState({ state: 'success', content: '' })\n setTimeout(() => this.close(), 1500)\n } catch (err) {\n const message = err instanceof Error ? err.message : 'Failed to submit'\n this.setState({ state: 'error', error: message })\n }\n }\n}\n"],"mappings":"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","names":["feedback_exports","__export","Feedback","import_meta","API_URL","submitFeedback","apiKey","content","title","response","error","ICONS","WIDGET_STYLES","Feedback","config","__publicField","partial","state","content","error","isOpen","isSubmitting","isSuccess","isError","WIDGET_STYLES","ICONS","target","ke","submitFeedback","err","message"]}
package/dist/index.cjs ADDED
@@ -0,0 +1,64 @@
1
+ 'use strict';
2
+
3
+ var chunkKGQKTDB4_cjs = require('./chunk-KGQKTDB4.cjs');
4
+ var chunkZPPQHIRO_cjs = require('./chunk-ZPPQHIRO.cjs');
5
+ require('./chunk-4DE2IREA.cjs');
6
+
7
+ var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
8
+ // src/feedback/dev-mode.ts
9
+ function isDevMode() {
10
+ if (typeof process !== "undefined" && process.env?.NODE_ENV) {
11
+ return process.env.NODE_ENV !== "production";
12
+ }
13
+ if (typeof ({ url: (typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)) }) !== "undefined" && undefined?.MODE) {
14
+ return undefined.MODE !== "production";
15
+ }
16
+ if (typeof window !== "undefined" && window.location) {
17
+ const hostname = window.location.hostname;
18
+ return hostname === "localhost" || hostname === "127.0.0.1" || hostname.endsWith(".local");
19
+ }
20
+ return false;
21
+ }
22
+
23
+ // src/feedback-init.ts
24
+ var feedbackInstance = null;
25
+ function init(config) {
26
+ if (!config.apiKey) {
27
+ console.error("[OurRoadmaps] Missing required apiKey in config");
28
+ return;
29
+ }
30
+ if (!isDevMode()) {
31
+ console.info("[OurRoadmaps] Widget disabled in production mode");
32
+ return;
33
+ }
34
+ if (feedbackInstance) {
35
+ console.warn("[OurRoadmaps] Widget already initialized");
36
+ return;
37
+ }
38
+ feedbackInstance = new chunkKGQKTDB4_cjs.Feedback(config);
39
+ feedbackInstance.mount();
40
+ }
41
+ function destroy() {
42
+ if (feedbackInstance) {
43
+ feedbackInstance.unmount();
44
+ feedbackInstance = null;
45
+ }
46
+ }
47
+
48
+ // src/index.ts
49
+ if (typeof window !== "undefined") {
50
+ window.OurRoadmaps = { init, destroy, createOverlay: chunkZPPQHIRO_cjs.createOverlay };
51
+ }
52
+
53
+ Object.defineProperty(exports, "Overlay", {
54
+ enumerable: true,
55
+ get: function () { return chunkZPPQHIRO_cjs.Overlay; }
56
+ });
57
+ Object.defineProperty(exports, "createOverlay", {
58
+ enumerable: true,
59
+ get: function () { return chunkZPPQHIRO_cjs.createOverlay; }
60
+ });
61
+ exports.destroy = destroy;
62
+ exports.init = init;
63
+ //# sourceMappingURL=index.cjs.map
64
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/feedback/dev-mode.ts","../src/feedback-init.ts","../src/index.ts"],"names":["Feedback","createOverlay"],"mappings":";;;;;;;;AAAO,SAAS,SAAA,GAAqB;AAEnC,EAAA,IAAI,OAAO,OAAA,KAAY,WAAA,IAAe,OAAA,CAAQ,KAAK,QAAA,EAAU;AAC3D,IAAA,OAAO,OAAA,CAAQ,IAAI,QAAA,KAAa,YAAA;AAAA,EAClC;AAGA,EAAA,IAAI,OAAO,sQAAA,KAAgB,WAAA,IAAe,WAAiB,IAAA,EAAM;AAC/D,IAAA,OAAO,UAAgB,IAAA,KAAS,YAAA;AAAA,EAClC;AAGA,EAAA,IAAI,OAAO,MAAA,KAAW,WAAA,IAAe,MAAA,CAAO,QAAA,EAAU;AACpD,IAAA,MAAM,QAAA,GAAW,OAAO,QAAA,CAAS,QAAA;AACjC,IAAA,OAAO,aAAa,WAAA,IAAe,QAAA,KAAa,WAAA,IAAe,QAAA,CAAS,SAAS,QAAQ,CAAA;AAAA,EAC3F;AAEA,EAAA,OAAO,KAAA;AACT;;;ACbA,IAAI,gBAAA,GAAoC,IAAA;AAEjC,SAAS,KAAK,MAAA,EAA4B;AAC/C,EAAA,IAAI,CAAC,OAAO,MAAA,EAAQ;AAClB,IAAA,OAAA,CAAQ,MAAM,iDAAiD,CAAA;AAC/D,IAAA;AAAA,EACF;AAEA,EAAA,IAAI,CAAC,WAAU,EAAG;AAChB,IAAA,OAAA,CAAQ,KAAK,kDAAkD,CAAA;AAC/D,IAAA;AAAA,EACF;AAEA,EAAA,IAAI,gBAAA,EAAkB;AACpB,IAAA,OAAA,CAAQ,KAAK,0CAA0C,CAAA;AACvD,IAAA;AAAA,EACF;AAEA,EAAA,gBAAA,GAAmB,IAAIA,2BAAS,MAAM,CAAA;AACtC,EAAA,gBAAA,CAAiB,KAAA,EAAM;AACzB;AAEO,SAAS,OAAA,GAAgB;AAC9B,EAAA,IAAI,gBAAA,EAAkB;AACpB,IAAA,gBAAA,CAAiB,OAAA,EAAQ;AACzB,IAAA,gBAAA,GAAmB,IAAA;AAAA,EACrB;AACF;;;ACNA,IAAI,OAAO,WAAW,WAAA,EAAa;AAEhC,EAAC,MAAA,CAAe,WAAA,GAAc,EAAE,IAAA,EAAM,wBAASC,+BAAA,EAAc;AAChE","file":"index.cjs","sourcesContent":["export function isDevMode(): boolean {\n // Check NODE_ENV (works with bundlers)\n if (typeof process !== 'undefined' && process.env?.NODE_ENV) {\n return process.env.NODE_ENV !== 'production'\n }\n\n // Check import.meta.env (Vite)\n if (typeof import.meta !== 'undefined' && import.meta.env?.MODE) {\n return import.meta.env.MODE !== 'production'\n }\n\n // Check hostname (works without bundler)\n if (typeof window !== 'undefined' && window.location) {\n const hostname = window.location.hostname\n return hostname === 'localhost' || hostname === '127.0.0.1' || hostname.endsWith('.local')\n }\n\n return false\n}\n","// Feedback initialization - extracted for lazy loading\nimport { isDevMode } from './feedback/dev-mode'\nimport { Feedback } from './feedback/Feedback'\nimport type { WidgetConfig } from './feedback/types'\n\nlet feedbackInstance: Feedback | null = null\n\nexport function init(config: WidgetConfig): void {\n if (!config.apiKey) {\n console.error('[OurRoadmaps] Missing required apiKey in config')\n return\n }\n\n if (!isDevMode()) {\n console.info('[OurRoadmaps] Widget disabled in production mode')\n return\n }\n\n if (feedbackInstance) {\n console.warn('[OurRoadmaps] Widget already initialized')\n return\n }\n\n feedbackInstance = new Feedback(config)\n feedbackInstance.mount()\n}\n\nexport function destroy(): void {\n if (feedbackInstance) {\n feedbackInstance.unmount()\n feedbackInstance = null\n }\n}\n","// Feedback module\n\nexport type { WidgetConfig } from './feedback/types'\nexport { destroy, init } from './feedback-init'\nexport type {\n Action,\n Anchor,\n Annotation,\n AnnotationsConfig,\n AnnotationsController,\n CursorConfig,\n CursorController,\n OverlayController,\n OverlayError,\n OverlayOptions,\n OverlayScript,\n Target,\n ValidationResult,\n} from './overlay'\n// Overlay module\nexport { createOverlay, Overlay } from './overlay'\n\n// For UMD bundle - expose on window\nimport { destroy, init } from './feedback-init'\nimport { createOverlay } from './overlay'\n\nif (typeof window !== 'undefined') {\n // biome-ignore lint/suspicious/noExplicitAny: Required for UMD global assignment\n ;(window as any).OurRoadmaps = { init, destroy, createOverlay }\n}\n"]}
@@ -0,0 +1,7 @@
1
+ import { W as WidgetConfig } from './types-CEewztln.cjs';
2
+ export { Action, Anchor, Annotation, AnnotationsConfig, AnnotationsController, CursorConfig, CursorController, Overlay, OverlayController, OverlayError, OverlayOptions, OverlayScript, Target, ValidationResult, createOverlay } from './overlay/index.cjs';
3
+
4
+ declare function init(config: WidgetConfig): void;
5
+ declare function destroy(): void;
6
+
7
+ export { WidgetConfig, destroy, init };
@@ -0,0 +1,7 @@
1
+ import { W as WidgetConfig } from './types-CEewztln.js';
2
+ export { Action, Anchor, Annotation, AnnotationsConfig, AnnotationsController, CursorConfig, CursorController, Overlay, OverlayController, OverlayError, OverlayOptions, OverlayScript, Target, ValidationResult, createOverlay } from './overlay/index.js';
3
+
4
+ declare function init(config: WidgetConfig): void;
5
+ declare function destroy(): void;
6
+
7
+ export { WidgetConfig, destroy, init };
package/dist/index.js ADDED
@@ -0,0 +1,53 @@
1
+ import { Feedback } from './chunk-MKPDPQXT.js';
2
+ import { createOverlay } from './chunk-XFAAEDJK.js';
3
+ export { Overlay, createOverlay } from './chunk-XFAAEDJK.js';
4
+ import './chunk-V6TY7KAL.js';
5
+
6
+ // src/feedback/dev-mode.ts
7
+ function isDevMode() {
8
+ if (typeof process !== "undefined" && process.env?.NODE_ENV) {
9
+ return process.env.NODE_ENV !== "production";
10
+ }
11
+ if (typeof import.meta !== "undefined" && import.meta.env?.MODE) {
12
+ return import.meta.env.MODE !== "production";
13
+ }
14
+ if (typeof window !== "undefined" && window.location) {
15
+ const hostname = window.location.hostname;
16
+ return hostname === "localhost" || hostname === "127.0.0.1" || hostname.endsWith(".local");
17
+ }
18
+ return false;
19
+ }
20
+
21
+ // src/feedback-init.ts
22
+ var feedbackInstance = null;
23
+ function init(config) {
24
+ if (!config.apiKey) {
25
+ console.error("[OurRoadmaps] Missing required apiKey in config");
26
+ return;
27
+ }
28
+ if (!isDevMode()) {
29
+ console.info("[OurRoadmaps] Widget disabled in production mode");
30
+ return;
31
+ }
32
+ if (feedbackInstance) {
33
+ console.warn("[OurRoadmaps] Widget already initialized");
34
+ return;
35
+ }
36
+ feedbackInstance = new Feedback(config);
37
+ feedbackInstance.mount();
38
+ }
39
+ function destroy() {
40
+ if (feedbackInstance) {
41
+ feedbackInstance.unmount();
42
+ feedbackInstance = null;
43
+ }
44
+ }
45
+
46
+ // src/index.ts
47
+ if (typeof window !== "undefined") {
48
+ window.OurRoadmaps = { init, destroy, createOverlay };
49
+ }
50
+
51
+ export { destroy, init };
52
+ //# sourceMappingURL=index.js.map
53
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/feedback/dev-mode.ts","../src/feedback-init.ts","../src/index.ts"],"names":[],"mappings":";;;;;;AAAO,SAAS,SAAA,GAAqB;AAEnC,EAAA,IAAI,OAAO,OAAA,KAAY,WAAA,IAAe,OAAA,CAAQ,KAAK,QAAA,EAAU;AAC3D,IAAA,OAAO,OAAA,CAAQ,IAAI,QAAA,KAAa,YAAA;AAAA,EAClC;AAGA,EAAA,IAAI,OAAO,MAAA,CAAA,IAAA,KAAgB,WAAA,IAAe,MAAA,CAAA,IAAA,CAAY,KAAK,IAAA,EAAM;AAC/D,IAAA,OAAO,MAAA,CAAA,IAAA,CAAY,IAAI,IAAA,KAAS,YAAA;AAAA,EAClC;AAGA,EAAA,IAAI,OAAO,MAAA,KAAW,WAAA,IAAe,MAAA,CAAO,QAAA,EAAU;AACpD,IAAA,MAAM,QAAA,GAAW,OAAO,QAAA,CAAS,QAAA;AACjC,IAAA,OAAO,aAAa,WAAA,IAAe,QAAA,KAAa,WAAA,IAAe,QAAA,CAAS,SAAS,QAAQ,CAAA;AAAA,EAC3F;AAEA,EAAA,OAAO,KAAA;AACT;;;ACbA,IAAI,gBAAA,GAAoC,IAAA;AAEjC,SAAS,KAAK,MAAA,EAA4B;AAC/C,EAAA,IAAI,CAAC,OAAO,MAAA,EAAQ;AAClB,IAAA,OAAA,CAAQ,MAAM,iDAAiD,CAAA;AAC/D,IAAA;AAAA,EACF;AAEA,EAAA,IAAI,CAAC,WAAU,EAAG;AAChB,IAAA,OAAA,CAAQ,KAAK,kDAAkD,CAAA;AAC/D,IAAA;AAAA,EACF;AAEA,EAAA,IAAI,gBAAA,EAAkB;AACpB,IAAA,OAAA,CAAQ,KAAK,0CAA0C,CAAA;AACvD,IAAA;AAAA,EACF;AAEA,EAAA,gBAAA,GAAmB,IAAI,SAAS,MAAM,CAAA;AACtC,EAAA,gBAAA,CAAiB,KAAA,EAAM;AACzB;AAEO,SAAS,OAAA,GAAgB;AAC9B,EAAA,IAAI,gBAAA,EAAkB;AACpB,IAAA,gBAAA,CAAiB,OAAA,EAAQ;AACzB,IAAA,gBAAA,GAAmB,IAAA;AAAA,EACrB;AACF;;;ACNA,IAAI,OAAO,WAAW,WAAA,EAAa;AAEhC,EAAC,MAAA,CAAe,WAAA,GAAc,EAAE,IAAA,EAAM,SAAS,aAAA,EAAc;AAChE","file":"index.js","sourcesContent":["export function isDevMode(): boolean {\n // Check NODE_ENV (works with bundlers)\n if (typeof process !== 'undefined' && process.env?.NODE_ENV) {\n return process.env.NODE_ENV !== 'production'\n }\n\n // Check import.meta.env (Vite)\n if (typeof import.meta !== 'undefined' && import.meta.env?.MODE) {\n return import.meta.env.MODE !== 'production'\n }\n\n // Check hostname (works without bundler)\n if (typeof window !== 'undefined' && window.location) {\n const hostname = window.location.hostname\n return hostname === 'localhost' || hostname === '127.0.0.1' || hostname.endsWith('.local')\n }\n\n return false\n}\n","// Feedback initialization - extracted for lazy loading\nimport { isDevMode } from './feedback/dev-mode'\nimport { Feedback } from './feedback/Feedback'\nimport type { WidgetConfig } from './feedback/types'\n\nlet feedbackInstance: Feedback | null = null\n\nexport function init(config: WidgetConfig): void {\n if (!config.apiKey) {\n console.error('[OurRoadmaps] Missing required apiKey in config')\n return\n }\n\n if (!isDevMode()) {\n console.info('[OurRoadmaps] Widget disabled in production mode')\n return\n }\n\n if (feedbackInstance) {\n console.warn('[OurRoadmaps] Widget already initialized')\n return\n }\n\n feedbackInstance = new Feedback(config)\n feedbackInstance.mount()\n}\n\nexport function destroy(): void {\n if (feedbackInstance) {\n feedbackInstance.unmount()\n feedbackInstance = null\n }\n}\n","// Feedback module\n\nexport type { WidgetConfig } from './feedback/types'\nexport { destroy, init } from './feedback-init'\nexport type {\n Action,\n Anchor,\n Annotation,\n AnnotationsConfig,\n AnnotationsController,\n CursorConfig,\n CursorController,\n OverlayController,\n OverlayError,\n OverlayOptions,\n OverlayScript,\n Target,\n ValidationResult,\n} from './overlay'\n// Overlay module\nexport { createOverlay, Overlay } from './overlay'\n\n// For UMD bundle - expose on window\nimport { destroy, init } from './feedback-init'\nimport { createOverlay } from './overlay'\n\nif (typeof window !== 'undefined') {\n // biome-ignore lint/suspicious/noExplicitAny: Required for UMD global assignment\n ;(window as any).OurRoadmaps = { init, destroy, createOverlay }\n}\n"]}
@@ -0,0 +1,17 @@
1
+ 'use strict';
2
+
3
+ var chunkZPPQHIRO_cjs = require('../chunk-ZPPQHIRO.cjs');
4
+ require('../chunk-4DE2IREA.cjs');
5
+
6
+
7
+
8
+ Object.defineProperty(exports, "Overlay", {
9
+ enumerable: true,
10
+ get: function () { return chunkZPPQHIRO_cjs.Overlay; }
11
+ });
12
+ Object.defineProperty(exports, "createOverlay", {
13
+ enumerable: true,
14
+ get: function () { return chunkZPPQHIRO_cjs.createOverlay; }
15
+ });
16
+ //# sourceMappingURL=index.cjs.map
17
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"index.cjs"}