@voyage-sdk/core 0.1.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 ADDED
@@ -0,0 +1,128 @@
1
+ # @voyage/sdk
2
+
3
+ Feedback widget SDK for collecting user feedback. Help your users send feedback directly to the maker.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @voyage-sdk/core
9
+ ```
10
+
11
+ Or via CDN:
12
+
13
+ ```html
14
+ <script src="https://unpkg.com/@voyage-sdk/core"></script>
15
+ <link rel="stylesheet" href="https://unpkg.com/@voyage-sdk/core/dist/style.css" />
16
+ ```
17
+
18
+ ## Quick Start
19
+
20
+ ### ES Module
21
+
22
+ ```javascript
23
+ import Voyage from '@voyage/sdk'
24
+ import '@voyage/sdk/style.css'
25
+
26
+ Voyage.init({
27
+ sdkKey: 'your-sdk-key-here'
28
+ })
29
+ ```
30
+
31
+ ### CDN / Script Tag
32
+
33
+ ```html
34
+ <script src="https://unpkg.com/@voyage-sdk/core"></script>
35
+ <link rel="stylesheet" href="https://unpkg.com/@voyage-sdk/core/dist/style.css" />
36
+ <script>
37
+ Voyage.init({
38
+ sdkKey: 'your-sdk-key-here'
39
+ })
40
+ </script>
41
+ ```
42
+
43
+ ## Configuration
44
+
45
+ ```javascript
46
+ Voyage.init({
47
+ // Required: Your SDK key from the dashboard
48
+ sdkKey: 'your-sdk-key-here',
49
+
50
+ // Optional: Override theme settings
51
+ theme: {
52
+ primaryColor: '#7C3AED',
53
+ position: 'bottom-right' // or 'bottom-left'
54
+ },
55
+
56
+ // Optional: Control where the widget appears
57
+ include: ['/dashboard/*', '/app/*'], // Only show on these paths
58
+ exclude: ['/admin/*', '/login'] // Hide on these paths
59
+ })
60
+ ```
61
+
62
+ ## API
63
+
64
+ ### `Voyage.init(config)`
65
+
66
+ Initialize the SDK with your configuration.
67
+
68
+ ### `Voyage.show()`
69
+
70
+ Show the feedback widget button.
71
+
72
+ ### `Voyage.hide()`
73
+
74
+ Hide the feedback widget button.
75
+
76
+ ### `Voyage.open()`
77
+
78
+ Open the feedback modal.
79
+
80
+ ### `Voyage.close()`
81
+
82
+ Close the feedback modal.
83
+
84
+ ### `Voyage.isOpen()`
85
+
86
+ Returns `true` if the modal is currently open.
87
+
88
+ ### `Voyage.isVisible()`
89
+
90
+ Returns `true` if the widget button is visible.
91
+
92
+ ### `Voyage.destroy()`
93
+
94
+ Remove the widget from the page and clean up.
95
+
96
+ ### `Voyage.refreshConfig()`
97
+
98
+ Fetch the latest widget configuration from the server.
99
+
100
+ ## Path Matching
101
+
102
+ Use `include` and `exclude` to control where the widget appears:
103
+
104
+ ```javascript
105
+ Voyage.init({
106
+ sdkKey: 'your-sdk-key',
107
+
108
+ // Widget only appears on paths starting with /app/
109
+ include: ['/app/*'],
110
+
111
+ // Or exclude specific paths
112
+ exclude: ['/admin/*', '/checkout']
113
+ })
114
+ ```
115
+
116
+ - `include` takes priority over `exclude`
117
+ - Use `/*` for wildcard matching (e.g., `/admin/*` matches `/admin/users`)
118
+ - Exact paths match exactly (e.g., `/login` only matches `/login`)
119
+
120
+ ## Get Your SDK Key
121
+
122
+ 1. Sign up at [voyage.io](https://voyage.io)
123
+ 2. Create a new project
124
+ 3. Copy your SDK key from the project settings
125
+
126
+ ## License
127
+
128
+ MIT
package/dist/index.cjs ADDED
@@ -0,0 +1,42 @@
1
+ 'use strict';Object.defineProperty(exports,'__esModule',{value:true});var preact=require('preact'),hooks=require('preact/hooks'),jsxRuntime=require('preact/jsx-runtime');function ce(){return jsxRuntime.jsx("svg",{width:"20",height:"20",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",children:jsxRuntime.jsx("path",{d:"M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"})})}function X({onClick:r,label:o,position:e,primaryColor:n}){let[c,l]=hooks.useState(false),[u,s]=hooks.useState(false),p=e==="bottom-right"?{right:"24px"}:{left:"24px"},d=()=>u?"scale(0.95)":c?"scale(1.02) translateY(-2px)":"scale(1)";return jsxRuntime.jsxs("button",{onClick:r,onMouseEnter:()=>l(true),onMouseLeave:()=>{l(false),s(false);},onMouseDown:()=>s(true),onMouseUp:()=>s(false),style:{position:"fixed",bottom:"24px",...p,display:"flex",alignItems:"center",gap:"10px",padding:"14px 24px",border:"none",borderRadius:"50px",cursor:"pointer",fontFamily:"'Quicksand', 'Nunito', system-ui, sans-serif",fontSize:"15px",fontWeight:600,color:"#fff",zIndex:9998,backgroundColor:n,boxShadow:c?`0 8px 32px ${n}60, 0 0 20px ${n}40`:`0 4px 20px ${n}50, 0 0 10px ${n}30`,transform:d(),transition:"all 0.2s cubic-bezier(0.34, 1.56, 0.64, 1)"},children:[jsxRuntime.jsx(ce,{}),jsxRuntime.jsx("span",{children:o})]})}function ge({size:r=24}){return jsxRuntime.jsx("svg",{width:r,height:r,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",children:jsxRuntime.jsx("path",{d:"M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"})})}function fe(){return jsxRuntime.jsxs("svg",{width:"18",height:"18",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",children:[jsxRuntime.jsx("path",{d:"M8 2l1.88 1.88M14.12 3.88L16 2M9 7.13v-1a3.003 3.003 0 116 0v1"}),jsxRuntime.jsx("path",{d:"M12 20c-3.3 0-6-2.7-6-6v-3a4 4 0 014-4h4a4 4 0 014 4v3c0 3.3-2.7 6-6 6"}),jsxRuntime.jsx("path",{d:"M12 20v-9M6.53 9C4.6 8.8 3 7.1 3 5M6 13H2M6 17l-4 1M17.47 9c1.93-.2 3.53-1.9 3.53-4M18 13h4M18 17l4 1"})]})}function he(){return jsxRuntime.jsxs("svg",{width:"18",height:"18",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",children:[jsxRuntime.jsx("path",{d:"M15 14c.2-1 .7-1.7 1.5-2.5 1-.9 1.5-2.2 1.5-3.5A6 6 0 006 8c0 1 .2 2.2 1.5 3.5.7.7 1.3 1.5 1.5 2.5"}),jsxRuntime.jsx("path",{d:"M9 18h6M10 22h4"})]})}function me(){return jsxRuntime.jsxs("svg",{width:"18",height:"18",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",children:[jsxRuntime.jsx("rect",{x:"3",y:"3",width:"18",height:"18",rx:"2",ry:"2"}),jsxRuntime.jsx("circle",{cx:"8.5",cy:"8.5",r:"1.5"}),jsxRuntime.jsx("polyline",{points:"21 15 16 10 5 21"})]})}function N(){return jsxRuntime.jsxs("svg",{width:"20",height:"20",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",children:[jsxRuntime.jsx("line",{x1:"18",y1:"6",x2:"6",y2:"18"}),jsxRuntime.jsx("line",{x1:"6",y1:"6",x2:"18",y2:"18"})]})}function be(){return jsxRuntime.jsx("svg",{width:"24",height:"24",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",children:jsxRuntime.jsx("polyline",{points:"20 6 9 17 4 12"})})}var i={bgLight:"#F8FAFC",bgCard:"rgba(255, 255, 255, 0.98)",border:"rgba(0, 0, 0, 0.08)",text:"#1F2937",textMuted:"#6B7280",error:"#EF4444",success:"#22C55E"};function z({config:r,metadata:o,onClose:e,onSubmit:n,onUpload:c}){let[l,u]=hooks.useState(null),[s,p]=hooks.useState(""),[d,y]=hooks.useState(""),[k,x]=hooks.useState(null),[w,v]=hooks.useState(null),[j,R]=hooks.useState(false),[W,B]=hooks.useState(false),[$,m]=hooks.useState(null),[re,ne]=hooks.useState(false),P=hooks.useRef(null),{i18n:C,formFields:J,theme:ie}=r,S=ie.primaryColor||"#0ea5e9",H=400,ae=l==="bug"?C.bugPlaceholder||C.placeholder:C.featurePlaceholder||C.placeholder,se=async f=>{let F=f.target.files?.[0];if(F){if(!F.type.startsWith("image/")){m("Only image files are allowed");return}if(F.size>10*1024*1024){m("Image must be under 10MB");return}B(true),m(null);try{let M=await ye(F,{maxWidth:1920,maxHeight:1080,quality:.8}),D=new FileReader;D.onload=()=>v(D.result),D.readAsDataURL(M);let U=await c(M);B(!1),U?x(U):(m("Failed to upload image"),v(null));}catch(M){console.error("[Voyage] Image processing error:",M),B(false),m("Failed to process image");}}},le=()=>{x(null),v(null),P.current&&(P.current.value="");},de=async()=>{if(!l){m("Please select a type");return}if(!s.trim()){m("Please enter your feedback");return}if(J.email.required&&!d.trim()){m("Please enter your email");return}R(true),m(null);let f={label:l,text:s.trim(),email:d.trim()||void 0,imageUrl:k||void 0,metadata:o},h=await n(f);R(false),h?ne(true):m("Failed to submit. Please try again.");},g=(()=>{let f=`${S}15`;return {overlay:{position:"fixed",top:0,left:0,right:0,bottom:0,backgroundColor:"rgba(0, 0, 0, 0.4)",backdropFilter:"blur(4px)",display:"flex",alignItems:"center",justifyContent:"center",zIndex:9999,padding:"20px"},modal:{backgroundColor:i.bgCard,backdropFilter:"blur(20px)",borderRadius:"20px",width:"100%",maxWidth:"580px",maxHeight:"90vh",overflowY:"auto",position:"relative",fontFamily:"'Quicksand', 'Nunito', system-ui, sans-serif",border:`1px solid ${i.border}`,boxShadow:"0 25px 50px -12px rgba(0, 0, 0, 0.15)"},colorBar:{height:"4px",backgroundColor:S},header:{display:"flex",alignItems:"center",justifyContent:"space-between",padding:"14px 20px",borderBottom:`1px solid ${i.border}`,backgroundColor:i.bgLight},headerIcon:{width:"32px",height:"32px",borderRadius:"10px",backgroundColor:S,display:"flex",alignItems:"center",justifyContent:"center",color:"#fff"},typeButton:h=>({display:"flex",alignItems:"center",justifyContent:"center",gap:"6px",padding:"10px 12px",border:h?`2px solid ${S}`:`1px solid ${i.border}`,borderRadius:"10px",backgroundColor:h?f:i.bgLight,cursor:"pointer",fontWeight:600,fontSize:"13px",color:h?i.text:i.textMuted,transition:"all 0.2s"}),submitButton:h=>({width:"100%",padding:"12px",backgroundColor:h?i.bgLight:S,color:h?i.textMuted:"#fff",border:"none",borderRadius:"12px",fontSize:"15px",fontWeight:700,cursor:h?"not-allowed":"pointer",fontFamily:"inherit",transition:"all 0.2s",boxShadow:h?"none":`0 4px 15px ${S}40`}),primaryButton:{width:"100%",padding:"16px",backgroundColor:S,color:"#fff",border:"none",borderRadius:"12px",fontSize:"15px",fontWeight:700,cursor:"pointer",fontFamily:"inherit",marginTop:"8px"}}})();return re?jsxRuntime.jsx("div",{style:g.overlay,children:jsxRuntime.jsx("div",{style:g.modal,children:jsxRuntime.jsxs("div",{style:Ee,children:[jsxRuntime.jsx("div",{style:Ve,children:jsxRuntime.jsx(be,{})}),jsxRuntime.jsx("h3",{style:Te,children:C.successMessage}),jsxRuntime.jsx("p",{style:Oe,children:"Your voice shapes what we build next."}),jsxRuntime.jsx("button",{onClick:e,style:g.primaryButton,children:"Done"})]})})}):jsxRuntime.jsx("div",{style:g.overlay,onClick:e,children:jsxRuntime.jsxs("div",{style:g.modal,onClick:f=>f.stopPropagation(),children:[jsxRuntime.jsxs("div",{style:g.header,children:[jsxRuntime.jsxs("div",{style:xe,children:[jsxRuntime.jsx("div",{style:g.headerIcon,children:jsxRuntime.jsx(ge,{size:20})}),jsxRuntime.jsx("span",{style:Se,children:"Help Us Improve"})]}),jsxRuntime.jsx("button",{onClick:e,style:Ce,children:jsxRuntime.jsx(N,{})})]}),jsxRuntime.jsx("div",{style:g.colorBar}),jsxRuntime.jsxs("div",{style:ve,children:[jsxRuntime.jsxs("div",{style:_,children:[jsxRuntime.jsx("label",{style:L,children:"Feedback Type"}),jsxRuntime.jsxs("div",{style:ke,children:[jsxRuntime.jsxs("button",{onClick:()=>u("bug"),style:g.typeButton(l==="bug"),children:[jsxRuntime.jsx("span",{style:q(l==="bug","bug"),children:jsxRuntime.jsx(fe,{})}),jsxRuntime.jsx("span",{children:"Bug"})]}),jsxRuntime.jsxs("button",{onClick:()=>u("feature"),style:g.typeButton(l==="feature"),children:[jsxRuntime.jsx("span",{style:q(l==="feature","feature"),children:jsxRuntime.jsx(he,{})}),jsxRuntime.jsx("span",{children:"Feature"})]})]})]}),jsxRuntime.jsxs("div",{style:we,children:[jsxRuntime.jsxs("div",{style:Pe,children:[jsxRuntime.jsx("label",{style:L,children:"Description"}),jsxRuntime.jsxs("div",{style:Le,children:[jsxRuntime.jsx("textarea",{placeholder:ae,value:s,maxLength:H,onInput:f=>p(f.target.value),style:je}),jsxRuntime.jsxs("span",{style:We,children:[s.length,"/",H]})]})]}),jsxRuntime.jsxs("div",{style:Fe,children:[jsxRuntime.jsx("label",{style:L,children:"Screenshot"}),w?jsxRuntime.jsxs("div",{style:Je,children:[jsxRuntime.jsx("img",{src:w,alt:"Preview",style:Ie}),jsxRuntime.jsx("button",{onClick:le,style:De,children:jsxRuntime.jsx(N,{})}),W&&jsxRuntime.jsx("div",{style:Xe,children:"..."})]}):jsxRuntime.jsxs("button",{onClick:()=>P.current?.click(),style:Me,children:[jsxRuntime.jsx(me,{}),jsxRuntime.jsx("span",{style:{fontSize:"11px"},children:"Upload"})]}),jsxRuntime.jsx("input",{ref:P,type:"file",accept:"image/*",onChange:se,style:{display:"none"}})]})]}),J.email.enabled&&jsxRuntime.jsxs("div",{style:_,children:[jsxRuntime.jsxs("label",{style:L,children:["Email ",J.email.required?"":"(optional)"]}),jsxRuntime.jsx("input",{type:"email",placeholder:"your@email.com",value:d,onInput:f=>y(f.target.value),style:Be})]}),$&&jsxRuntime.jsx("p",{style:ze,children:$}),jsxRuntime.jsx("button",{onClick:de,disabled:j||W,style:g.submitButton(j||W),children:j?jsxRuntime.jsxs("span",{style:Re,children:[jsxRuntime.jsx("span",{className:"voyage-spinner"}),"Sending..."]}):C.submitLabel})]})]})})}async function ye(r,o){let{maxWidth:e,maxHeight:n,quality:c}=o;return new Promise((l,u)=>{let s=new Image;s.onload=()=>{let{width:p,height:d}=s;if(p>e||d>n){let x=Math.min(e/p,n/d);p=Math.round(p*x),d=Math.round(d*x);}let y=document.createElement("canvas");y.width=p,y.height=d;let k=y.getContext("2d");if(!k){u(new Error("Failed to get canvas context"));return}k.drawImage(s,0,0,p,d),y.toBlob(x=>{if(!x){u(new Error("Failed to convert image"));return}let w=Date.now(),v=new File([x],`image_${w}.webp`,{type:"image/webp"});l(v);},"image/webp",c);},s.onerror=()=>u(new Error("Failed to load image")),s.src=URL.createObjectURL(r);})}var xe={display:"flex",alignItems:"center",gap:"12px"},Se={fontSize:"16px",fontWeight:700,color:i.text},Ce={background:"transparent",border:"none",color:i.textMuted,cursor:"pointer",padding:"8px",borderRadius:"8px",display:"flex",alignItems:"center",justifyContent:"center",transition:"all 0.2s"},ve={padding:"16px 20px"},_={marginBottom:"14px"},L={display:"block",fontSize:"13px",fontWeight:600,color:i.text,marginBottom:"8px"},ke={display:"grid",gridTemplateColumns:"1fr 1fr",gap:"12px"},we={display:"flex",gap:"14px",marginBottom:"14px"},Pe={flex:1,display:"flex",flexDirection:"column"},Fe={width:"120px",flexShrink:0,display:"flex",flexDirection:"column"},Me={width:"100%",aspectRatio:"1",border:`2px dashed ${i.border}`,borderRadius:"10px",backgroundColor:"transparent",cursor:"pointer",display:"flex",flexDirection:"column",alignItems:"center",justifyContent:"center",gap:"4px",color:i.textMuted,fontSize:"12px",transition:"all 0.2s"},Ie={width:"100%",aspectRatio:"1",objectFit:"cover",borderRadius:"10px"},q=(r,o)=>({display:"flex",alignItems:"center",justifyContent:"center",color:r?o==="bug"?i.error:"#F59E0B":i.textMuted}),Le={position:"relative"},je={width:"100%",minHeight:"120px",padding:"14px",border:`1px solid ${i.border}`,borderRadius:"12px",fontSize:"14px",resize:"vertical",boxSizing:"border-box",backgroundColor:i.bgLight,color:i.text,fontFamily:"inherit",outline:"none"},We={position:"absolute",bottom:"12px",right:"14px",fontSize:"12px",color:i.textMuted},Be={width:"100%",padding:"10px 12px",border:`1px solid ${i.border}`,borderRadius:"10px",fontSize:"13px",boxSizing:"border-box",backgroundColor:i.bgLight,color:i.text,fontFamily:"inherit",outline:"none"},Je={position:"relative",display:"inline-block",borderRadius:"12px",overflow:"hidden"},De={position:"absolute",top:"8px",right:"8px",width:"28px",height:"28px",borderRadius:"50%",backgroundColor:"rgba(0, 0, 0, 0.7)",color:"#fff",border:"none",cursor:"pointer",display:"flex",alignItems:"center",justifyContent:"center"},Xe={position:"absolute",inset:0,backgroundColor:"rgba(0, 0, 0, 0.7)",display:"flex",alignItems:"center",justifyContent:"center",color:"#fff",fontSize:"14px",borderRadius:"12px"},ze={color:i.error,fontSize:"14px",marginBottom:"16px",padding:"12px",backgroundColor:"rgba(239, 68, 68, 0.1)",borderRadius:"8px",border:"1px solid rgba(239, 68, 68, 0.3)"},Ee={padding:"48px 24px",textAlign:"center"},Ve={width:"64px",height:"64px",borderRadius:"50%",backgroundColor:"rgba(34, 197, 94, 0.15)",color:i.success,display:"flex",alignItems:"center",justifyContent:"center",margin:"0 auto 20px"},Te={fontSize:"20px",fontWeight:700,color:i.text,marginBottom:"8px"},Oe={fontSize:"14px",color:i.textMuted,marginBottom:"24px"},Re={display:"flex",alignItems:"center",justifyContent:"center",gap:"8px"};function E({config:r,onSubmit:o,onUpload:e,getMetadata:n,visible:c,defaultOpen:l=false,onOpenChange:u}){let[s,p]=hooks.useState(l);if(hooks.useEffect(()=>{u?.(s);},[s,u]),!c)return null;let{theme:d,i18n:y}=r;return jsxRuntime.jsxs(jsxRuntime.Fragment,{children:[!s&&jsxRuntime.jsx(X,{onClick:()=>p(true),label:y.buttonLabel,position:d.position,primaryColor:d.primaryColor}),s&&jsxRuntime.jsx(z,{config:r,metadata:n(),onClose:()=>p(false),onSubmit:o,onUpload:e})]})}var Q=()=>{if(typeof document>"u"||document.getElementById("voyage-sdk-styles"))return;let r=document.createElement("style");r.id="voyage-sdk-styles",r.textContent=`
2
+ /* Voyage SDK Styles */
3
+
4
+ /* Spinner Animation */
5
+ @keyframes voyage-spin {
6
+ from { transform: rotate(0deg); }
7
+ to { transform: rotate(360deg); }
8
+ }
9
+
10
+ #voyage-widget-container * {
11
+ box-sizing: border-box;
12
+ }
13
+
14
+ #voyage-widget-container button {
15
+ font-family: 'Quicksand', 'Nunito', system-ui, sans-serif;
16
+ }
17
+
18
+ #voyage-widget-container textarea:focus,
19
+ #voyage-widget-container input:focus {
20
+ border-color: rgba(124, 58, 237, 0.5);
21
+ box-shadow: 0 0 0 3px rgba(124, 58, 237, 0.1);
22
+ }
23
+
24
+ #voyage-widget-container button:focus {
25
+ outline: none;
26
+ }
27
+
28
+ /* Spinner */
29
+ .voyage-spinner {
30
+ width: 16px;
31
+ height: 16px;
32
+ border: 2px solid rgba(255,255,255,0.3);
33
+ border-top-color: #fff;
34
+ border-radius: 50%;
35
+ animation: voyage-spin 0.8s linear infinite;
36
+ }
37
+
38
+ /* Load Quicksand font if not present */
39
+ @import url('https://fonts.googleapis.com/css2?family=Quicksand:wght@400;500;600;700&display=swap');
40
+ `,document.head.appendChild(r);};var V="https://rxflzurkkuqgopjkshjh.supabase.co/functions/v1",G="voyage_config";async function T(r){let o=Y(r);if(o)return o;try{let e=await fetch(`${V}/get-widget-config`,{method:"GET",headers:{"Content-Type":"application/json","x-sdk-key":r}});if(!e.ok)return console.error("[Voyage] Failed to fetch config:",e.status),null;let n=await e.json();return Ne(r,n),n}catch(e){return console.error("[Voyage] Network error:",e),Y(r,true)}}async function Z(r,o){try{let e=await fetch(`${V}/submit-feedback`,{method:"POST",headers:{"Content-Type":"application/json","x-sdk-key":r},body:JSON.stringify({label:o.label,content:o.text,email:o.email,imageUrl:o.imageUrl,metadata:o.metadata})});if(!e.ok){let n=await e.json().catch(()=>({}));return console.error("[Voyage] Failed to submit feedback:",e.status,n),!1}return !0}catch(e){return console.error("[Voyage] Network error:",e),false}}async function ee(r,o){if(!o.type.startsWith("image/"))return console.error("[Voyage] Only image files are allowed"),null;if(o.size>5*1024*1024)return console.error("[Voyage] Image must be under 5MB"),null;try{let e=new FormData;e.append("file",o);let n=await fetch(`${V}/upload-feedback-image`,{method:"POST",headers:{"x-sdk-key":r},body:e});if(!n.ok){let l=await n.json().catch(()=>({}));return console.error("[Voyage] Failed to upload image:",n.status,l),null}return (await n.json()).url}catch(e){return console.error("[Voyage] Network error:",e),null}}function Y(r,o=false){try{let e=localStorage.getItem(`${G}_${r}`);if(!e)return null;let n=JSON.parse(e);return Date.now()-n.timestamp>36e5&&!o?null:n.config}catch{return null}}function Ne(r,o){try{let e={config:o,timestamp:Date.now()};localStorage.setItem(`${G}_${r}`,JSON.stringify(e));}catch{}}function te(){return {url:window.location.href,pathname:window.location.pathname,browser:navigator.userAgent,viewport:`${window.innerWidth}x${window.innerHeight}`,title:document.title,timestamp:new Date().toISOString(),locale:navigator.language,referrer:document.referrer}}var qe={projectId:"",formFields:{email:{enabled:true,required:false},category:{enabled:true,options:["bug","feature"]}},theme:{primaryColor:"#0ea5e9",position:"bottom-right"},i18n:{buttonLabel:"To : the Maker",placeholder:"Describe your ideas to improve our product",bugPlaceholder:"Please describe the bug in detail (e.g., clicked a button and it didn't work)",featurePlaceholder:"Describe your ideas to improve our product (e.g., I want to export TASK.md to Linear tickets)",submitLabel:"Send to the Maker",successMessage:"Thank you for your feedback!"}},O=class{constructor(){this.config=null;this.serverConfig=null;this.container=null;this.isWidgetVisible=false;this.isModalOpen=false;}async init(o){if(this.config=o,!o.sdkKey){console.error("[Voyage] Invalid SDK Key");return}if(Q(),!this.shouldDisplay()){console.log("[Voyage] Widget hidden by display rules");return}let e=await T(o.sdkKey);this.serverConfig=this.mergeConfig(e,o),this.isWidgetVisible=true,this.renderWidget(),console.log("[Voyage] Initialized");}show(){if(!this.config){console.warn("[Voyage] SDK not initialized");return}this.isWidgetVisible=true,this.renderWidget();}hide(){this.isWidgetVisible=false,this.renderWidget();}open(){if(!this.config){console.warn("[Voyage] SDK not initialized");return}this.isModalOpen=true,this.renderWidget();}close(){this.isModalOpen=false,this.renderWidget();}isOpen(){return this.isModalOpen}isVisible(){return this.isWidgetVisible}async refreshConfig(){if(!this.config)return;let o=await T(this.config.sdkKey);this.serverConfig=this.mergeConfig(o,this.config),this.renderWidget();}destroy(){this.container&&(preact.render(null,this.container),this.container.remove(),this.container=null),this.config=null,this.serverConfig=null,this.isWidgetVisible=false,this.isModalOpen=false;}renderWidget(){if(!this.serverConfig||!this.config)return;this.container||(this.container=document.createElement("div"),this.container.id="voyage-widget-container",document.body.appendChild(this.container));let o=this.config.sdkKey;preact.render(preact.h(E,{config:this.serverConfig,visible:this.isWidgetVisible,defaultOpen:this.isModalOpen,onOpenChange:e=>{this.isModalOpen=e;},getMetadata:te,onSubmit:async e=>Z(o,e),onUpload:async e=>ee(o,e)}),this.container);}mergeConfig(o,e){return o||{...qe}}shouldDisplay(){if(!this.config)return false;let o=window.location.pathname,{include:e,exclude:n}=this.config;return e&&e.length>0?e.some(c=>this.matchPath(o,c)):n&&n.length>0?!n.some(c=>this.matchPath(o,c)):true}matchPath(o,e){if(e.endsWith("/*")){let n=e.slice(0,-2);return o.startsWith(n)}return o===e}},Ke=new O;var St=Ke;
41
+ exports.Voyage=Ke;exports.default=St;//# sourceMappingURL=index.cjs.map
42
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/Button.tsx","../src/components/Modal.tsx","../src/components/Widget.tsx","../src/styles.ts","../src/utils/api.ts","../src/utils/metadata.ts","../src/index.ts"],"names":["MessageIcon","jsx","Button","onClick","label","position","primaryColor","isHovered","setIsHovered","useState","isPressed","setIsPressed","positionStyle","getTransform","jsxs","size","BugIcon","LightbulbIcon","ImageIcon","CloseIcon","CheckIcon","colors","Modal","config","metadata","onClose","onSubmit","onUpload","setLabel","text","setText","email","setEmail","imageUrl","setImageUrl","imagePreview","setImagePreview","isSubmitting","setIsSubmitting","isUploading","setIsUploading","error","setError","success","setSuccess","fileInputRef","useRef","i18n","formFields","theme","maxLength","currentPlaceholder","handleFileSelect","e","file","processedFile","processImage","reader","url","err","handleRemoveImage","handleSubmit","feedbackData","result","styles","primaryBgLight","active","disabled","successContainerStyle","successIconStyle","successTitleStyle","successTextStyle","headerLeftStyle","headerTitleStyle","closeButtonStyle","contentStyle","sectionStyle","labelStyle","typeButtonsStyle","typeIconStyle","descriptionRowStyle","descriptionColStyle","textareaWrapperStyle","textareaStyle","charCountStyle","uploadColStyle","imagePreviewContainerStyle","imagePreviewSquareStyle","removeImageButtonStyle","uploadingOverlayStyle","uploadButtonSquareStyle","inputStyle","errorStyle","spinnerContainerStyle","options","maxWidth","maxHeight","quality","resolve","reject","img","width","height","ratio","canvas","ctx","blob","timestamp","newFile","type","Widget","getMetadata","visible","defaultOpen","onOpenChange","isOpen","setIsOpen","useEffect","Fragment","injectStyles","style","API_BASE","CACHE_KEY","fetchProjectConfig","sdkKey","cached","getFromCache","response","saveToCache","submitFeedback","data","errorData","uploadImage","formData","ignoreExpiry","raw","captureMetadata","DEFAULT_CONFIG","VoyageSDK","fetchedConfig","render","h","open","server","_client","pathname","include","exclude","pattern","prefix","Voyage","index_default"],"mappings":"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","file":"index.cjs","sourcesContent":["import { h } from 'preact';\nimport { useState } from 'preact/hooks';\n\ninterface ButtonProps {\n onClick: () => void;\n label: string;\n position: 'bottom-right' | 'bottom-left';\n primaryColor: string;\n}\n\n// 말풍선 아이콘 SVG\nfunction MessageIcon() {\n return (\n <svg\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <path d=\"M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z\" />\n </svg>\n );\n}\n\nexport function Button({ onClick, label, position, primaryColor }: ButtonProps) {\n const [isHovered, setIsHovered] = useState(false);\n const [isPressed, setIsPressed] = useState(false);\n\n const positionStyle = position === 'bottom-right'\n ? { right: '24px' }\n : { left: '24px' };\n\n // Jelly 효과 transform\n const getTransform = () => {\n if (isPressed) return 'scale(0.95)';\n if (isHovered) return 'scale(1.02) translateY(-2px)';\n return 'scale(1)';\n };\n\n return (\n <button\n onClick={onClick}\n onMouseEnter={() => setIsHovered(true)}\n onMouseLeave={() => { setIsHovered(false); setIsPressed(false); }}\n onMouseDown={() => setIsPressed(true)}\n onMouseUp={() => setIsPressed(false)}\n style={{\n position: 'fixed',\n bottom: '24px',\n ...positionStyle,\n display: 'flex',\n alignItems: 'center',\n gap: '10px',\n padding: '14px 24px',\n border: 'none',\n borderRadius: '50px',\n cursor: 'pointer',\n fontFamily: \"'Quicksand', 'Nunito', system-ui, sans-serif\",\n fontSize: '15px',\n fontWeight: 600,\n color: '#fff',\n zIndex: 9998,\n // primaryColor 적용\n backgroundColor: primaryColor,\n // 그림자 + glow 효과\n boxShadow: isHovered\n ? `0 8px 32px ${primaryColor}60, 0 0 20px ${primaryColor}40`\n : `0 4px 20px ${primaryColor}50, 0 0 10px ${primaryColor}30`,\n // Jelly 애니메이션\n transform: getTransform(),\n transition: 'all 0.2s cubic-bezier(0.34, 1.56, 0.64, 1)',\n }}\n >\n <MessageIcon />\n <span>{label}</span>\n </button>\n );\n}\n","import { h } from 'preact';\nimport { useRef, useState } from 'preact/hooks';\n\nimport type { ProjectConfig, FeedbackData, FeedbackMetadata } from '../types';\n\ninterface ModalProps {\n config: ProjectConfig;\n metadata: FeedbackMetadata;\n onClose: () => void;\n onSubmit: (data: FeedbackData) => Promise<boolean>;\n onUpload: (file: File) => Promise<string | null>;\n}\n\n// 아이콘들\nfunction MessageIcon({ size = 24 }: { size?: number }) {\n return (\n <svg width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <path d=\"M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z\" />\n </svg>\n );\n}\n\nfunction BugIcon() {\n return (\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <path d=\"M8 2l1.88 1.88M14.12 3.88L16 2M9 7.13v-1a3.003 3.003 0 116 0v1\" />\n <path d=\"M12 20c-3.3 0-6-2.7-6-6v-3a4 4 0 014-4h4a4 4 0 014 4v3c0 3.3-2.7 6-6 6\" />\n <path d=\"M12 20v-9M6.53 9C4.6 8.8 3 7.1 3 5M6 13H2M6 17l-4 1M17.47 9c1.93-.2 3.53-1.9 3.53-4M18 13h4M18 17l4 1\" />\n </svg>\n );\n}\n\nfunction LightbulbIcon() {\n return (\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <path d=\"M15 14c.2-1 .7-1.7 1.5-2.5 1-.9 1.5-2.2 1.5-3.5A6 6 0 006 8c0 1 .2 2.2 1.5 3.5.7.7 1.3 1.5 1.5 2.5\" />\n <path d=\"M9 18h6M10 22h4\" />\n </svg>\n );\n}\n\nfunction ImageIcon() {\n return (\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"2\" ry=\"2\" />\n <circle cx=\"8.5\" cy=\"8.5\" r=\"1.5\" />\n <polyline points=\"21 15 16 10 5 21\" />\n </svg>\n );\n}\n\nfunction CloseIcon() {\n return (\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\" />\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\" />\n </svg>\n );\n}\n\nfunction CheckIcon() {\n return (\n <svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <polyline points=\"20 6 9 17 4 12\" />\n </svg>\n );\n}\n\n// 라이트 테마 색상\nconst colors = {\n bg: '#FFFFFF',\n bgLight: '#F8FAFC',\n bgCard: 'rgba(255, 255, 255, 0.98)',\n border: 'rgba(0, 0, 0, 0.08)',\n borderLight: 'rgba(0, 0, 0, 0.12)',\n text: '#1F2937',\n textMuted: '#6B7280',\n error: '#EF4444',\n success: '#22C55E',\n};\n\nexport function Modal({ config, metadata, onClose, onSubmit, onUpload }: ModalProps) {\n const [label, setLabel] = useState<'bug' | 'feature' | null>(null);\n const [text, setText] = useState('');\n const [email, setEmail] = useState('');\n const [imageUrl, setImageUrl] = useState<string | null>(null);\n const [imagePreview, setImagePreview] = useState<string | null>(null);\n const [isSubmitting, setIsSubmitting] = useState(false);\n const [isUploading, setIsUploading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [success, setSuccess] = useState(false);\n const fileInputRef = useRef<HTMLInputElement>(null);\n\n const { i18n, formFields, theme } = config;\n const primaryColor = theme.primaryColor || '#0ea5e9';\n const maxLength = 400;\n\n // 동적 placeholder\n const currentPlaceholder = label === 'bug'\n ? (i18n.bugPlaceholder || i18n.placeholder)\n : (i18n.featurePlaceholder || i18n.placeholder);\n\n const handleFileSelect = async (e: Event) => {\n const target = e.target as HTMLInputElement;\n const file = target.files?.[0];\n if (!file) return;\n\n if (!file.type.startsWith('image/')) {\n setError('Only image files are allowed');\n return;\n }\n if (file.size > 10 * 1024 * 1024) {\n setError('Image must be under 10MB');\n return;\n }\n\n setIsUploading(true);\n setError(null);\n\n try {\n // 이미지를 WebP로 변환 및 resize\n const processedFile = await processImage(file, {\n maxWidth: 1920,\n maxHeight: 1080,\n quality: 0.8,\n });\n\n // 프리뷰 생성\n const reader = new FileReader();\n reader.onload = () => setImagePreview(reader.result as string);\n reader.readAsDataURL(processedFile);\n\n const url = await onUpload(processedFile);\n setIsUploading(false);\n\n if (url) {\n setImageUrl(url);\n } else {\n setError('Failed to upload image');\n setImagePreview(null);\n }\n } catch (err) {\n console.error('[Voyage] Image processing error:', err);\n setIsUploading(false);\n setError('Failed to process image');\n }\n };\n\n const handleRemoveImage = () => {\n setImageUrl(null);\n setImagePreview(null);\n if (fileInputRef.current) {\n fileInputRef.current.value = '';\n }\n };\n\n const handleSubmit = async () => {\n if (!label) {\n setError('Please select a type');\n return;\n }\n if (!text.trim()) {\n setError('Please enter your feedback');\n return;\n }\n if (formFields.email.required && !email.trim()) {\n setError('Please enter your email');\n return;\n }\n\n setIsSubmitting(true);\n setError(null);\n\n const feedbackData: FeedbackData = {\n label,\n text: text.trim(),\n email: email.trim() || undefined,\n imageUrl: imageUrl || undefined,\n metadata,\n };\n\n const result = await onSubmit(feedbackData);\n setIsSubmitting(false);\n\n if (result) {\n setSuccess(true);\n } else {\n setError('Failed to submit. Please try again.');\n }\n };\n\n // 스타일 함수 (primaryColor 적용)\n const getStyles = () => {\n const primaryBgLight = `${primaryColor}15`;\n\n return {\n overlay: {\n position: 'fixed',\n top: 0,\n left: 0,\n right: 0,\n bottom: 0,\n backgroundColor: 'rgba(0, 0, 0, 0.4)',\n backdropFilter: 'blur(4px)',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n zIndex: 9999,\n padding: '20px',\n } as h.JSX.CSSProperties,\n\n modal: {\n backgroundColor: colors.bgCard,\n backdropFilter: 'blur(20px)',\n borderRadius: '20px',\n width: '100%',\n maxWidth: '580px',\n maxHeight: '90vh',\n overflowY: 'auto',\n position: 'relative',\n fontFamily: \"'Quicksand', 'Nunito', system-ui, sans-serif\",\n border: `1px solid ${colors.border}`,\n boxShadow: '0 25px 50px -12px rgba(0, 0, 0, 0.15)',\n } as h.JSX.CSSProperties,\n\n colorBar: {\n height: '4px',\n backgroundColor: primaryColor,\n } as h.JSX.CSSProperties,\n\n header: {\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n padding: '14px 20px',\n borderBottom: `1px solid ${colors.border}`,\n backgroundColor: colors.bgLight,\n } as h.JSX.CSSProperties,\n\n headerIcon: {\n width: '32px',\n height: '32px',\n borderRadius: '10px',\n backgroundColor: primaryColor,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n color: '#fff',\n } as h.JSX.CSSProperties,\n\n typeButton: (active: boolean) => ({\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n gap: '6px',\n padding: '10px 12px',\n border: active ? `2px solid ${primaryColor}` : `1px solid ${colors.border}`,\n borderRadius: '10px',\n backgroundColor: active ? primaryBgLight : colors.bgLight,\n cursor: 'pointer',\n fontWeight: 600,\n fontSize: '13px',\n color: active ? colors.text : colors.textMuted,\n transition: 'all 0.2s',\n } as h.JSX.CSSProperties),\n\n submitButton: (disabled: boolean) => ({\n width: '100%',\n padding: '12px',\n backgroundColor: disabled ? colors.bgLight : primaryColor,\n color: disabled ? colors.textMuted : '#fff',\n border: 'none',\n borderRadius: '12px',\n fontSize: '15px',\n fontWeight: 700,\n cursor: disabled ? 'not-allowed' : 'pointer',\n fontFamily: 'inherit',\n transition: 'all 0.2s',\n boxShadow: disabled ? 'none' : `0 4px 15px ${primaryColor}40`,\n } as h.JSX.CSSProperties),\n\n primaryButton: {\n width: '100%',\n padding: '16px',\n backgroundColor: primaryColor,\n color: '#fff',\n border: 'none',\n borderRadius: '12px',\n fontSize: '15px',\n fontWeight: 700,\n cursor: 'pointer',\n fontFamily: 'inherit',\n marginTop: '8px',\n } as h.JSX.CSSProperties,\n };\n };\n\n const styles = getStyles();\n\n // 성공 화면\n if (success) {\n return (\n <div style={styles.overlay}>\n <div style={styles.modal}>\n <div style={successContainerStyle}>\n <div style={successIconStyle}>\n <CheckIcon />\n </div>\n <h3 style={successTitleStyle}>{i18n.successMessage}</h3>\n <p style={successTextStyle}>Your voice shapes what we build next.</p>\n <button onClick={onClose} style={styles.primaryButton}>\n Done\n </button>\n </div>\n </div>\n </div>\n );\n }\n\n return (\n <div style={styles.overlay} onClick={onClose}>\n <div style={styles.modal} onClick={(e) => e.stopPropagation()}>\n {/* 헤더 */}\n <div style={styles.header}>\n <div style={headerLeftStyle}>\n <div style={styles.headerIcon}>\n <MessageIcon size={20} />\n </div>\n <span style={headerTitleStyle}>Help Us Improve</span>\n </div>\n <button onClick={onClose} style={closeButtonStyle}>\n <CloseIcon />\n </button>\n </div>\n\n {/* 컬러 바 */}\n <div style={styles.colorBar} />\n\n {/* 컨텐츠 */}\n <div style={contentStyle}>\n {/* 이슈 타입 */}\n <div style={sectionStyle}>\n <label style={labelStyle}>Feedback Type</label>\n <div style={typeButtonsStyle}>\n <button\n onClick={() => setLabel('bug')}\n style={styles.typeButton(label === 'bug')}\n >\n <span style={typeIconStyle(label === 'bug', 'bug')}>\n <BugIcon />\n </span>\n <span>Bug</span>\n </button>\n <button\n onClick={() => setLabel('feature')}\n style={styles.typeButton(label === 'feature')}\n >\n <span style={typeIconStyle(label === 'feature', 'feature')}>\n <LightbulbIcon />\n </span>\n <span>Feature</span>\n </button>\n </div>\n </div>\n\n {/* 설명 + 이미지 (flex row) */}\n <div style={descriptionRowStyle}>\n {/* 텍스트 영역 */}\n <div style={descriptionColStyle}>\n <label style={labelStyle}>Description</label>\n <div style={textareaWrapperStyle}>\n <textarea\n placeholder={currentPlaceholder}\n value={text}\n maxLength={maxLength}\n onInput={(e) => setText((e.target as HTMLTextAreaElement).value)}\n style={textareaStyle}\n />\n <span style={charCountStyle}>{text.length}/{maxLength}</span>\n </div>\n </div>\n\n {/* 이미지 업로드 (정사각형) */}\n <div style={uploadColStyle}>\n <label style={labelStyle}>Screenshot</label>\n {imagePreview ? (\n <div style={imagePreviewContainerStyle}>\n <img src={imagePreview} alt=\"Preview\" style={imagePreviewSquareStyle} />\n <button onClick={handleRemoveImage} style={removeImageButtonStyle}>\n <CloseIcon />\n </button>\n {isUploading && <div style={uploadingOverlayStyle}>...</div>}\n </div>\n ) : (\n <button\n onClick={() => fileInputRef.current?.click()}\n style={uploadButtonSquareStyle}\n >\n <ImageIcon />\n <span style={{ fontSize: '11px' }}>Upload</span>\n </button>\n )}\n <input\n ref={fileInputRef}\n type=\"file\"\n accept=\"image/*\"\n onChange={handleFileSelect}\n style={{ display: 'none' }}\n />\n </div>\n </div>\n\n {/* 이메일 */}\n {formFields.email.enabled && (\n <div style={sectionStyle}>\n <label style={labelStyle}>\n Email {formFields.email.required ? '' : '(optional)'}\n </label>\n <input\n type=\"email\"\n placeholder=\"your@email.com\"\n value={email}\n onInput={(e) => setEmail((e.target as HTMLInputElement).value)}\n style={inputStyle}\n />\n </div>\n )}\n\n {/* 에러 */}\n {error && <p style={errorStyle}>{error}</p>}\n\n {/* 제출 버튼 */}\n <button\n onClick={handleSubmit}\n disabled={isSubmitting || isUploading}\n style={styles.submitButton(isSubmitting || isUploading)}\n >\n {isSubmitting ? (\n <span style={spinnerContainerStyle}>\n <span className=\"voyage-spinner\" />\n Sending...\n </span>\n ) : (\n i18n.submitLabel\n )}\n </button>\n </div>\n </div>\n </div>\n );\n}\n\n// 이미지 처리 유틸: resize + WebP 변환\ninterface ProcessImageOptions {\n maxWidth: number;\n maxHeight: number;\n quality: number;\n}\n\nasync function processImage(file: File, options: ProcessImageOptions): Promise<File> {\n const { maxWidth, maxHeight, quality } = options;\n\n return new Promise((resolve, reject) => {\n const img = new Image();\n img.onload = () => {\n // 비율 유지하면서 resize 계산\n let { width, height } = img;\n\n if (width > maxWidth || height > maxHeight) {\n const ratio = Math.min(maxWidth / width, maxHeight / height);\n width = Math.round(width * ratio);\n height = Math.round(height * ratio);\n }\n\n // Canvas에 그리기\n const canvas = document.createElement('canvas');\n canvas.width = width;\n canvas.height = height;\n\n const ctx = canvas.getContext('2d');\n if (!ctx) {\n reject(new Error('Failed to get canvas context'));\n return;\n }\n\n ctx.drawImage(img, 0, 0, width, height);\n\n // WebP로 변환\n canvas.toBlob(\n (blob) => {\n if (!blob) {\n reject(new Error('Failed to convert image'));\n return;\n }\n\n // Blob을 File로 변환\n const timestamp = Date.now();\n const newFile = new File([blob], `image_${timestamp}.webp`, {\n type: 'image/webp',\n });\n\n resolve(newFile);\n },\n 'image/webp',\n quality\n );\n };\n\n img.onerror = () => reject(new Error('Failed to load image'));\n img.src = URL.createObjectURL(file);\n });\n}\n\n// 정적 스타일 정의 (라이트 테마)\nconst headerLeftStyle: h.JSX.CSSProperties = {\n display: 'flex',\n alignItems: 'center',\n gap: '12px',\n};\n\nconst headerTitleStyle: h.JSX.CSSProperties = {\n fontSize: '16px',\n fontWeight: 700,\n color: colors.text,\n};\n\nconst closeButtonStyle: h.JSX.CSSProperties = {\n background: 'transparent',\n border: 'none',\n color: colors.textMuted,\n cursor: 'pointer',\n padding: '8px',\n borderRadius: '8px',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n transition: 'all 0.2s',\n};\n\nconst contentStyle: h.JSX.CSSProperties = {\n padding: '16px 20px',\n};\n\nconst sectionStyle: h.JSX.CSSProperties = {\n marginBottom: '14px',\n};\n\nconst labelStyle: h.JSX.CSSProperties = {\n display: 'block',\n fontSize: '13px',\n fontWeight: 600,\n color: colors.text,\n marginBottom: '8px',\n};\n\nconst typeButtonsStyle: h.JSX.CSSProperties = {\n display: 'grid',\n gridTemplateColumns: '1fr 1fr',\n gap: '12px',\n};\n\nconst descriptionRowStyle: h.JSX.CSSProperties = {\n display: 'flex',\n gap: '14px',\n marginBottom: '14px',\n};\n\nconst descriptionColStyle: h.JSX.CSSProperties = {\n flex: 1,\n display: 'flex',\n flexDirection: 'column',\n};\n\nconst uploadColStyle: h.JSX.CSSProperties = {\n width: '120px',\n flexShrink: 0,\n display: 'flex',\n flexDirection: 'column',\n};\n\nconst uploadButtonSquareStyle: h.JSX.CSSProperties = {\n width: '100%',\n aspectRatio: '1',\n border: `2px dashed ${colors.border}`,\n borderRadius: '10px',\n backgroundColor: 'transparent',\n cursor: 'pointer',\n display: 'flex',\n flexDirection: 'column',\n alignItems: 'center',\n justifyContent: 'center',\n gap: '4px',\n color: colors.textMuted,\n fontSize: '12px',\n transition: 'all 0.2s',\n};\n\nconst imagePreviewSquareStyle: h.JSX.CSSProperties = {\n width: '100%',\n aspectRatio: '1',\n objectFit: 'cover',\n borderRadius: '10px',\n};\n\nconst typeIconStyle = (active: boolean, type: 'bug' | 'feature'): h.JSX.CSSProperties => ({\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n color: active\n ? (type === 'bug' ? colors.error : '#F59E0B')\n : colors.textMuted,\n});\n\nconst textareaWrapperStyle: h.JSX.CSSProperties = {\n position: 'relative',\n};\n\nconst textareaStyle: h.JSX.CSSProperties = {\n width: '100%',\n minHeight: '120px',\n padding: '14px',\n border: `1px solid ${colors.border}`,\n borderRadius: '12px',\n fontSize: '14px',\n resize: 'vertical',\n boxSizing: 'border-box',\n backgroundColor: colors.bgLight,\n color: colors.text,\n fontFamily: 'inherit',\n outline: 'none',\n};\n\nconst charCountStyle: h.JSX.CSSProperties = {\n position: 'absolute',\n bottom: '12px',\n right: '14px',\n fontSize: '12px',\n color: colors.textMuted,\n};\n\nconst inputStyle: h.JSX.CSSProperties = {\n width: '100%',\n padding: '10px 12px',\n border: `1px solid ${colors.border}`,\n borderRadius: '10px',\n fontSize: '13px',\n boxSizing: 'border-box',\n backgroundColor: colors.bgLight,\n color: colors.text,\n fontFamily: 'inherit',\n outline: 'none',\n};\n\nconst imagePreviewContainerStyle: h.JSX.CSSProperties = {\n position: 'relative',\n display: 'inline-block',\n borderRadius: '12px',\n overflow: 'hidden',\n};\n\nconst removeImageButtonStyle: h.JSX.CSSProperties = {\n position: 'absolute',\n top: '8px',\n right: '8px',\n width: '28px',\n height: '28px',\n borderRadius: '50%',\n backgroundColor: 'rgba(0, 0, 0, 0.7)',\n color: '#fff',\n border: 'none',\n cursor: 'pointer',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n};\n\nconst uploadingOverlayStyle: h.JSX.CSSProperties = {\n position: 'absolute',\n inset: 0,\n backgroundColor: 'rgba(0, 0, 0, 0.7)',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n color: '#fff',\n fontSize: '14px',\n borderRadius: '12px',\n};\n\nconst errorStyle: h.JSX.CSSProperties = {\n color: colors.error,\n fontSize: '14px',\n marginBottom: '16px',\n padding: '12px',\n backgroundColor: 'rgba(239, 68, 68, 0.1)',\n borderRadius: '8px',\n border: `1px solid rgba(239, 68, 68, 0.3)`,\n};\n\nconst successContainerStyle: h.JSX.CSSProperties = {\n padding: '48px 24px',\n textAlign: 'center',\n};\n\nconst successIconStyle: h.JSX.CSSProperties = {\n width: '64px',\n height: '64px',\n borderRadius: '50%',\n backgroundColor: 'rgba(34, 197, 94, 0.15)',\n color: colors.success,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n margin: '0 auto 20px',\n};\n\nconst successTitleStyle: h.JSX.CSSProperties = {\n fontSize: '20px',\n fontWeight: 700,\n color: colors.text,\n marginBottom: '8px',\n};\n\nconst successTextStyle: h.JSX.CSSProperties = {\n fontSize: '14px',\n color: colors.textMuted,\n marginBottom: '24px',\n};\n\nconst spinnerContainerStyle: h.JSX.CSSProperties = {\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n gap: '8px',\n};\n","import { h } from 'preact';\nimport { useState, useEffect } from 'preact/hooks';\nimport { Button } from './Button';\nimport { Modal } from './Modal';\nimport type { ProjectConfig, FeedbackData, FeedbackMetadata } from '../types';\n\ninterface WidgetProps {\n config: ProjectConfig;\n onSubmit: (data: FeedbackData) => Promise<boolean>;\n onUpload: (file: File) => Promise<string | null>;\n getMetadata: () => FeedbackMetadata;\n visible: boolean;\n defaultOpen?: boolean;\n onOpenChange?: (open: boolean) => void;\n}\n\nexport function Widget({\n config,\n onSubmit,\n onUpload,\n getMetadata,\n visible,\n defaultOpen = false,\n onOpenChange,\n}: WidgetProps) {\n const [isOpen, setIsOpen] = useState(defaultOpen);\n\n useEffect(() => {\n onOpenChange?.(isOpen);\n }, [isOpen, onOpenChange]);\n\n if (!visible) {\n return null;\n }\n\n const { theme, i18n } = config;\n\n return (\n <>\n {!isOpen && (\n <Button\n onClick={() => setIsOpen(true)}\n label={i18n.buttonLabel}\n position={theme.position}\n primaryColor={theme.primaryColor}\n />\n )}\n\n {isOpen && (\n <Modal\n config={config}\n metadata={getMetadata()}\n onClose={() => setIsOpen(false)}\n onSubmit={onSubmit}\n onUpload={onUpload}\n />\n )}\n </>\n );\n}\n","// SDK 전용 CSS 스타일 (동적 주입)\nexport const injectStyles = () => {\n if (typeof document === 'undefined') return;\n if (document.getElementById('voyage-sdk-styles')) return;\n\n const style = document.createElement('style');\n style.id = 'voyage-sdk-styles';\n style.textContent = `\n /* Voyage SDK Styles */\n\n /* Spinner Animation */\n @keyframes voyage-spin {\n from { transform: rotate(0deg); }\n to { transform: rotate(360deg); }\n }\n\n #voyage-widget-container * {\n box-sizing: border-box;\n }\n\n #voyage-widget-container button {\n font-family: 'Quicksand', 'Nunito', system-ui, sans-serif;\n }\n\n #voyage-widget-container textarea:focus,\n #voyage-widget-container input:focus {\n border-color: rgba(124, 58, 237, 0.5);\n box-shadow: 0 0 0 3px rgba(124, 58, 237, 0.1);\n }\n\n #voyage-widget-container button:focus {\n outline: none;\n }\n\n /* Spinner */\n .voyage-spinner {\n width: 16px;\n height: 16px;\n border: 2px solid rgba(255,255,255,0.3);\n border-top-color: #fff;\n border-radius: 50%;\n animation: voyage-spin 0.8s linear infinite;\n }\n\n /* Load Quicksand font if not present */\n @import url('https://fonts.googleapis.com/css2?family=Quicksand:wght@400;500;600;700&display=swap');\n `;\n\n document.head.appendChild(style);\n};\n","import type { ProjectConfig, FeedbackData } from '../types';\n\n// Supabase Edge Functions URL\nconst API_BASE = 'https://rxflzurkkuqgopjkshjh.supabase.co/functions/v1';\n\nconst CACHE_KEY = 'voyage_config';\nconst CACHE_TTL = 60 * 60 * 1000; // 1시간\n\ninterface CachedConfig {\n config: ProjectConfig;\n timestamp: number;\n}\n\nexport async function fetchProjectConfig(sdkKey: string): Promise<ProjectConfig | null> {\n // 캐시 확인\n const cached = getFromCache(sdkKey);\n if (cached) {\n return cached;\n }\n\n try {\n const response = await fetch(`${API_BASE}/get-widget-config`, {\n method: 'GET',\n headers: {\n 'Content-Type': 'application/json',\n 'x-sdk-key': sdkKey,\n },\n });\n\n if (!response.ok) {\n console.error('[Voyage] Failed to fetch config:', response.status);\n return null;\n }\n\n const config = await response.json() as ProjectConfig;\n saveToCache(sdkKey, config);\n return config;\n } catch (error) {\n console.error('[Voyage] Network error:', error);\n // 네트워크 실패 시 캐시 사용 (만료되어도)\n return getFromCache(sdkKey, true);\n }\n}\n\nexport async function submitFeedback(sdkKey: string, data: FeedbackData): Promise<boolean> {\n try {\n const response = await fetch(`${API_BASE}/submit-feedback`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'x-sdk-key': sdkKey,\n },\n body: JSON.stringify({\n label: data.label,\n content: data.text,\n email: data.email,\n imageUrl: data.imageUrl,\n metadata: data.metadata,\n }),\n });\n\n if (!response.ok) {\n const errorData = await response.json().catch(() => ({}));\n console.error('[Voyage] Failed to submit feedback:', response.status, errorData);\n return false;\n }\n\n return true;\n } catch (error) {\n console.error('[Voyage] Network error:', error);\n return false;\n }\n}\n\nexport async function uploadImage(sdkKey: string, file: File): Promise<string | null> {\n // 검증\n if (!file.type.startsWith('image/')) {\n console.error('[Voyage] Only image files are allowed');\n return null;\n }\n\n if (file.size > 5 * 1024 * 1024) {\n console.error('[Voyage] Image must be under 5MB');\n return null;\n }\n\n try {\n const formData = new FormData();\n formData.append('file', file);\n\n const response = await fetch(`${API_BASE}/upload-feedback-image`, {\n method: 'POST',\n headers: {\n 'x-sdk-key': sdkKey,\n },\n body: formData,\n });\n\n if (!response.ok) {\n const errorData = await response.json().catch(() => ({}));\n console.error('[Voyage] Failed to upload image:', response.status, errorData);\n return null;\n }\n\n const result = await response.json();\n return result.url;\n } catch (error) {\n console.error('[Voyage] Network error:', error);\n return null;\n }\n}\n\nfunction getFromCache(sdkKey: string, ignoreExpiry = false): ProjectConfig | null {\n try {\n const raw = localStorage.getItem(`${CACHE_KEY}_${sdkKey}`);\n if (!raw) return null;\n\n const cached: CachedConfig = JSON.parse(raw);\n const isExpired = Date.now() - cached.timestamp > CACHE_TTL;\n\n if (isExpired && !ignoreExpiry) {\n return null;\n }\n\n return cached.config;\n } catch {\n return null;\n }\n}\n\nfunction saveToCache(sdkKey: string, config: ProjectConfig): void {\n try {\n const cached: CachedConfig = {\n config,\n timestamp: Date.now(),\n };\n localStorage.setItem(`${CACHE_KEY}_${sdkKey}`, JSON.stringify(cached));\n } catch {\n // localStorage 사용 불가 시 무시\n }\n}\n","import type { FeedbackMetadata } from '../types';\n\nexport function captureMetadata(): FeedbackMetadata {\n return {\n url: window.location.href,\n pathname: window.location.pathname,\n browser: navigator.userAgent,\n viewport: `${window.innerWidth}x${window.innerHeight}`,\n title: document.title,\n timestamp: new Date().toISOString(),\n locale: navigator.language,\n referrer: document.referrer,\n };\n}\n","// @voyage/sdk\n// Feedback Widget SDK for collecting user feedback\n\nimport { h, render } from 'preact';\n\nimport { Widget } from './components';\nimport { injectStyles } from './styles';\nimport { fetchProjectConfig, submitFeedback, uploadImage } from './utils/api';\nimport { captureMetadata } from './utils/metadata';\n\nimport type { VoyageConfig, ProjectConfig, FeedbackData } from './types';\n\nexport type { VoyageConfig, ProjectConfig, FeedbackData, FeedbackMetadata } from './types';\n\nconst DEFAULT_CONFIG: ProjectConfig = {\n projectId: '',\n formFields: {\n email: { enabled: true, required: false },\n category: { enabled: true, options: ['bug', 'feature'] },\n },\n theme: {\n primaryColor: '#0ea5e9',\n position: 'bottom-right',\n },\n i18n: {\n buttonLabel: 'To : the Maker',\n placeholder: 'Describe your ideas to improve our product',\n bugPlaceholder: 'Please describe the bug in detail (e.g., clicked a button and it didn\\'t work)',\n featurePlaceholder: 'Describe your ideas to improve our product (e.g., I want to export TASK.md to Linear tickets)',\n submitLabel: 'Send to the Maker',\n successMessage: 'Thank you for your feedback!',\n },\n};\n\nclass VoyageSDK {\n private config: VoyageConfig | null = null;\n private serverConfig: ProjectConfig | null = null;\n private container: HTMLElement | null = null;\n private isWidgetVisible = false;\n private isModalOpen = false;\n\n async init(config: VoyageConfig): Promise<void> {\n this.config = config;\n\n if (!config.sdkKey) {\n console.error('[Voyage] Invalid SDK Key');\n return;\n }\n\n // Inject SDK styles\n injectStyles();\n\n // Check display control\n if (!this.shouldDisplay()) {\n console.log('[Voyage] Widget hidden by display rules');\n return;\n }\n\n // Fetch server config\n const fetchedConfig = await fetchProjectConfig(config.sdkKey);\n this.serverConfig = this.mergeConfig(fetchedConfig, config);\n\n // Render widget\n this.isWidgetVisible = true;\n this.renderWidget();\n console.log('[Voyage] Initialized');\n }\n\n show(): void {\n if (!this.config) {\n console.warn('[Voyage] SDK not initialized');\n return;\n }\n this.isWidgetVisible = true;\n this.renderWidget();\n }\n\n hide(): void {\n this.isWidgetVisible = false;\n this.renderWidget();\n }\n\n open(): void {\n if (!this.config) {\n console.warn('[Voyage] SDK not initialized');\n return;\n }\n this.isModalOpen = true;\n this.renderWidget();\n }\n\n close(): void {\n this.isModalOpen = false;\n this.renderWidget();\n }\n\n isOpen(): boolean {\n return this.isModalOpen;\n }\n\n isVisible(): boolean {\n return this.isWidgetVisible;\n }\n\n async refreshConfig(): Promise<void> {\n if (!this.config) return;\n const fetchedConfig = await fetchProjectConfig(this.config.sdkKey);\n this.serverConfig = this.mergeConfig(fetchedConfig, this.config);\n this.renderWidget();\n }\n\n destroy(): void {\n if (this.container) {\n render(null, this.container);\n this.container.remove();\n this.container = null;\n }\n this.config = null;\n this.serverConfig = null;\n this.isWidgetVisible = false;\n this.isModalOpen = false;\n }\n\n private renderWidget(): void {\n if (!this.serverConfig || !this.config) return;\n\n // Create container if not exists\n if (!this.container) {\n this.container = document.createElement('div');\n this.container.id = 'voyage-widget-container';\n document.body.appendChild(this.container);\n }\n\n const sdkKey = this.config.sdkKey;\n\n render(\n h(Widget, {\n config: this.serverConfig,\n visible: this.isWidgetVisible,\n defaultOpen: this.isModalOpen,\n onOpenChange: (open: boolean) => {\n this.isModalOpen = open;\n },\n getMetadata: captureMetadata,\n onSubmit: async (data: FeedbackData) => {\n return submitFeedback(sdkKey, data);\n },\n onUpload: async (file: File) => {\n return uploadImage(sdkKey, file);\n },\n }),\n this.container\n );\n }\n\n private mergeConfig(server: ProjectConfig | null, _client: VoyageConfig): ProjectConfig {\n // Server config takes priority: use values set from dashboard\n // Client-side theme override disabled (to prevent config conflicts)\n return server || { ...DEFAULT_CONFIG };\n }\n\n private shouldDisplay(): boolean {\n if (!this.config) return false;\n\n const pathname = window.location.pathname;\n const { include, exclude } = this.config;\n\n // include takes priority over exclude\n if (include && include.length > 0) {\n return include.some((pattern) => this.matchPath(pathname, pattern));\n }\n\n if (exclude && exclude.length > 0) {\n return !exclude.some((pattern) => this.matchPath(pathname, pattern));\n }\n\n return true;\n }\n\n private matchPath(pathname: string, pattern: string): boolean {\n // Simple glob matching: /admin/* matches /admin/anything\n if (pattern.endsWith('/*')) {\n const prefix = pattern.slice(0, -2);\n return pathname.startsWith(prefix);\n }\n return pathname === pattern;\n }\n}\n\n// Singleton instance\nconst Voyage = new VoyageSDK();\n\nexport { Voyage };\nexport default Voyage;\n"]}
@@ -0,0 +1,73 @@
1
+ interface VoyageConfig {
2
+ sdkKey: string;
3
+ /** Path patterns to exclude widget display (e.g., ['/admin/*', '/login']) */
4
+ exclude?: string[];
5
+ /** Path patterns to include widget display (when set, widget only shows on these paths) */
6
+ include?: string[];
7
+ }
8
+ interface FeedbackData {
9
+ label: 'bug' | 'feature';
10
+ text: string;
11
+ imageUrl?: string;
12
+ email?: string;
13
+ metadata: FeedbackMetadata;
14
+ }
15
+ interface FeedbackMetadata {
16
+ url: string;
17
+ pathname: string;
18
+ browser: string;
19
+ viewport: string;
20
+ title: string;
21
+ timestamp: string;
22
+ locale: string;
23
+ referrer: string;
24
+ }
25
+ interface ProjectConfig {
26
+ projectId: string;
27
+ formFields: {
28
+ email: {
29
+ enabled: boolean;
30
+ required: boolean;
31
+ };
32
+ category: {
33
+ enabled: boolean;
34
+ options: string[];
35
+ };
36
+ };
37
+ theme: {
38
+ primaryColor: string;
39
+ position: 'bottom-right' | 'bottom-left' | string;
40
+ };
41
+ i18n: {
42
+ buttonLabel: string;
43
+ placeholder: string;
44
+ bugPlaceholder: string;
45
+ featurePlaceholder: string;
46
+ submitLabel: string;
47
+ successMessage: string;
48
+ };
49
+ }
50
+
51
+ declare class VoyageSDK {
52
+ private config;
53
+ private serverConfig;
54
+ private container;
55
+ private isWidgetVisible;
56
+ private isModalOpen;
57
+ init(config: VoyageConfig): Promise<void>;
58
+ show(): void;
59
+ hide(): void;
60
+ open(): void;
61
+ close(): void;
62
+ isOpen(): boolean;
63
+ isVisible(): boolean;
64
+ refreshConfig(): Promise<void>;
65
+ destroy(): void;
66
+ private renderWidget;
67
+ private mergeConfig;
68
+ private shouldDisplay;
69
+ private matchPath;
70
+ }
71
+ declare const Voyage: VoyageSDK;
72
+
73
+ export { type FeedbackData, type FeedbackMetadata, type ProjectConfig, Voyage, type VoyageConfig, Voyage as default };
@@ -0,0 +1,73 @@
1
+ interface VoyageConfig {
2
+ sdkKey: string;
3
+ /** Path patterns to exclude widget display (e.g., ['/admin/*', '/login']) */
4
+ exclude?: string[];
5
+ /** Path patterns to include widget display (when set, widget only shows on these paths) */
6
+ include?: string[];
7
+ }
8
+ interface FeedbackData {
9
+ label: 'bug' | 'feature';
10
+ text: string;
11
+ imageUrl?: string;
12
+ email?: string;
13
+ metadata: FeedbackMetadata;
14
+ }
15
+ interface FeedbackMetadata {
16
+ url: string;
17
+ pathname: string;
18
+ browser: string;
19
+ viewport: string;
20
+ title: string;
21
+ timestamp: string;
22
+ locale: string;
23
+ referrer: string;
24
+ }
25
+ interface ProjectConfig {
26
+ projectId: string;
27
+ formFields: {
28
+ email: {
29
+ enabled: boolean;
30
+ required: boolean;
31
+ };
32
+ category: {
33
+ enabled: boolean;
34
+ options: string[];
35
+ };
36
+ };
37
+ theme: {
38
+ primaryColor: string;
39
+ position: 'bottom-right' | 'bottom-left' | string;
40
+ };
41
+ i18n: {
42
+ buttonLabel: string;
43
+ placeholder: string;
44
+ bugPlaceholder: string;
45
+ featurePlaceholder: string;
46
+ submitLabel: string;
47
+ successMessage: string;
48
+ };
49
+ }
50
+
51
+ declare class VoyageSDK {
52
+ private config;
53
+ private serverConfig;
54
+ private container;
55
+ private isWidgetVisible;
56
+ private isModalOpen;
57
+ init(config: VoyageConfig): Promise<void>;
58
+ show(): void;
59
+ hide(): void;
60
+ open(): void;
61
+ close(): void;
62
+ isOpen(): boolean;
63
+ isVisible(): boolean;
64
+ refreshConfig(): Promise<void>;
65
+ destroy(): void;
66
+ private renderWidget;
67
+ private mergeConfig;
68
+ private shouldDisplay;
69
+ private matchPath;
70
+ }
71
+ declare const Voyage: VoyageSDK;
72
+
73
+ export { type FeedbackData, type FeedbackMetadata, type ProjectConfig, Voyage, type VoyageConfig, Voyage as default };
package/dist/index.js ADDED
@@ -0,0 +1,42 @@
1
+ import {render,h}from'preact';import {useState,useEffect,useRef}from'preact/hooks';import {jsxs,Fragment,jsx}from'preact/jsx-runtime';function ce(){return jsx("svg",{width:"20",height:"20",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",children:jsx("path",{d:"M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"})})}function X({onClick:r,label:o,position:e,primaryColor:n}){let[c,l]=useState(false),[u,s]=useState(false),p=e==="bottom-right"?{right:"24px"}:{left:"24px"},d=()=>u?"scale(0.95)":c?"scale(1.02) translateY(-2px)":"scale(1)";return jsxs("button",{onClick:r,onMouseEnter:()=>l(true),onMouseLeave:()=>{l(false),s(false);},onMouseDown:()=>s(true),onMouseUp:()=>s(false),style:{position:"fixed",bottom:"24px",...p,display:"flex",alignItems:"center",gap:"10px",padding:"14px 24px",border:"none",borderRadius:"50px",cursor:"pointer",fontFamily:"'Quicksand', 'Nunito', system-ui, sans-serif",fontSize:"15px",fontWeight:600,color:"#fff",zIndex:9998,backgroundColor:n,boxShadow:c?`0 8px 32px ${n}60, 0 0 20px ${n}40`:`0 4px 20px ${n}50, 0 0 10px ${n}30`,transform:d(),transition:"all 0.2s cubic-bezier(0.34, 1.56, 0.64, 1)"},children:[jsx(ce,{}),jsx("span",{children:o})]})}function ge({size:r=24}){return jsx("svg",{width:r,height:r,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",children:jsx("path",{d:"M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"})})}function fe(){return jsxs("svg",{width:"18",height:"18",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",children:[jsx("path",{d:"M8 2l1.88 1.88M14.12 3.88L16 2M9 7.13v-1a3.003 3.003 0 116 0v1"}),jsx("path",{d:"M12 20c-3.3 0-6-2.7-6-6v-3a4 4 0 014-4h4a4 4 0 014 4v3c0 3.3-2.7 6-6 6"}),jsx("path",{d:"M12 20v-9M6.53 9C4.6 8.8 3 7.1 3 5M6 13H2M6 17l-4 1M17.47 9c1.93-.2 3.53-1.9 3.53-4M18 13h4M18 17l4 1"})]})}function he(){return jsxs("svg",{width:"18",height:"18",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",children:[jsx("path",{d:"M15 14c.2-1 .7-1.7 1.5-2.5 1-.9 1.5-2.2 1.5-3.5A6 6 0 006 8c0 1 .2 2.2 1.5 3.5.7.7 1.3 1.5 1.5 2.5"}),jsx("path",{d:"M9 18h6M10 22h4"})]})}function me(){return jsxs("svg",{width:"18",height:"18",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",children:[jsx("rect",{x:"3",y:"3",width:"18",height:"18",rx:"2",ry:"2"}),jsx("circle",{cx:"8.5",cy:"8.5",r:"1.5"}),jsx("polyline",{points:"21 15 16 10 5 21"})]})}function N(){return jsxs("svg",{width:"20",height:"20",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",children:[jsx("line",{x1:"18",y1:"6",x2:"6",y2:"18"}),jsx("line",{x1:"6",y1:"6",x2:"18",y2:"18"})]})}function be(){return jsx("svg",{width:"24",height:"24",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",children:jsx("polyline",{points:"20 6 9 17 4 12"})})}var i={bgLight:"#F8FAFC",bgCard:"rgba(255, 255, 255, 0.98)",border:"rgba(0, 0, 0, 0.08)",text:"#1F2937",textMuted:"#6B7280",error:"#EF4444",success:"#22C55E"};function z({config:r,metadata:o,onClose:e,onSubmit:n,onUpload:c}){let[l,u]=useState(null),[s,p]=useState(""),[d,y]=useState(""),[k,x]=useState(null),[w,v]=useState(null),[j,R]=useState(false),[W,B]=useState(false),[$,m]=useState(null),[re,ne]=useState(false),P=useRef(null),{i18n:C,formFields:J,theme:ie}=r,S=ie.primaryColor||"#0ea5e9",H=400,ae=l==="bug"?C.bugPlaceholder||C.placeholder:C.featurePlaceholder||C.placeholder,se=async f=>{let F=f.target.files?.[0];if(F){if(!F.type.startsWith("image/")){m("Only image files are allowed");return}if(F.size>10*1024*1024){m("Image must be under 10MB");return}B(true),m(null);try{let M=await ye(F,{maxWidth:1920,maxHeight:1080,quality:.8}),D=new FileReader;D.onload=()=>v(D.result),D.readAsDataURL(M);let U=await c(M);B(!1),U?x(U):(m("Failed to upload image"),v(null));}catch(M){console.error("[Voyage] Image processing error:",M),B(false),m("Failed to process image");}}},le=()=>{x(null),v(null),P.current&&(P.current.value="");},de=async()=>{if(!l){m("Please select a type");return}if(!s.trim()){m("Please enter your feedback");return}if(J.email.required&&!d.trim()){m("Please enter your email");return}R(true),m(null);let f={label:l,text:s.trim(),email:d.trim()||void 0,imageUrl:k||void 0,metadata:o},h=await n(f);R(false),h?ne(true):m("Failed to submit. Please try again.");},g=(()=>{let f=`${S}15`;return {overlay:{position:"fixed",top:0,left:0,right:0,bottom:0,backgroundColor:"rgba(0, 0, 0, 0.4)",backdropFilter:"blur(4px)",display:"flex",alignItems:"center",justifyContent:"center",zIndex:9999,padding:"20px"},modal:{backgroundColor:i.bgCard,backdropFilter:"blur(20px)",borderRadius:"20px",width:"100%",maxWidth:"580px",maxHeight:"90vh",overflowY:"auto",position:"relative",fontFamily:"'Quicksand', 'Nunito', system-ui, sans-serif",border:`1px solid ${i.border}`,boxShadow:"0 25px 50px -12px rgba(0, 0, 0, 0.15)"},colorBar:{height:"4px",backgroundColor:S},header:{display:"flex",alignItems:"center",justifyContent:"space-between",padding:"14px 20px",borderBottom:`1px solid ${i.border}`,backgroundColor:i.bgLight},headerIcon:{width:"32px",height:"32px",borderRadius:"10px",backgroundColor:S,display:"flex",alignItems:"center",justifyContent:"center",color:"#fff"},typeButton:h=>({display:"flex",alignItems:"center",justifyContent:"center",gap:"6px",padding:"10px 12px",border:h?`2px solid ${S}`:`1px solid ${i.border}`,borderRadius:"10px",backgroundColor:h?f:i.bgLight,cursor:"pointer",fontWeight:600,fontSize:"13px",color:h?i.text:i.textMuted,transition:"all 0.2s"}),submitButton:h=>({width:"100%",padding:"12px",backgroundColor:h?i.bgLight:S,color:h?i.textMuted:"#fff",border:"none",borderRadius:"12px",fontSize:"15px",fontWeight:700,cursor:h?"not-allowed":"pointer",fontFamily:"inherit",transition:"all 0.2s",boxShadow:h?"none":`0 4px 15px ${S}40`}),primaryButton:{width:"100%",padding:"16px",backgroundColor:S,color:"#fff",border:"none",borderRadius:"12px",fontSize:"15px",fontWeight:700,cursor:"pointer",fontFamily:"inherit",marginTop:"8px"}}})();return re?jsx("div",{style:g.overlay,children:jsx("div",{style:g.modal,children:jsxs("div",{style:Ee,children:[jsx("div",{style:Ve,children:jsx(be,{})}),jsx("h3",{style:Te,children:C.successMessage}),jsx("p",{style:Oe,children:"Your voice shapes what we build next."}),jsx("button",{onClick:e,style:g.primaryButton,children:"Done"})]})})}):jsx("div",{style:g.overlay,onClick:e,children:jsxs("div",{style:g.modal,onClick:f=>f.stopPropagation(),children:[jsxs("div",{style:g.header,children:[jsxs("div",{style:xe,children:[jsx("div",{style:g.headerIcon,children:jsx(ge,{size:20})}),jsx("span",{style:Se,children:"Help Us Improve"})]}),jsx("button",{onClick:e,style:Ce,children:jsx(N,{})})]}),jsx("div",{style:g.colorBar}),jsxs("div",{style:ve,children:[jsxs("div",{style:_,children:[jsx("label",{style:L,children:"Feedback Type"}),jsxs("div",{style:ke,children:[jsxs("button",{onClick:()=>u("bug"),style:g.typeButton(l==="bug"),children:[jsx("span",{style:q(l==="bug","bug"),children:jsx(fe,{})}),jsx("span",{children:"Bug"})]}),jsxs("button",{onClick:()=>u("feature"),style:g.typeButton(l==="feature"),children:[jsx("span",{style:q(l==="feature","feature"),children:jsx(he,{})}),jsx("span",{children:"Feature"})]})]})]}),jsxs("div",{style:we,children:[jsxs("div",{style:Pe,children:[jsx("label",{style:L,children:"Description"}),jsxs("div",{style:Le,children:[jsx("textarea",{placeholder:ae,value:s,maxLength:H,onInput:f=>p(f.target.value),style:je}),jsxs("span",{style:We,children:[s.length,"/",H]})]})]}),jsxs("div",{style:Fe,children:[jsx("label",{style:L,children:"Screenshot"}),w?jsxs("div",{style:Je,children:[jsx("img",{src:w,alt:"Preview",style:Ie}),jsx("button",{onClick:le,style:De,children:jsx(N,{})}),W&&jsx("div",{style:Xe,children:"..."})]}):jsxs("button",{onClick:()=>P.current?.click(),style:Me,children:[jsx(me,{}),jsx("span",{style:{fontSize:"11px"},children:"Upload"})]}),jsx("input",{ref:P,type:"file",accept:"image/*",onChange:se,style:{display:"none"}})]})]}),J.email.enabled&&jsxs("div",{style:_,children:[jsxs("label",{style:L,children:["Email ",J.email.required?"":"(optional)"]}),jsx("input",{type:"email",placeholder:"your@email.com",value:d,onInput:f=>y(f.target.value),style:Be})]}),$&&jsx("p",{style:ze,children:$}),jsx("button",{onClick:de,disabled:j||W,style:g.submitButton(j||W),children:j?jsxs("span",{style:Re,children:[jsx("span",{className:"voyage-spinner"}),"Sending..."]}):C.submitLabel})]})]})})}async function ye(r,o){let{maxWidth:e,maxHeight:n,quality:c}=o;return new Promise((l,u)=>{let s=new Image;s.onload=()=>{let{width:p,height:d}=s;if(p>e||d>n){let x=Math.min(e/p,n/d);p=Math.round(p*x),d=Math.round(d*x);}let y=document.createElement("canvas");y.width=p,y.height=d;let k=y.getContext("2d");if(!k){u(new Error("Failed to get canvas context"));return}k.drawImage(s,0,0,p,d),y.toBlob(x=>{if(!x){u(new Error("Failed to convert image"));return}let w=Date.now(),v=new File([x],`image_${w}.webp`,{type:"image/webp"});l(v);},"image/webp",c);},s.onerror=()=>u(new Error("Failed to load image")),s.src=URL.createObjectURL(r);})}var xe={display:"flex",alignItems:"center",gap:"12px"},Se={fontSize:"16px",fontWeight:700,color:i.text},Ce={background:"transparent",border:"none",color:i.textMuted,cursor:"pointer",padding:"8px",borderRadius:"8px",display:"flex",alignItems:"center",justifyContent:"center",transition:"all 0.2s"},ve={padding:"16px 20px"},_={marginBottom:"14px"},L={display:"block",fontSize:"13px",fontWeight:600,color:i.text,marginBottom:"8px"},ke={display:"grid",gridTemplateColumns:"1fr 1fr",gap:"12px"},we={display:"flex",gap:"14px",marginBottom:"14px"},Pe={flex:1,display:"flex",flexDirection:"column"},Fe={width:"120px",flexShrink:0,display:"flex",flexDirection:"column"},Me={width:"100%",aspectRatio:"1",border:`2px dashed ${i.border}`,borderRadius:"10px",backgroundColor:"transparent",cursor:"pointer",display:"flex",flexDirection:"column",alignItems:"center",justifyContent:"center",gap:"4px",color:i.textMuted,fontSize:"12px",transition:"all 0.2s"},Ie={width:"100%",aspectRatio:"1",objectFit:"cover",borderRadius:"10px"},q=(r,o)=>({display:"flex",alignItems:"center",justifyContent:"center",color:r?o==="bug"?i.error:"#F59E0B":i.textMuted}),Le={position:"relative"},je={width:"100%",minHeight:"120px",padding:"14px",border:`1px solid ${i.border}`,borderRadius:"12px",fontSize:"14px",resize:"vertical",boxSizing:"border-box",backgroundColor:i.bgLight,color:i.text,fontFamily:"inherit",outline:"none"},We={position:"absolute",bottom:"12px",right:"14px",fontSize:"12px",color:i.textMuted},Be={width:"100%",padding:"10px 12px",border:`1px solid ${i.border}`,borderRadius:"10px",fontSize:"13px",boxSizing:"border-box",backgroundColor:i.bgLight,color:i.text,fontFamily:"inherit",outline:"none"},Je={position:"relative",display:"inline-block",borderRadius:"12px",overflow:"hidden"},De={position:"absolute",top:"8px",right:"8px",width:"28px",height:"28px",borderRadius:"50%",backgroundColor:"rgba(0, 0, 0, 0.7)",color:"#fff",border:"none",cursor:"pointer",display:"flex",alignItems:"center",justifyContent:"center"},Xe={position:"absolute",inset:0,backgroundColor:"rgba(0, 0, 0, 0.7)",display:"flex",alignItems:"center",justifyContent:"center",color:"#fff",fontSize:"14px",borderRadius:"12px"},ze={color:i.error,fontSize:"14px",marginBottom:"16px",padding:"12px",backgroundColor:"rgba(239, 68, 68, 0.1)",borderRadius:"8px",border:"1px solid rgba(239, 68, 68, 0.3)"},Ee={padding:"48px 24px",textAlign:"center"},Ve={width:"64px",height:"64px",borderRadius:"50%",backgroundColor:"rgba(34, 197, 94, 0.15)",color:i.success,display:"flex",alignItems:"center",justifyContent:"center",margin:"0 auto 20px"},Te={fontSize:"20px",fontWeight:700,color:i.text,marginBottom:"8px"},Oe={fontSize:"14px",color:i.textMuted,marginBottom:"24px"},Re={display:"flex",alignItems:"center",justifyContent:"center",gap:"8px"};function E({config:r,onSubmit:o,onUpload:e,getMetadata:n,visible:c,defaultOpen:l=false,onOpenChange:u}){let[s,p]=useState(l);if(useEffect(()=>{u?.(s);},[s,u]),!c)return null;let{theme:d,i18n:y}=r;return jsxs(Fragment,{children:[!s&&jsx(X,{onClick:()=>p(true),label:y.buttonLabel,position:d.position,primaryColor:d.primaryColor}),s&&jsx(z,{config:r,metadata:n(),onClose:()=>p(false),onSubmit:o,onUpload:e})]})}var Q=()=>{if(typeof document>"u"||document.getElementById("voyage-sdk-styles"))return;let r=document.createElement("style");r.id="voyage-sdk-styles",r.textContent=`
2
+ /* Voyage SDK Styles */
3
+
4
+ /* Spinner Animation */
5
+ @keyframes voyage-spin {
6
+ from { transform: rotate(0deg); }
7
+ to { transform: rotate(360deg); }
8
+ }
9
+
10
+ #voyage-widget-container * {
11
+ box-sizing: border-box;
12
+ }
13
+
14
+ #voyage-widget-container button {
15
+ font-family: 'Quicksand', 'Nunito', system-ui, sans-serif;
16
+ }
17
+
18
+ #voyage-widget-container textarea:focus,
19
+ #voyage-widget-container input:focus {
20
+ border-color: rgba(124, 58, 237, 0.5);
21
+ box-shadow: 0 0 0 3px rgba(124, 58, 237, 0.1);
22
+ }
23
+
24
+ #voyage-widget-container button:focus {
25
+ outline: none;
26
+ }
27
+
28
+ /* Spinner */
29
+ .voyage-spinner {
30
+ width: 16px;
31
+ height: 16px;
32
+ border: 2px solid rgba(255,255,255,0.3);
33
+ border-top-color: #fff;
34
+ border-radius: 50%;
35
+ animation: voyage-spin 0.8s linear infinite;
36
+ }
37
+
38
+ /* Load Quicksand font if not present */
39
+ @import url('https://fonts.googleapis.com/css2?family=Quicksand:wght@400;500;600;700&display=swap');
40
+ `,document.head.appendChild(r);};var V="https://rxflzurkkuqgopjkshjh.supabase.co/functions/v1",G="voyage_config";async function T(r){let o=Y(r);if(o)return o;try{let e=await fetch(`${V}/get-widget-config`,{method:"GET",headers:{"Content-Type":"application/json","x-sdk-key":r}});if(!e.ok)return console.error("[Voyage] Failed to fetch config:",e.status),null;let n=await e.json();return Ne(r,n),n}catch(e){return console.error("[Voyage] Network error:",e),Y(r,true)}}async function Z(r,o){try{let e=await fetch(`${V}/submit-feedback`,{method:"POST",headers:{"Content-Type":"application/json","x-sdk-key":r},body:JSON.stringify({label:o.label,content:o.text,email:o.email,imageUrl:o.imageUrl,metadata:o.metadata})});if(!e.ok){let n=await e.json().catch(()=>({}));return console.error("[Voyage] Failed to submit feedback:",e.status,n),!1}return !0}catch(e){return console.error("[Voyage] Network error:",e),false}}async function ee(r,o){if(!o.type.startsWith("image/"))return console.error("[Voyage] Only image files are allowed"),null;if(o.size>5*1024*1024)return console.error("[Voyage] Image must be under 5MB"),null;try{let e=new FormData;e.append("file",o);let n=await fetch(`${V}/upload-feedback-image`,{method:"POST",headers:{"x-sdk-key":r},body:e});if(!n.ok){let l=await n.json().catch(()=>({}));return console.error("[Voyage] Failed to upload image:",n.status,l),null}return (await n.json()).url}catch(e){return console.error("[Voyage] Network error:",e),null}}function Y(r,o=false){try{let e=localStorage.getItem(`${G}_${r}`);if(!e)return null;let n=JSON.parse(e);return Date.now()-n.timestamp>36e5&&!o?null:n.config}catch{return null}}function Ne(r,o){try{let e={config:o,timestamp:Date.now()};localStorage.setItem(`${G}_${r}`,JSON.stringify(e));}catch{}}function te(){return {url:window.location.href,pathname:window.location.pathname,browser:navigator.userAgent,viewport:`${window.innerWidth}x${window.innerHeight}`,title:document.title,timestamp:new Date().toISOString(),locale:navigator.language,referrer:document.referrer}}var qe={projectId:"",formFields:{email:{enabled:true,required:false},category:{enabled:true,options:["bug","feature"]}},theme:{primaryColor:"#0ea5e9",position:"bottom-right"},i18n:{buttonLabel:"To : the Maker",placeholder:"Describe your ideas to improve our product",bugPlaceholder:"Please describe the bug in detail (e.g., clicked a button and it didn't work)",featurePlaceholder:"Describe your ideas to improve our product (e.g., I want to export TASK.md to Linear tickets)",submitLabel:"Send to the Maker",successMessage:"Thank you for your feedback!"}},O=class{constructor(){this.config=null;this.serverConfig=null;this.container=null;this.isWidgetVisible=false;this.isModalOpen=false;}async init(o){if(this.config=o,!o.sdkKey){console.error("[Voyage] Invalid SDK Key");return}if(Q(),!this.shouldDisplay()){console.log("[Voyage] Widget hidden by display rules");return}let e=await T(o.sdkKey);this.serverConfig=this.mergeConfig(e,o),this.isWidgetVisible=true,this.renderWidget(),console.log("[Voyage] Initialized");}show(){if(!this.config){console.warn("[Voyage] SDK not initialized");return}this.isWidgetVisible=true,this.renderWidget();}hide(){this.isWidgetVisible=false,this.renderWidget();}open(){if(!this.config){console.warn("[Voyage] SDK not initialized");return}this.isModalOpen=true,this.renderWidget();}close(){this.isModalOpen=false,this.renderWidget();}isOpen(){return this.isModalOpen}isVisible(){return this.isWidgetVisible}async refreshConfig(){if(!this.config)return;let o=await T(this.config.sdkKey);this.serverConfig=this.mergeConfig(o,this.config),this.renderWidget();}destroy(){this.container&&(render(null,this.container),this.container.remove(),this.container=null),this.config=null,this.serverConfig=null,this.isWidgetVisible=false,this.isModalOpen=false;}renderWidget(){if(!this.serverConfig||!this.config)return;this.container||(this.container=document.createElement("div"),this.container.id="voyage-widget-container",document.body.appendChild(this.container));let o=this.config.sdkKey;render(h(E,{config:this.serverConfig,visible:this.isWidgetVisible,defaultOpen:this.isModalOpen,onOpenChange:e=>{this.isModalOpen=e;},getMetadata:te,onSubmit:async e=>Z(o,e),onUpload:async e=>ee(o,e)}),this.container);}mergeConfig(o,e){return o||{...qe}}shouldDisplay(){if(!this.config)return false;let o=window.location.pathname,{include:e,exclude:n}=this.config;return e&&e.length>0?e.some(c=>this.matchPath(o,c)):n&&n.length>0?!n.some(c=>this.matchPath(o,c)):true}matchPath(o,e){if(e.endsWith("/*")){let n=e.slice(0,-2);return o.startsWith(n)}return o===e}},Ke=new O;var St=Ke;
41
+ export{Ke as Voyage,St as default};//# sourceMappingURL=index.js.map
42
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/Button.tsx","../src/components/Modal.tsx","../src/components/Widget.tsx","../src/styles.ts","../src/utils/api.ts","../src/utils/metadata.ts","../src/index.ts"],"names":["MessageIcon","jsx","Button","onClick","label","position","primaryColor","isHovered","setIsHovered","useState","isPressed","setIsPressed","positionStyle","getTransform","jsxs","size","BugIcon","LightbulbIcon","ImageIcon","CloseIcon","CheckIcon","colors","Modal","config","metadata","onClose","onSubmit","onUpload","setLabel","text","setText","email","setEmail","imageUrl","setImageUrl","imagePreview","setImagePreview","isSubmitting","setIsSubmitting","isUploading","setIsUploading","error","setError","success","setSuccess","fileInputRef","useRef","i18n","formFields","theme","maxLength","currentPlaceholder","handleFileSelect","e","file","processedFile","processImage","reader","url","err","handleRemoveImage","handleSubmit","feedbackData","result","styles","primaryBgLight","active","disabled","successContainerStyle","successIconStyle","successTitleStyle","successTextStyle","headerLeftStyle","headerTitleStyle","closeButtonStyle","contentStyle","sectionStyle","labelStyle","typeButtonsStyle","typeIconStyle","descriptionRowStyle","descriptionColStyle","textareaWrapperStyle","textareaStyle","charCountStyle","uploadColStyle","imagePreviewContainerStyle","imagePreviewSquareStyle","removeImageButtonStyle","uploadingOverlayStyle","uploadButtonSquareStyle","inputStyle","errorStyle","spinnerContainerStyle","options","maxWidth","maxHeight","quality","resolve","reject","img","width","height","ratio","canvas","ctx","blob","timestamp","newFile","type","Widget","getMetadata","visible","defaultOpen","onOpenChange","isOpen","setIsOpen","useEffect","Fragment","injectStyles","style","API_BASE","CACHE_KEY","fetchProjectConfig","sdkKey","cached","getFromCache","response","saveToCache","submitFeedback","data","errorData","uploadImage","formData","ignoreExpiry","raw","captureMetadata","DEFAULT_CONFIG","VoyageSDK","fetchedConfig","render","h","open","server","_client","pathname","include","exclude","pattern","prefix","Voyage","index_default"],"mappings":"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","file":"index.js","sourcesContent":["import { h } from 'preact';\nimport { useState } from 'preact/hooks';\n\ninterface ButtonProps {\n onClick: () => void;\n label: string;\n position: 'bottom-right' | 'bottom-left';\n primaryColor: string;\n}\n\n// 말풍선 아이콘 SVG\nfunction MessageIcon() {\n return (\n <svg\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <path d=\"M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z\" />\n </svg>\n );\n}\n\nexport function Button({ onClick, label, position, primaryColor }: ButtonProps) {\n const [isHovered, setIsHovered] = useState(false);\n const [isPressed, setIsPressed] = useState(false);\n\n const positionStyle = position === 'bottom-right'\n ? { right: '24px' }\n : { left: '24px' };\n\n // Jelly 효과 transform\n const getTransform = () => {\n if (isPressed) return 'scale(0.95)';\n if (isHovered) return 'scale(1.02) translateY(-2px)';\n return 'scale(1)';\n };\n\n return (\n <button\n onClick={onClick}\n onMouseEnter={() => setIsHovered(true)}\n onMouseLeave={() => { setIsHovered(false); setIsPressed(false); }}\n onMouseDown={() => setIsPressed(true)}\n onMouseUp={() => setIsPressed(false)}\n style={{\n position: 'fixed',\n bottom: '24px',\n ...positionStyle,\n display: 'flex',\n alignItems: 'center',\n gap: '10px',\n padding: '14px 24px',\n border: 'none',\n borderRadius: '50px',\n cursor: 'pointer',\n fontFamily: \"'Quicksand', 'Nunito', system-ui, sans-serif\",\n fontSize: '15px',\n fontWeight: 600,\n color: '#fff',\n zIndex: 9998,\n // primaryColor 적용\n backgroundColor: primaryColor,\n // 그림자 + glow 효과\n boxShadow: isHovered\n ? `0 8px 32px ${primaryColor}60, 0 0 20px ${primaryColor}40`\n : `0 4px 20px ${primaryColor}50, 0 0 10px ${primaryColor}30`,\n // Jelly 애니메이션\n transform: getTransform(),\n transition: 'all 0.2s cubic-bezier(0.34, 1.56, 0.64, 1)',\n }}\n >\n <MessageIcon />\n <span>{label}</span>\n </button>\n );\n}\n","import { h } from 'preact';\nimport { useRef, useState } from 'preact/hooks';\n\nimport type { ProjectConfig, FeedbackData, FeedbackMetadata } from '../types';\n\ninterface ModalProps {\n config: ProjectConfig;\n metadata: FeedbackMetadata;\n onClose: () => void;\n onSubmit: (data: FeedbackData) => Promise<boolean>;\n onUpload: (file: File) => Promise<string | null>;\n}\n\n// 아이콘들\nfunction MessageIcon({ size = 24 }: { size?: number }) {\n return (\n <svg width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <path d=\"M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z\" />\n </svg>\n );\n}\n\nfunction BugIcon() {\n return (\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <path d=\"M8 2l1.88 1.88M14.12 3.88L16 2M9 7.13v-1a3.003 3.003 0 116 0v1\" />\n <path d=\"M12 20c-3.3 0-6-2.7-6-6v-3a4 4 0 014-4h4a4 4 0 014 4v3c0 3.3-2.7 6-6 6\" />\n <path d=\"M12 20v-9M6.53 9C4.6 8.8 3 7.1 3 5M6 13H2M6 17l-4 1M17.47 9c1.93-.2 3.53-1.9 3.53-4M18 13h4M18 17l4 1\" />\n </svg>\n );\n}\n\nfunction LightbulbIcon() {\n return (\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <path d=\"M15 14c.2-1 .7-1.7 1.5-2.5 1-.9 1.5-2.2 1.5-3.5A6 6 0 006 8c0 1 .2 2.2 1.5 3.5.7.7 1.3 1.5 1.5 2.5\" />\n <path d=\"M9 18h6M10 22h4\" />\n </svg>\n );\n}\n\nfunction ImageIcon() {\n return (\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"2\" ry=\"2\" />\n <circle cx=\"8.5\" cy=\"8.5\" r=\"1.5\" />\n <polyline points=\"21 15 16 10 5 21\" />\n </svg>\n );\n}\n\nfunction CloseIcon() {\n return (\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\" />\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\" />\n </svg>\n );\n}\n\nfunction CheckIcon() {\n return (\n <svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <polyline points=\"20 6 9 17 4 12\" />\n </svg>\n );\n}\n\n// 라이트 테마 색상\nconst colors = {\n bg: '#FFFFFF',\n bgLight: '#F8FAFC',\n bgCard: 'rgba(255, 255, 255, 0.98)',\n border: 'rgba(0, 0, 0, 0.08)',\n borderLight: 'rgba(0, 0, 0, 0.12)',\n text: '#1F2937',\n textMuted: '#6B7280',\n error: '#EF4444',\n success: '#22C55E',\n};\n\nexport function Modal({ config, metadata, onClose, onSubmit, onUpload }: ModalProps) {\n const [label, setLabel] = useState<'bug' | 'feature' | null>(null);\n const [text, setText] = useState('');\n const [email, setEmail] = useState('');\n const [imageUrl, setImageUrl] = useState<string | null>(null);\n const [imagePreview, setImagePreview] = useState<string | null>(null);\n const [isSubmitting, setIsSubmitting] = useState(false);\n const [isUploading, setIsUploading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [success, setSuccess] = useState(false);\n const fileInputRef = useRef<HTMLInputElement>(null);\n\n const { i18n, formFields, theme } = config;\n const primaryColor = theme.primaryColor || '#0ea5e9';\n const maxLength = 400;\n\n // 동적 placeholder\n const currentPlaceholder = label === 'bug'\n ? (i18n.bugPlaceholder || i18n.placeholder)\n : (i18n.featurePlaceholder || i18n.placeholder);\n\n const handleFileSelect = async (e: Event) => {\n const target = e.target as HTMLInputElement;\n const file = target.files?.[0];\n if (!file) return;\n\n if (!file.type.startsWith('image/')) {\n setError('Only image files are allowed');\n return;\n }\n if (file.size > 10 * 1024 * 1024) {\n setError('Image must be under 10MB');\n return;\n }\n\n setIsUploading(true);\n setError(null);\n\n try {\n // 이미지를 WebP로 변환 및 resize\n const processedFile = await processImage(file, {\n maxWidth: 1920,\n maxHeight: 1080,\n quality: 0.8,\n });\n\n // 프리뷰 생성\n const reader = new FileReader();\n reader.onload = () => setImagePreview(reader.result as string);\n reader.readAsDataURL(processedFile);\n\n const url = await onUpload(processedFile);\n setIsUploading(false);\n\n if (url) {\n setImageUrl(url);\n } else {\n setError('Failed to upload image');\n setImagePreview(null);\n }\n } catch (err) {\n console.error('[Voyage] Image processing error:', err);\n setIsUploading(false);\n setError('Failed to process image');\n }\n };\n\n const handleRemoveImage = () => {\n setImageUrl(null);\n setImagePreview(null);\n if (fileInputRef.current) {\n fileInputRef.current.value = '';\n }\n };\n\n const handleSubmit = async () => {\n if (!label) {\n setError('Please select a type');\n return;\n }\n if (!text.trim()) {\n setError('Please enter your feedback');\n return;\n }\n if (formFields.email.required && !email.trim()) {\n setError('Please enter your email');\n return;\n }\n\n setIsSubmitting(true);\n setError(null);\n\n const feedbackData: FeedbackData = {\n label,\n text: text.trim(),\n email: email.trim() || undefined,\n imageUrl: imageUrl || undefined,\n metadata,\n };\n\n const result = await onSubmit(feedbackData);\n setIsSubmitting(false);\n\n if (result) {\n setSuccess(true);\n } else {\n setError('Failed to submit. Please try again.');\n }\n };\n\n // 스타일 함수 (primaryColor 적용)\n const getStyles = () => {\n const primaryBgLight = `${primaryColor}15`;\n\n return {\n overlay: {\n position: 'fixed',\n top: 0,\n left: 0,\n right: 0,\n bottom: 0,\n backgroundColor: 'rgba(0, 0, 0, 0.4)',\n backdropFilter: 'blur(4px)',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n zIndex: 9999,\n padding: '20px',\n } as h.JSX.CSSProperties,\n\n modal: {\n backgroundColor: colors.bgCard,\n backdropFilter: 'blur(20px)',\n borderRadius: '20px',\n width: '100%',\n maxWidth: '580px',\n maxHeight: '90vh',\n overflowY: 'auto',\n position: 'relative',\n fontFamily: \"'Quicksand', 'Nunito', system-ui, sans-serif\",\n border: `1px solid ${colors.border}`,\n boxShadow: '0 25px 50px -12px rgba(0, 0, 0, 0.15)',\n } as h.JSX.CSSProperties,\n\n colorBar: {\n height: '4px',\n backgroundColor: primaryColor,\n } as h.JSX.CSSProperties,\n\n header: {\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n padding: '14px 20px',\n borderBottom: `1px solid ${colors.border}`,\n backgroundColor: colors.bgLight,\n } as h.JSX.CSSProperties,\n\n headerIcon: {\n width: '32px',\n height: '32px',\n borderRadius: '10px',\n backgroundColor: primaryColor,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n color: '#fff',\n } as h.JSX.CSSProperties,\n\n typeButton: (active: boolean) => ({\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n gap: '6px',\n padding: '10px 12px',\n border: active ? `2px solid ${primaryColor}` : `1px solid ${colors.border}`,\n borderRadius: '10px',\n backgroundColor: active ? primaryBgLight : colors.bgLight,\n cursor: 'pointer',\n fontWeight: 600,\n fontSize: '13px',\n color: active ? colors.text : colors.textMuted,\n transition: 'all 0.2s',\n } as h.JSX.CSSProperties),\n\n submitButton: (disabled: boolean) => ({\n width: '100%',\n padding: '12px',\n backgroundColor: disabled ? colors.bgLight : primaryColor,\n color: disabled ? colors.textMuted : '#fff',\n border: 'none',\n borderRadius: '12px',\n fontSize: '15px',\n fontWeight: 700,\n cursor: disabled ? 'not-allowed' : 'pointer',\n fontFamily: 'inherit',\n transition: 'all 0.2s',\n boxShadow: disabled ? 'none' : `0 4px 15px ${primaryColor}40`,\n } as h.JSX.CSSProperties),\n\n primaryButton: {\n width: '100%',\n padding: '16px',\n backgroundColor: primaryColor,\n color: '#fff',\n border: 'none',\n borderRadius: '12px',\n fontSize: '15px',\n fontWeight: 700,\n cursor: 'pointer',\n fontFamily: 'inherit',\n marginTop: '8px',\n } as h.JSX.CSSProperties,\n };\n };\n\n const styles = getStyles();\n\n // 성공 화면\n if (success) {\n return (\n <div style={styles.overlay}>\n <div style={styles.modal}>\n <div style={successContainerStyle}>\n <div style={successIconStyle}>\n <CheckIcon />\n </div>\n <h3 style={successTitleStyle}>{i18n.successMessage}</h3>\n <p style={successTextStyle}>Your voice shapes what we build next.</p>\n <button onClick={onClose} style={styles.primaryButton}>\n Done\n </button>\n </div>\n </div>\n </div>\n );\n }\n\n return (\n <div style={styles.overlay} onClick={onClose}>\n <div style={styles.modal} onClick={(e) => e.stopPropagation()}>\n {/* 헤더 */}\n <div style={styles.header}>\n <div style={headerLeftStyle}>\n <div style={styles.headerIcon}>\n <MessageIcon size={20} />\n </div>\n <span style={headerTitleStyle}>Help Us Improve</span>\n </div>\n <button onClick={onClose} style={closeButtonStyle}>\n <CloseIcon />\n </button>\n </div>\n\n {/* 컬러 바 */}\n <div style={styles.colorBar} />\n\n {/* 컨텐츠 */}\n <div style={contentStyle}>\n {/* 이슈 타입 */}\n <div style={sectionStyle}>\n <label style={labelStyle}>Feedback Type</label>\n <div style={typeButtonsStyle}>\n <button\n onClick={() => setLabel('bug')}\n style={styles.typeButton(label === 'bug')}\n >\n <span style={typeIconStyle(label === 'bug', 'bug')}>\n <BugIcon />\n </span>\n <span>Bug</span>\n </button>\n <button\n onClick={() => setLabel('feature')}\n style={styles.typeButton(label === 'feature')}\n >\n <span style={typeIconStyle(label === 'feature', 'feature')}>\n <LightbulbIcon />\n </span>\n <span>Feature</span>\n </button>\n </div>\n </div>\n\n {/* 설명 + 이미지 (flex row) */}\n <div style={descriptionRowStyle}>\n {/* 텍스트 영역 */}\n <div style={descriptionColStyle}>\n <label style={labelStyle}>Description</label>\n <div style={textareaWrapperStyle}>\n <textarea\n placeholder={currentPlaceholder}\n value={text}\n maxLength={maxLength}\n onInput={(e) => setText((e.target as HTMLTextAreaElement).value)}\n style={textareaStyle}\n />\n <span style={charCountStyle}>{text.length}/{maxLength}</span>\n </div>\n </div>\n\n {/* 이미지 업로드 (정사각형) */}\n <div style={uploadColStyle}>\n <label style={labelStyle}>Screenshot</label>\n {imagePreview ? (\n <div style={imagePreviewContainerStyle}>\n <img src={imagePreview} alt=\"Preview\" style={imagePreviewSquareStyle} />\n <button onClick={handleRemoveImage} style={removeImageButtonStyle}>\n <CloseIcon />\n </button>\n {isUploading && <div style={uploadingOverlayStyle}>...</div>}\n </div>\n ) : (\n <button\n onClick={() => fileInputRef.current?.click()}\n style={uploadButtonSquareStyle}\n >\n <ImageIcon />\n <span style={{ fontSize: '11px' }}>Upload</span>\n </button>\n )}\n <input\n ref={fileInputRef}\n type=\"file\"\n accept=\"image/*\"\n onChange={handleFileSelect}\n style={{ display: 'none' }}\n />\n </div>\n </div>\n\n {/* 이메일 */}\n {formFields.email.enabled && (\n <div style={sectionStyle}>\n <label style={labelStyle}>\n Email {formFields.email.required ? '' : '(optional)'}\n </label>\n <input\n type=\"email\"\n placeholder=\"your@email.com\"\n value={email}\n onInput={(e) => setEmail((e.target as HTMLInputElement).value)}\n style={inputStyle}\n />\n </div>\n )}\n\n {/* 에러 */}\n {error && <p style={errorStyle}>{error}</p>}\n\n {/* 제출 버튼 */}\n <button\n onClick={handleSubmit}\n disabled={isSubmitting || isUploading}\n style={styles.submitButton(isSubmitting || isUploading)}\n >\n {isSubmitting ? (\n <span style={spinnerContainerStyle}>\n <span className=\"voyage-spinner\" />\n Sending...\n </span>\n ) : (\n i18n.submitLabel\n )}\n </button>\n </div>\n </div>\n </div>\n );\n}\n\n// 이미지 처리 유틸: resize + WebP 변환\ninterface ProcessImageOptions {\n maxWidth: number;\n maxHeight: number;\n quality: number;\n}\n\nasync function processImage(file: File, options: ProcessImageOptions): Promise<File> {\n const { maxWidth, maxHeight, quality } = options;\n\n return new Promise((resolve, reject) => {\n const img = new Image();\n img.onload = () => {\n // 비율 유지하면서 resize 계산\n let { width, height } = img;\n\n if (width > maxWidth || height > maxHeight) {\n const ratio = Math.min(maxWidth / width, maxHeight / height);\n width = Math.round(width * ratio);\n height = Math.round(height * ratio);\n }\n\n // Canvas에 그리기\n const canvas = document.createElement('canvas');\n canvas.width = width;\n canvas.height = height;\n\n const ctx = canvas.getContext('2d');\n if (!ctx) {\n reject(new Error('Failed to get canvas context'));\n return;\n }\n\n ctx.drawImage(img, 0, 0, width, height);\n\n // WebP로 변환\n canvas.toBlob(\n (blob) => {\n if (!blob) {\n reject(new Error('Failed to convert image'));\n return;\n }\n\n // Blob을 File로 변환\n const timestamp = Date.now();\n const newFile = new File([blob], `image_${timestamp}.webp`, {\n type: 'image/webp',\n });\n\n resolve(newFile);\n },\n 'image/webp',\n quality\n );\n };\n\n img.onerror = () => reject(new Error('Failed to load image'));\n img.src = URL.createObjectURL(file);\n });\n}\n\n// 정적 스타일 정의 (라이트 테마)\nconst headerLeftStyle: h.JSX.CSSProperties = {\n display: 'flex',\n alignItems: 'center',\n gap: '12px',\n};\n\nconst headerTitleStyle: h.JSX.CSSProperties = {\n fontSize: '16px',\n fontWeight: 700,\n color: colors.text,\n};\n\nconst closeButtonStyle: h.JSX.CSSProperties = {\n background: 'transparent',\n border: 'none',\n color: colors.textMuted,\n cursor: 'pointer',\n padding: '8px',\n borderRadius: '8px',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n transition: 'all 0.2s',\n};\n\nconst contentStyle: h.JSX.CSSProperties = {\n padding: '16px 20px',\n};\n\nconst sectionStyle: h.JSX.CSSProperties = {\n marginBottom: '14px',\n};\n\nconst labelStyle: h.JSX.CSSProperties = {\n display: 'block',\n fontSize: '13px',\n fontWeight: 600,\n color: colors.text,\n marginBottom: '8px',\n};\n\nconst typeButtonsStyle: h.JSX.CSSProperties = {\n display: 'grid',\n gridTemplateColumns: '1fr 1fr',\n gap: '12px',\n};\n\nconst descriptionRowStyle: h.JSX.CSSProperties = {\n display: 'flex',\n gap: '14px',\n marginBottom: '14px',\n};\n\nconst descriptionColStyle: h.JSX.CSSProperties = {\n flex: 1,\n display: 'flex',\n flexDirection: 'column',\n};\n\nconst uploadColStyle: h.JSX.CSSProperties = {\n width: '120px',\n flexShrink: 0,\n display: 'flex',\n flexDirection: 'column',\n};\n\nconst uploadButtonSquareStyle: h.JSX.CSSProperties = {\n width: '100%',\n aspectRatio: '1',\n border: `2px dashed ${colors.border}`,\n borderRadius: '10px',\n backgroundColor: 'transparent',\n cursor: 'pointer',\n display: 'flex',\n flexDirection: 'column',\n alignItems: 'center',\n justifyContent: 'center',\n gap: '4px',\n color: colors.textMuted,\n fontSize: '12px',\n transition: 'all 0.2s',\n};\n\nconst imagePreviewSquareStyle: h.JSX.CSSProperties = {\n width: '100%',\n aspectRatio: '1',\n objectFit: 'cover',\n borderRadius: '10px',\n};\n\nconst typeIconStyle = (active: boolean, type: 'bug' | 'feature'): h.JSX.CSSProperties => ({\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n color: active\n ? (type === 'bug' ? colors.error : '#F59E0B')\n : colors.textMuted,\n});\n\nconst textareaWrapperStyle: h.JSX.CSSProperties = {\n position: 'relative',\n};\n\nconst textareaStyle: h.JSX.CSSProperties = {\n width: '100%',\n minHeight: '120px',\n padding: '14px',\n border: `1px solid ${colors.border}`,\n borderRadius: '12px',\n fontSize: '14px',\n resize: 'vertical',\n boxSizing: 'border-box',\n backgroundColor: colors.bgLight,\n color: colors.text,\n fontFamily: 'inherit',\n outline: 'none',\n};\n\nconst charCountStyle: h.JSX.CSSProperties = {\n position: 'absolute',\n bottom: '12px',\n right: '14px',\n fontSize: '12px',\n color: colors.textMuted,\n};\n\nconst inputStyle: h.JSX.CSSProperties = {\n width: '100%',\n padding: '10px 12px',\n border: `1px solid ${colors.border}`,\n borderRadius: '10px',\n fontSize: '13px',\n boxSizing: 'border-box',\n backgroundColor: colors.bgLight,\n color: colors.text,\n fontFamily: 'inherit',\n outline: 'none',\n};\n\nconst imagePreviewContainerStyle: h.JSX.CSSProperties = {\n position: 'relative',\n display: 'inline-block',\n borderRadius: '12px',\n overflow: 'hidden',\n};\n\nconst removeImageButtonStyle: h.JSX.CSSProperties = {\n position: 'absolute',\n top: '8px',\n right: '8px',\n width: '28px',\n height: '28px',\n borderRadius: '50%',\n backgroundColor: 'rgba(0, 0, 0, 0.7)',\n color: '#fff',\n border: 'none',\n cursor: 'pointer',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n};\n\nconst uploadingOverlayStyle: h.JSX.CSSProperties = {\n position: 'absolute',\n inset: 0,\n backgroundColor: 'rgba(0, 0, 0, 0.7)',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n color: '#fff',\n fontSize: '14px',\n borderRadius: '12px',\n};\n\nconst errorStyle: h.JSX.CSSProperties = {\n color: colors.error,\n fontSize: '14px',\n marginBottom: '16px',\n padding: '12px',\n backgroundColor: 'rgba(239, 68, 68, 0.1)',\n borderRadius: '8px',\n border: `1px solid rgba(239, 68, 68, 0.3)`,\n};\n\nconst successContainerStyle: h.JSX.CSSProperties = {\n padding: '48px 24px',\n textAlign: 'center',\n};\n\nconst successIconStyle: h.JSX.CSSProperties = {\n width: '64px',\n height: '64px',\n borderRadius: '50%',\n backgroundColor: 'rgba(34, 197, 94, 0.15)',\n color: colors.success,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n margin: '0 auto 20px',\n};\n\nconst successTitleStyle: h.JSX.CSSProperties = {\n fontSize: '20px',\n fontWeight: 700,\n color: colors.text,\n marginBottom: '8px',\n};\n\nconst successTextStyle: h.JSX.CSSProperties = {\n fontSize: '14px',\n color: colors.textMuted,\n marginBottom: '24px',\n};\n\nconst spinnerContainerStyle: h.JSX.CSSProperties = {\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n gap: '8px',\n};\n","import { h } from 'preact';\nimport { useState, useEffect } from 'preact/hooks';\nimport { Button } from './Button';\nimport { Modal } from './Modal';\nimport type { ProjectConfig, FeedbackData, FeedbackMetadata } from '../types';\n\ninterface WidgetProps {\n config: ProjectConfig;\n onSubmit: (data: FeedbackData) => Promise<boolean>;\n onUpload: (file: File) => Promise<string | null>;\n getMetadata: () => FeedbackMetadata;\n visible: boolean;\n defaultOpen?: boolean;\n onOpenChange?: (open: boolean) => void;\n}\n\nexport function Widget({\n config,\n onSubmit,\n onUpload,\n getMetadata,\n visible,\n defaultOpen = false,\n onOpenChange,\n}: WidgetProps) {\n const [isOpen, setIsOpen] = useState(defaultOpen);\n\n useEffect(() => {\n onOpenChange?.(isOpen);\n }, [isOpen, onOpenChange]);\n\n if (!visible) {\n return null;\n }\n\n const { theme, i18n } = config;\n\n return (\n <>\n {!isOpen && (\n <Button\n onClick={() => setIsOpen(true)}\n label={i18n.buttonLabel}\n position={theme.position}\n primaryColor={theme.primaryColor}\n />\n )}\n\n {isOpen && (\n <Modal\n config={config}\n metadata={getMetadata()}\n onClose={() => setIsOpen(false)}\n onSubmit={onSubmit}\n onUpload={onUpload}\n />\n )}\n </>\n );\n}\n","// SDK 전용 CSS 스타일 (동적 주입)\nexport const injectStyles = () => {\n if (typeof document === 'undefined') return;\n if (document.getElementById('voyage-sdk-styles')) return;\n\n const style = document.createElement('style');\n style.id = 'voyage-sdk-styles';\n style.textContent = `\n /* Voyage SDK Styles */\n\n /* Spinner Animation */\n @keyframes voyage-spin {\n from { transform: rotate(0deg); }\n to { transform: rotate(360deg); }\n }\n\n #voyage-widget-container * {\n box-sizing: border-box;\n }\n\n #voyage-widget-container button {\n font-family: 'Quicksand', 'Nunito', system-ui, sans-serif;\n }\n\n #voyage-widget-container textarea:focus,\n #voyage-widget-container input:focus {\n border-color: rgba(124, 58, 237, 0.5);\n box-shadow: 0 0 0 3px rgba(124, 58, 237, 0.1);\n }\n\n #voyage-widget-container button:focus {\n outline: none;\n }\n\n /* Spinner */\n .voyage-spinner {\n width: 16px;\n height: 16px;\n border: 2px solid rgba(255,255,255,0.3);\n border-top-color: #fff;\n border-radius: 50%;\n animation: voyage-spin 0.8s linear infinite;\n }\n\n /* Load Quicksand font if not present */\n @import url('https://fonts.googleapis.com/css2?family=Quicksand:wght@400;500;600;700&display=swap');\n `;\n\n document.head.appendChild(style);\n};\n","import type { ProjectConfig, FeedbackData } from '../types';\n\n// Supabase Edge Functions URL\nconst API_BASE = 'https://rxflzurkkuqgopjkshjh.supabase.co/functions/v1';\n\nconst CACHE_KEY = 'voyage_config';\nconst CACHE_TTL = 60 * 60 * 1000; // 1시간\n\ninterface CachedConfig {\n config: ProjectConfig;\n timestamp: number;\n}\n\nexport async function fetchProjectConfig(sdkKey: string): Promise<ProjectConfig | null> {\n // 캐시 확인\n const cached = getFromCache(sdkKey);\n if (cached) {\n return cached;\n }\n\n try {\n const response = await fetch(`${API_BASE}/get-widget-config`, {\n method: 'GET',\n headers: {\n 'Content-Type': 'application/json',\n 'x-sdk-key': sdkKey,\n },\n });\n\n if (!response.ok) {\n console.error('[Voyage] Failed to fetch config:', response.status);\n return null;\n }\n\n const config = await response.json() as ProjectConfig;\n saveToCache(sdkKey, config);\n return config;\n } catch (error) {\n console.error('[Voyage] Network error:', error);\n // 네트워크 실패 시 캐시 사용 (만료되어도)\n return getFromCache(sdkKey, true);\n }\n}\n\nexport async function submitFeedback(sdkKey: string, data: FeedbackData): Promise<boolean> {\n try {\n const response = await fetch(`${API_BASE}/submit-feedback`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'x-sdk-key': sdkKey,\n },\n body: JSON.stringify({\n label: data.label,\n content: data.text,\n email: data.email,\n imageUrl: data.imageUrl,\n metadata: data.metadata,\n }),\n });\n\n if (!response.ok) {\n const errorData = await response.json().catch(() => ({}));\n console.error('[Voyage] Failed to submit feedback:', response.status, errorData);\n return false;\n }\n\n return true;\n } catch (error) {\n console.error('[Voyage] Network error:', error);\n return false;\n }\n}\n\nexport async function uploadImage(sdkKey: string, file: File): Promise<string | null> {\n // 검증\n if (!file.type.startsWith('image/')) {\n console.error('[Voyage] Only image files are allowed');\n return null;\n }\n\n if (file.size > 5 * 1024 * 1024) {\n console.error('[Voyage] Image must be under 5MB');\n return null;\n }\n\n try {\n const formData = new FormData();\n formData.append('file', file);\n\n const response = await fetch(`${API_BASE}/upload-feedback-image`, {\n method: 'POST',\n headers: {\n 'x-sdk-key': sdkKey,\n },\n body: formData,\n });\n\n if (!response.ok) {\n const errorData = await response.json().catch(() => ({}));\n console.error('[Voyage] Failed to upload image:', response.status, errorData);\n return null;\n }\n\n const result = await response.json();\n return result.url;\n } catch (error) {\n console.error('[Voyage] Network error:', error);\n return null;\n }\n}\n\nfunction getFromCache(sdkKey: string, ignoreExpiry = false): ProjectConfig | null {\n try {\n const raw = localStorage.getItem(`${CACHE_KEY}_${sdkKey}`);\n if (!raw) return null;\n\n const cached: CachedConfig = JSON.parse(raw);\n const isExpired = Date.now() - cached.timestamp > CACHE_TTL;\n\n if (isExpired && !ignoreExpiry) {\n return null;\n }\n\n return cached.config;\n } catch {\n return null;\n }\n}\n\nfunction saveToCache(sdkKey: string, config: ProjectConfig): void {\n try {\n const cached: CachedConfig = {\n config,\n timestamp: Date.now(),\n };\n localStorage.setItem(`${CACHE_KEY}_${sdkKey}`, JSON.stringify(cached));\n } catch {\n // localStorage 사용 불가 시 무시\n }\n}\n","import type { FeedbackMetadata } from '../types';\n\nexport function captureMetadata(): FeedbackMetadata {\n return {\n url: window.location.href,\n pathname: window.location.pathname,\n browser: navigator.userAgent,\n viewport: `${window.innerWidth}x${window.innerHeight}`,\n title: document.title,\n timestamp: new Date().toISOString(),\n locale: navigator.language,\n referrer: document.referrer,\n };\n}\n","// @voyage/sdk\n// Feedback Widget SDK for collecting user feedback\n\nimport { h, render } from 'preact';\n\nimport { Widget } from './components';\nimport { injectStyles } from './styles';\nimport { fetchProjectConfig, submitFeedback, uploadImage } from './utils/api';\nimport { captureMetadata } from './utils/metadata';\n\nimport type { VoyageConfig, ProjectConfig, FeedbackData } from './types';\n\nexport type { VoyageConfig, ProjectConfig, FeedbackData, FeedbackMetadata } from './types';\n\nconst DEFAULT_CONFIG: ProjectConfig = {\n projectId: '',\n formFields: {\n email: { enabled: true, required: false },\n category: { enabled: true, options: ['bug', 'feature'] },\n },\n theme: {\n primaryColor: '#0ea5e9',\n position: 'bottom-right',\n },\n i18n: {\n buttonLabel: 'To : the Maker',\n placeholder: 'Describe your ideas to improve our product',\n bugPlaceholder: 'Please describe the bug in detail (e.g., clicked a button and it didn\\'t work)',\n featurePlaceholder: 'Describe your ideas to improve our product (e.g., I want to export TASK.md to Linear tickets)',\n submitLabel: 'Send to the Maker',\n successMessage: 'Thank you for your feedback!',\n },\n};\n\nclass VoyageSDK {\n private config: VoyageConfig | null = null;\n private serverConfig: ProjectConfig | null = null;\n private container: HTMLElement | null = null;\n private isWidgetVisible = false;\n private isModalOpen = false;\n\n async init(config: VoyageConfig): Promise<void> {\n this.config = config;\n\n if (!config.sdkKey) {\n console.error('[Voyage] Invalid SDK Key');\n return;\n }\n\n // Inject SDK styles\n injectStyles();\n\n // Check display control\n if (!this.shouldDisplay()) {\n console.log('[Voyage] Widget hidden by display rules');\n return;\n }\n\n // Fetch server config\n const fetchedConfig = await fetchProjectConfig(config.sdkKey);\n this.serverConfig = this.mergeConfig(fetchedConfig, config);\n\n // Render widget\n this.isWidgetVisible = true;\n this.renderWidget();\n console.log('[Voyage] Initialized');\n }\n\n show(): void {\n if (!this.config) {\n console.warn('[Voyage] SDK not initialized');\n return;\n }\n this.isWidgetVisible = true;\n this.renderWidget();\n }\n\n hide(): void {\n this.isWidgetVisible = false;\n this.renderWidget();\n }\n\n open(): void {\n if (!this.config) {\n console.warn('[Voyage] SDK not initialized');\n return;\n }\n this.isModalOpen = true;\n this.renderWidget();\n }\n\n close(): void {\n this.isModalOpen = false;\n this.renderWidget();\n }\n\n isOpen(): boolean {\n return this.isModalOpen;\n }\n\n isVisible(): boolean {\n return this.isWidgetVisible;\n }\n\n async refreshConfig(): Promise<void> {\n if (!this.config) return;\n const fetchedConfig = await fetchProjectConfig(this.config.sdkKey);\n this.serverConfig = this.mergeConfig(fetchedConfig, this.config);\n this.renderWidget();\n }\n\n destroy(): void {\n if (this.container) {\n render(null, this.container);\n this.container.remove();\n this.container = null;\n }\n this.config = null;\n this.serverConfig = null;\n this.isWidgetVisible = false;\n this.isModalOpen = false;\n }\n\n private renderWidget(): void {\n if (!this.serverConfig || !this.config) return;\n\n // Create container if not exists\n if (!this.container) {\n this.container = document.createElement('div');\n this.container.id = 'voyage-widget-container';\n document.body.appendChild(this.container);\n }\n\n const sdkKey = this.config.sdkKey;\n\n render(\n h(Widget, {\n config: this.serverConfig,\n visible: this.isWidgetVisible,\n defaultOpen: this.isModalOpen,\n onOpenChange: (open: boolean) => {\n this.isModalOpen = open;\n },\n getMetadata: captureMetadata,\n onSubmit: async (data: FeedbackData) => {\n return submitFeedback(sdkKey, data);\n },\n onUpload: async (file: File) => {\n return uploadImage(sdkKey, file);\n },\n }),\n this.container\n );\n }\n\n private mergeConfig(server: ProjectConfig | null, _client: VoyageConfig): ProjectConfig {\n // Server config takes priority: use values set from dashboard\n // Client-side theme override disabled (to prevent config conflicts)\n return server || { ...DEFAULT_CONFIG };\n }\n\n private shouldDisplay(): boolean {\n if (!this.config) return false;\n\n const pathname = window.location.pathname;\n const { include, exclude } = this.config;\n\n // include takes priority over exclude\n if (include && include.length > 0) {\n return include.some((pattern) => this.matchPath(pathname, pattern));\n }\n\n if (exclude && exclude.length > 0) {\n return !exclude.some((pattern) => this.matchPath(pathname, pattern));\n }\n\n return true;\n }\n\n private matchPath(pathname: string, pattern: string): boolean {\n // Simple glob matching: /admin/* matches /admin/anything\n if (pattern.endsWith('/*')) {\n const prefix = pattern.slice(0, -2);\n return pathname.startsWith(prefix);\n }\n return pathname === pattern;\n }\n}\n\n// Singleton instance\nconst Voyage = new VoyageSDK();\n\nexport { Voyage };\nexport default Voyage;\n"]}
package/package.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "@voyage-sdk/core",
3
+ "version": "0.1.0",
4
+ "description": "Voyage Feedback Widget SDK - Collect user feedback with ease",
5
+ "author": "Voyage",
6
+ "homepage": "https://voyagefeedback.com",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/voyage-sdk/core"
10
+ },
11
+ "publishConfig": {
12
+ "access": "public"
13
+ },
14
+ "type": "module",
15
+ "main": "./dist/index.cjs",
16
+ "module": "./dist/index.js",
17
+ "types": "./dist/index.d.ts",
18
+ "exports": {
19
+ ".": {
20
+ "types": "./dist/index.d.ts",
21
+ "import": "./dist/index.js",
22
+ "require": "./dist/index.cjs"
23
+ },
24
+ "./style.css": "./dist/style.css"
25
+ },
26
+ "files": [
27
+ "dist"
28
+ ],
29
+ "scripts": {
30
+ "build": "tsup",
31
+ "dev": "tsup --watch"
32
+ },
33
+ "dependencies": {
34
+ "preact": "^10.24.0"
35
+ },
36
+ "devDependencies": {
37
+ "tsup": "^8.3.0",
38
+ "typescript": "^5.5.0"
39
+ },
40
+ "keywords": [
41
+ "feedback",
42
+ "widget",
43
+ "voyage",
44
+ "morphprd"
45
+ ],
46
+ "license": "MIT"
47
+ }