@antontranelis/money-printer 1.0.18 → 1.0.19
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 +116 -86
- package/dist/assets/pdfWorker-DjqCcfZq.js +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +18 -2
- package/dist/index.js +852 -654
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -1,133 +1,163 @@
|
|
|
1
|
-
# Money
|
|
1
|
+
# Money Printer
|
|
2
2
|
|
|
3
3
|
Create personalized time vouchers that look like real currency. Design your own "Zeitgutscheine" (time vouchers) with custom portraits, names, and descriptions - perfect as unique gifts.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
**[Live Demo](https://antontranelis.github.io/money-printer/)**
|
|
6
6
|
|
|
7
7
|
## Features
|
|
8
8
|
|
|
9
|
-
- **Custom Portrait**: Upload your photo
|
|
9
|
+
- **Custom Portrait**: Upload your photo with zoom and pan controls
|
|
10
|
+
- **Background Removal**: AI-powered background removal with adjustable opacity and blur
|
|
11
|
+
- **Sepia Effect**: Local vintage currency engraving effect (no API required)
|
|
10
12
|
- **Personalization**: Add your name, email, and phone number
|
|
11
13
|
- **Multiple Denominations**: Choose between 1, 5, or 10 hour vouchers
|
|
12
14
|
- **Bilingual**: Full support for German and English
|
|
13
15
|
- **High-Quality Export**: Download as print-ready PDF (A4 landscape)
|
|
14
|
-
- **
|
|
16
|
+
- **Responsive**: Works on desktop and mobile with touch support
|
|
15
17
|
|
|
16
|
-
##
|
|
18
|
+
## Installation
|
|
17
19
|
|
|
18
|
-
|
|
20
|
+
```bash
|
|
21
|
+
npm install @antontranelis/money-printer
|
|
22
|
+
```
|
|
19
23
|
|
|
20
|
-
##
|
|
24
|
+
## Usage
|
|
21
25
|
|
|
22
|
-
|
|
23
|
-
- **TypeScript** - Type Safety
|
|
24
|
-
- **Zustand** - State Management
|
|
25
|
-
- **jsPDF** - PDF Generation
|
|
26
|
-
- **Tailwind CSS + DaisyUI** - Styling
|
|
27
|
-
- **Vite** - Build Tool
|
|
26
|
+
### As a React Component
|
|
28
27
|
|
|
29
|
-
|
|
28
|
+
```tsx
|
|
29
|
+
import { MoneyPrinter } from '@antontranelis/money-printer';
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
function App() {
|
|
32
|
+
return <MoneyPrinter />;
|
|
33
|
+
}
|
|
34
|
+
```
|
|
32
35
|
|
|
33
|
-
|
|
34
|
-
|
|
36
|
+
### Individual Components
|
|
37
|
+
|
|
38
|
+
```tsx
|
|
39
|
+
import {
|
|
40
|
+
BillForm,
|
|
41
|
+
BillPreview,
|
|
42
|
+
ExportButton,
|
|
43
|
+
PortraitUpload,
|
|
44
|
+
useBillStore
|
|
45
|
+
} from '@antontranelis/money-printer';
|
|
46
|
+
|
|
47
|
+
function CustomEditor() {
|
|
48
|
+
const portrait = useBillStore((state) => state.portrait);
|
|
49
|
+
|
|
50
|
+
return (
|
|
51
|
+
<div>
|
|
52
|
+
<PortraitUpload />
|
|
53
|
+
<BillPreview />
|
|
54
|
+
<ExportButton />
|
|
55
|
+
</div>
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
```
|
|
35
59
|
|
|
36
|
-
###
|
|
60
|
+
### Services
|
|
61
|
+
|
|
62
|
+
```tsx
|
|
63
|
+
import {
|
|
64
|
+
// PDF Generation
|
|
65
|
+
generateBillPDF,
|
|
66
|
+
exportBillAsPDF,
|
|
67
|
+
|
|
68
|
+
// Canvas Rendering
|
|
69
|
+
renderFrontSide,
|
|
70
|
+
renderBackSide,
|
|
71
|
+
|
|
72
|
+
// Image Effects (local, no API)
|
|
73
|
+
applyEngravingEffect,
|
|
74
|
+
resizeImage,
|
|
75
|
+
compositeWithBackground,
|
|
76
|
+
clearImageCache,
|
|
77
|
+
|
|
78
|
+
// AI Enhancement (requires API key)
|
|
79
|
+
removeBackground,
|
|
80
|
+
setApiKey,
|
|
81
|
+
hasApiKey,
|
|
82
|
+
} from '@antontranelis/money-printer';
|
|
83
|
+
```
|
|
37
84
|
|
|
38
|
-
|
|
39
|
-
# Clone the repository
|
|
40
|
-
git clone https://github.com/yourusername/money-generator.git
|
|
41
|
-
cd money-generator
|
|
85
|
+
## Portrait Controls
|
|
42
86
|
|
|
43
|
-
|
|
44
|
-
npm install
|
|
87
|
+
### Zoom & Pan
|
|
45
88
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
89
|
+
- **Zoom Slider**: Adjust portrait size from 50% to 200%
|
|
90
|
+
- **Pan/Drag**: When zoomed in, drag the portrait to reposition (mouse & touch supported)
|
|
91
|
+
- Works in both the avatar preview and the bill preview canvas
|
|
49
92
|
|
|
50
|
-
###
|
|
93
|
+
### Background Removal
|
|
51
94
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
95
|
+
1. Toggle "Remove background" (requires Stability AI API key)
|
|
96
|
+
2. Adjust **Background Opacity** (0-100%) to blend original background
|
|
97
|
+
3. Adjust **Blur** (0-100%) for depth-of-field effect
|
|
55
98
|
|
|
56
|
-
|
|
57
|
-
npm run preview
|
|
58
|
-
```
|
|
99
|
+
### Sepia Effect
|
|
59
100
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
2. **Adjust Zoom**: Use the slider to zoom in/out on your portrait
|
|
64
|
-
3. **Enter Details**: Fill in your name and contact information
|
|
65
|
-
4. **Choose Hours**: Select 1, 5, or 10 hours for your voucher
|
|
66
|
-
5. **Add Description**: Optionally add a custom description
|
|
67
|
-
6. **Download PDF**: Click the download button to get your print-ready voucher
|
|
101
|
+
- Local processing, no API required
|
|
102
|
+
- Adjustable intensity (0-100%)
|
|
103
|
+
- Applies vintage currency engraving look
|
|
68
104
|
|
|
69
|
-
## AI
|
|
105
|
+
## AI Features (Optional)
|
|
70
106
|
|
|
71
|
-
|
|
107
|
+
Background removal uses Stability AI. To enable:
|
|
72
108
|
|
|
73
|
-
To enable:
|
|
74
109
|
1. Get an API key from [Stability AI](https://stability.ai/)
|
|
75
|
-
2. Click "
|
|
76
|
-
3.
|
|
110
|
+
2. Click "Remove background" toggle in the app
|
|
111
|
+
3. Enter your API key when prompted
|
|
77
112
|
|
|
78
|
-
|
|
113
|
+
The sepia/engraving effect runs locally and doesn't require an API key.
|
|
79
114
|
|
|
80
|
-
##
|
|
115
|
+
## Tech Stack
|
|
116
|
+
|
|
117
|
+
- **React 18/19** - UI Framework
|
|
118
|
+
- **TypeScript** - Type Safety
|
|
119
|
+
- **Zustand** - State Management
|
|
120
|
+
- **jsPDF** - PDF Generation
|
|
121
|
+
- **Canvas API** - Image Processing
|
|
81
122
|
|
|
82
|
-
|
|
123
|
+
## Templates
|
|
83
124
|
|
|
84
|
-
|
|
125
|
+
The package includes two template sets:
|
|
85
126
|
|
|
86
|
-
- **German (DE)**: High-DPI templates (
|
|
127
|
+
- **German (DE)**: High-DPI templates (6144x3200px)
|
|
87
128
|
- **English (EN)**: Standard-DPI templates (1536x1024px)
|
|
88
129
|
|
|
89
|
-
|
|
130
|
+
Templates are bundled in `public/templates/`.
|
|
90
131
|
|
|
91
|
-
|
|
132
|
+
## Development
|
|
92
133
|
|
|
93
|
-
|
|
134
|
+
```bash
|
|
135
|
+
# Clone the repository
|
|
136
|
+
git clone https://github.com/antontranelis/money-printer.git
|
|
137
|
+
cd money-printer
|
|
94
138
|
|
|
95
|
-
|
|
139
|
+
# Install dependencies
|
|
140
|
+
npm install
|
|
96
141
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
├── components/ # React components
|
|
100
|
-
│ ├── BillForm.tsx # Main form container
|
|
101
|
-
│ ├── BillPreview.tsx # Canvas preview
|
|
102
|
-
│ ├── ExportButton.tsx # PDF download
|
|
103
|
-
│ ├── PortraitUpload.tsx
|
|
104
|
-
│ └── ...
|
|
105
|
-
├── services/ # Core services
|
|
106
|
-
│ ├── pdfGenerator.ts # PDF creation
|
|
107
|
-
│ ├── canvasRenderer.ts # Canvas drawing
|
|
108
|
-
│ └── stabilityAI.ts # AI enhancement
|
|
109
|
-
├── stores/ # Zustand stores
|
|
110
|
-
│ └── billStore.ts # App state
|
|
111
|
-
├── types/ # TypeScript types
|
|
112
|
-
├── constants/ # Templates & translations
|
|
113
|
-
└── hooks/ # Custom React hooks
|
|
114
|
-
```
|
|
142
|
+
# Start development server
|
|
143
|
+
npm run dev
|
|
115
144
|
|
|
116
|
-
|
|
145
|
+
# Build library
|
|
146
|
+
npm run build:lib
|
|
117
147
|
|
|
118
|
-
|
|
148
|
+
# Build standalone app
|
|
149
|
+
npm run build
|
|
150
|
+
```
|
|
119
151
|
|
|
120
|
-
|
|
121
|
-
2. Create your feature branch (`git checkout -b feature/AmazingFeature`)
|
|
122
|
-
3. Commit your changes (`git commit -m 'Add some AmazingFeature'`)
|
|
123
|
-
4. Push to the branch (`git push origin feature/AmazingFeature`)
|
|
124
|
-
5. Open a Pull Request
|
|
152
|
+
## Performance Optimizations
|
|
125
153
|
|
|
126
|
-
|
|
154
|
+
The image processing is optimized for smooth performance:
|
|
127
155
|
|
|
128
|
-
|
|
156
|
+
- **Image Caching**: Avoids reloading the same data URLs
|
|
157
|
+
- **Uint32Array**: Fast pixel manipulation for sepia effect
|
|
158
|
+
- **Reusable Canvas**: Reduces garbage collection pressure
|
|
159
|
+
- **Debounced Updates**: Slider changes are debounced to prevent lag
|
|
129
160
|
|
|
130
|
-
##
|
|
161
|
+
## License
|
|
131
162
|
|
|
132
|
-
|
|
133
|
-
- Built with modern React patterns and best practices
|
|
163
|
+
MIT License - see [LICENSE](LICENSE) for details.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
(function(){"use strict";function C(t,o,n){t/=255,o/=255,n/=255;const s=Math.max(t,o,n),i=Math.min(t,o,n),r=(s+i)/2;let a=0,l=0;if(s!==i){const c=s-i;switch(l=r>.5?c/(2-s-i):c/(s+i),s){case t:a=((o-n)/c+(o<n?6:0))/6;break;case o:a=((n-t)/c+2)/6;break;case n:a=((t-o)/c+4)/6;break}}return[a,l,r]}function D(t,o,n){if(o===0){const a=Math.round(n*255);return[a,a,a]}const s=(a,l,c)=>(c<0&&(c+=1),c>1&&(c-=1),c<1/6?a+(l-a)*6*c:c<1/2?l:c<2/3?a+(l-a)*(2/3-c)*6:a),i=n<.5?n*(1+o):n+o-n*o,r=2*n-i;return[Math.round(s(r,i,t+1/3)*255),Math.round(s(r,i,t)*255),Math.round(s(r,i,t-1/3)*255)]}function T(t,o){if(o>=155&&o<=165)return;const n=new Uint32Array(t.data.buffer),s=o%360/360,i=60/360,r=260/360;for(let a=0;a<n.length;a++){const l=n[a],c=l&255,p=l>>8&255,e=l>>16&255,f=l>>24&255;if(f===0)continue;const[u,m,g]=C(c,p,e);if(m<.06||u<i||u>r)continue;const[d,h,b]=D(s,m,g);n[a]=f<<24|b<<16|h<<8|d}}async function P(t){const n=await(await fetch(t)).blob();return createImageBitmap(n)}function H(t,o,n,s,i,r,a,l,c){t.save(),t.beginPath(),t.ellipse(n,s,i,r,0,0,Math.PI*2),t.closePath(),t.clip();const p=o.width/o.height,e=i/r,f=i*2,u=r*2;let m,g;p>e?(g=u,m=u*p):(m=f,g=f/p),m*=a,g*=a;const d=Math.max(0,(m-f)/2),h=Math.max(0,(g-u)/2),b=n-m/2+l*d,y=s-g/2+c*h;t.drawImage(o,b,y,m,g),t.restore()}function B(t,o,n,s,i,r="center",a){t.save(),t.font=`${i}px "Times New Roman", serif`,t.textAlign=r,t.textBaseline="middle",t.fillStyle="#2a3a2a",a?t.fillText(o,n,s,a):t.fillText(o,n,s),t.restore()}function x(t,o,n,s,i,r,a,l="center"){t.save(),t.font=`${i}px "Times New Roman", serif`,t.textAlign=l,t.textBaseline="top",t.fillStyle="#2a3a2a";const c=o.split(" "),p=[];let e="";for(const m of c){const g=e?`${e} ${m}`:m;t.measureText(g).width>r&&e?(p.push(e),e=m):e=g}e&&p.push(e);const f=p.length*a;let u=s-f/2;for(const m of p)t.fillText(m,n,u),u+=a;t.restore()}function A(t,o,n,s,i,r,a,l,c="center"){t.save(),t.font=`${a}px "Times New Roman", serif`,t.textAlign=c,t.textBaseline="middle",t.fillStyle="#2a3a2a";const p=[o,n,s].filter(Boolean),e=(p.length-1)*l;let f=r-e/2;for(const u of p)u&&(t.fillText(u,i,f),f+=l);t.restore()}self.onmessage=async t=>{const{frontTemplateUrl:o,backTemplateUrl:n,portraitUrl:s,templateWidth:i,templateHeight:r,templateHue:a,portraitZoom:l,portraitPanX:c,portraitPanY:p,layout:e,name:f,email:u,phone:m,description:g}=t.data;try{const[d,h]=await Promise.all([P(o),P(n)]);let b=null;s&&(b=await P(s));const y=new OffscreenCanvas(i,r),M=new OffscreenCanvas(i,r),k=y.getContext("2d"),w=M.getContext("2d");if(k.drawImage(d,0,0,i,r),a<155||a>165){const I=k.getImageData(0,0,i,r);T(I,a),k.putImageData(I,0,0)}if(b&&H(k,b,e.front.portrait.x,e.front.portrait.y,e.front.portrait.radiusX,e.front.portrait.radiusY,l,c,p),f&&B(k,f,e.front.namePlate.x,e.front.namePlate.y,e.front.namePlate.fontSize,e.front.namePlate.align||"center",e.front.namePlate.maxWidth),w.drawImage(h,0,0,i,r),a<155||a>165){const I=w.getImageData(0,0,i,r);T(I,a),w.putImageData(I,0,0)}e.back.contactInfo&&(f||u||m)&&A(w,f,u,m,e.back.contactInfo.x,e.back.contactInfo.y,e.back.contactInfo.fontSize,e.back.contactInfo.lineHeight||e.back.contactInfo.fontSize*1.8,e.back.contactInfo.align||"center"),e.back.description&&g&&x(w,g,e.back.description.x,e.back.description.y,e.back.description.fontSize,e.back.description.maxWidth||400,e.back.description.lineHeight||e.back.description.fontSize*1.4,e.back.description.align||"center"),f&&B(w,f,e.back.namePlate.x,e.back.namePlate.y,e.back.namePlate.fontSize,e.back.namePlate.align||"center",e.back.namePlate.maxWidth);const[z,R]=await Promise.all([y.convertToBlob({type:"image/jpeg",quality:.95}),M.convertToBlob({type:"image/jpeg",quality:.95})]),[v,S]=await Promise.all([z.arrayBuffer(),R.arrayBuffer()]),W={type:"success",frontImageData:v,backImageData:S,width:i,height:r};self.postMessage(W,[v,S])}catch(d){const h={type:"error",error:String(d)};self.postMessage(h)}}})();
|
package/dist/index.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const o=require("react/jsx-runtime"),it=require("zustand"),st=require("zustand/middleware"),m=require("react"),lt=require("jspdf");var G=typeof document<"u"?document.currentScript:null;const Ne={personalInfo:{name:"",email:"",phone:""},voucherConfig:{hours:1,description:"",language:"de"},portrait:{original:null,enhanced:null,useEnhanced:!1,zoom:1,panX:0,panY:0,rawImage:null,bgRemovedImage:null,bgRemoved:!1,bgOpacity:0,bgBlur:0,engravingIntensity:0},currentSide:"front",isEnhancing:!1,isExporting:!1},f=it.create()(st.persist(e=>({...Ne,setPersonalInfo:t=>e(n=>({personalInfo:{...n.personalInfo,...t}})),setVoucherConfig:t=>e(n=>({voucherConfig:{...n.voucherConfig,...t}})),setPortrait:(t,n=null)=>e(r=>({portrait:{...r.portrait,original:t,enhanced:n,useEnhanced:!1,zoom:r.portrait.original&&t?r.portrait.zoom:1,panX:r.portrait.original&&t?r.portrait.panX:0,panY:r.portrait.original&&t?r.portrait.panY:0}})),setEnhancedPortrait:t=>e(n=>({portrait:{...n.portrait,enhanced:t,useEnhanced:t!==null}})),toggleUseEnhanced:()=>e(t=>({portrait:{...t.portrait,useEnhanced:t.portrait.enhanced?!t.portrait.useEnhanced:!1}})),setPortraitZoom:t=>e(n=>({portrait:{...n.portrait,zoom:t}})),setPortraitPan:(t,n)=>e(r=>({portrait:{...r.portrait,panX:t,panY:n}})),setPortraitRawImage:t=>e(n=>({portrait:{...n.portrait,rawImage:t}})),setPortraitBgRemoved:(t,n)=>e(r=>({portrait:{...r.portrait,bgRemoved:t,bgRemovedImage:n!==void 0?n:r.portrait.bgRemovedImage}})),setPortraitBgOpacity:t=>e(n=>({portrait:{...n.portrait,bgOpacity:t}})),setPortraitBgBlur:t=>e(n=>({portrait:{...n.portrait,bgBlur:t}})),setPortraitEngravingIntensity:t=>e(n=>({portrait:{...n.portrait,engravingIntensity:t}})),setCurrentSide:t=>e({currentSide:t}),flipSide:()=>e(t=>({currentSide:t.currentSide==="front"?"back":"front"})),setIsEnhancing:t=>e({isEnhancing:t}),setIsExporting:t=>e({isExporting:t}),setLanguage:t=>e(n=>({voucherConfig:{...n.voucherConfig,language:t}})),setHours:t=>e(n=>({voucherConfig:{...n.voucherConfig,hours:t}})),reset:()=>e(Ne)}),{name:"money-generator-storage",partialize:e=>({personalInfo:e.personalInfo,voucherConfig:e.voucherConfig,portrait:{original:null,enhanced:null,useEnhanced:e.portrait.useEnhanced,zoom:e.portrait.zoom,panX:e.portrait.panX,panY:e.portrait.panY,rawImage:null,bgRemovedImage:null,bgRemoved:!1,bgOpacity:0,bgBlur:0,engravingIntensity:e.portrait.engravingIntensity}})})),ct={header:{title:"Money Generator",subtitle:"Erstelle deinen persönlichen Zeitgutschein"},form:{personalInfo:{title:"Persönliche Daten",name:"Name",namePlaceholder:"Dein Name",email:"E-Mail",emailPlaceholder:"deine@email.de",phone:"Telefon",phonePlaceholder:"+49 123 456789"},portrait:{title:"Portrait",upload:"Bild hochladen",dragDrop:"oder hierher ziehen",enhance:"Mit AI verbessern",enhancing:"Wird verbessert...",useOriginal:"Original verwenden",useEnhanced:"Verbessertes verwenden",zoom:"Zoom"},voucher:{title:"Gutschein",hours:"Stunden",hourLabel:"Stunde",hoursLabel:"Stunden",description:"Beschreibung",descriptionPlaceholder:"Was kann mit diesem Gutschein eingelöst werden?"}},preview:{front:"Vorderseite",back:"Rückseite",flip:"Umdrehen"},export:{button:"Als PDF herunterladen",exporting:"PDF wird erstellt...",success:"Download gestartet!"},bill:{descriptionText:"Für diesen Schein erhältst du {hours} {hourLabel} meiner Zeit oder ein gleichwertiges Dankeschön"}},dt={header:{title:"Money Generator",subtitle:"Create your personal time voucher"},form:{personalInfo:{title:"Personal Information",name:"Name",namePlaceholder:"Your name",email:"Email",emailPlaceholder:"your@email.com",phone:"Phone",phonePlaceholder:"+1 234 567890"},portrait:{title:"Portrait",upload:"Upload image",dragDrop:"or drag and drop",enhance:"Enhance with AI",enhancing:"Enhancing...",useOriginal:"Use original",useEnhanced:"Use enhanced",zoom:"Zoom"},voucher:{title:"Voucher",hours:"Hours",hourLabel:"hour",hoursLabel:"hours",description:"Description",descriptionPlaceholder:"What can be redeemed with this voucher?"}},preview:{front:"Front",back:"Back",flip:"Flip"},export:{button:"Download as PDF",exporting:"Creating PDF...",success:"Download started!"},bill:{descriptionText:"This voucher entitles you to {hours} {hourLabel} of my time or an equivalent thank you"}},ut={de:ct,en:dt};function H(e){return ut[e]}function xe(e,t,n){if(n&&n.trim())return n;const r=H(e),a=t===1?r.form.voucher.hourLabel:r.form.voucher.hoursLabel;return r.bill.descriptionText.replace("{hours}",t.toString()).replace("{hourLabel}",a)}function ht(){const e=f(a=>a.voucherConfig.language),t=f(a=>a.personalInfo),n=f(a=>a.setPersonalInfo),r=H(e);return o.jsxs("div",{className:"space-y-4",children:[o.jsxs("div",{className:"form-control",children:[o.jsx("label",{className:"label",children:o.jsx("span",{className:"label-text font-medium",children:r.form.personalInfo.name})}),o.jsx("input",{type:"text",placeholder:r.form.personalInfo.namePlaceholder,className:"input input-bordered w-full",value:t.name,onChange:a=>n({name:a.target.value})})]}),o.jsxs("div",{className:"form-control",children:[o.jsx("label",{className:"label",children:o.jsx("span",{className:"label-text font-medium",children:r.form.personalInfo.email})}),o.jsx("input",{type:"email",placeholder:r.form.personalInfo.emailPlaceholder,className:"input input-bordered w-full",value:t.email,onChange:a=>n({email:a.target.value})})]}),o.jsxs("div",{className:"form-control",children:[o.jsx("label",{className:"label",children:o.jsx("span",{className:"label-text font-medium",children:r.form.personalInfo.phone})}),o.jsx("input",{type:"tel",placeholder:r.form.personalInfo.phonePlaceholder,className:"input input-bordered w-full",value:t.phone,onChange:a=>n({phone:a.target.value})})]})]})}const pt=1024,V=new Map;let Y=null,Re=null;function Ae(e,t){return Y||(Y=document.createElement("canvas"),Re=Y.getContext("2d",{willReadFrequently:!0})),(Y.width!==e||Y.height!==t)&&(Y.width=e,Y.height=t),Re}function ce(e){const t=V.get(e);return t?Promise.resolve(t):new Promise((n,r)=>{const a=new Image;a.onload=()=>{if(V.size>10){const s=V.keys().next().value;s&&V.delete(s)}V.set(e,a),n(a)},a.onerror=()=>r(new Error("Failed to load image")),a.src=e})}function gt(){V.clear()}async function mt(e,t=pt){const n=await ce(e),r=Math.max(n.width,n.height);if(r<=t)return e;const a=t/r,s=Math.round(n.width*a),l=Math.round(n.height*a),c=document.createElement("canvas"),d=c.getContext("2d");if(!d)throw new Error("Failed to get canvas context");return c.width=s,c.height=l,d.drawImage(n,0,0,s,l),c.toDataURL("image/jpeg",.9)}async function be(e,t,n,r=0){const[a,s]=await Promise.all([ce(e),ce(t)]),l=Ae(a.width,a.height);if(l.clearRect(0,0,a.width,a.height),n>0){const c=r*20;l.save(),l.globalAlpha=n,c>0&&(l.filter=`blur(${c}px)`),l.drawImage(s,0,0,a.width,a.height),l.restore()}return l.drawImage(a,0,0),Y.toDataURL("image/png")}async function Le(e,t=.5){const n=await ce(e),r=Ae(n.width,n.height);r.drawImage(n,0,0);const a=r.getImageData(0,0,n.width,n.height),s=a.data,l=new Uint32Array(s.buffer),c=1+t*.8,d=1-t;for(let u=0;u<l.length;u++){const g=l[u],b=g&255,w=g>>8&255,x=g>>16&255,h=g>>24&255;if(h===0)continue;let I=(((77*b+150*w+29*x>>8)/255-.5)*c+.5)*255;I<0?I=0:I>255&&(I=255);let y=I*.9+25,k=I*.78+15,C=I*.55+5;y>255&&(y=255),k>255&&(k=255),C>255&&(C=255);const D=b*d+y*t|0,j=w*d+k*t|0,E=x*d+C*t|0;l[u]=h<<24|E<<16|j<<8|D}return r.putImageData(a,0,0),Y.toDataURL("image/png")}const ft="https://api.stability.ai/v1/generation",bt="https://api.stability.ai/v2beta/stable-image/edit/remove-background",De="stability_api_key";let Z=null;function vt(e){Z=e}function wt(){return Z}function xt(){return Z!==null}const yt={vintage:"portrait in the style of vintage currency engraving, fine line work, crosshatching, sepia tones, detailed stippling, classic bank note portrait style",engraved:"portrait as detailed intaglio engraving, currency bill style, fine parallel lines, high contrast, official government portrait",currency:"portrait rendered as US dollar bill engraving, official currency portrait style, green tint, fine line engraving technique"},Te=[{width:1024,height:1024},{width:1152,height:896},{width:1216,height:832},{width:1344,height:768},{width:1536,height:640},{width:640,height:1536},{width:768,height:1344},{width:832,height:1216},{width:896,height:1152}];function It(e,t){const n=e/t;let r=Te[0],a=1/0;for(const s of Te){const l=s.width/s.height,c=Math.abs(n-l);c<a&&(a=c,r=s)}return r}function Pt(e){return new Promise((t,n)=>{const r=new Image;r.onload=()=>{const a=It(r.width,r.height),s=document.createElement("canvas");s.width=a.width,s.height=a.height;const l=s.getContext("2d");if(!l){n(new Error("Failed to get canvas context"));return}const c=r.width/r.height,d=a.width/a.height;let u=0,g=0,b=r.width,w=r.height;c>d?(b=r.height*d,u=(r.width-b)/2):(w=r.width/d,g=(r.height-w)/2),l.drawImage(r,u,g,b,w,0,0,a.width,a.height),t(s.toDataURL("image/png"))},r.onerror=()=>n(new Error("Failed to load image")),r.src=e})}function Me(e){var l;const t=e.split(","),n=((l=t[0].match(/:(.*?);/))==null?void 0:l[1])||"image/png",r=atob(t[1]),a=r.length,s=new Uint8Array(a);for(let c=0;c<a;c++)s[c]=r.charCodeAt(c);return new Blob([s],{type:n})}function he(){var t;const e=typeof{url:typeof document>"u"?require("url").pathToFileURL(__filename).href:G&&G.tagName.toUpperCase()==="SCRIPT"&&G.src||new URL("index.cjs",document.baseURI).href}<"u"&&"sk-7mEKklrqaltdtgbX0VoZbkA8E1cl939Spn75jSIYRvp1BW0b"||typeof process<"u"&&((t=process.env)==null?void 0:t.NEXT_PUBLIC_STABILITY_API_KEY);return e&&e!=="your-api-key-here"?e:typeof localStorage<"u"?localStorage.getItem(De):null}function _e(e){localStorage.setItem(De,e)}function we(){return Z?!0:he()!==null}async function kt(e){const t=he();if(!t)throw new Error("No Stability AI API key configured");const{imageDataUrl:n,style:r,strength:a=.35}=e,s=await Pt(n),l=Me(s),c=new FormData;c.append("init_image",l,"portrait.png"),c.append("init_image_mode","IMAGE_STRENGTH"),c.append("image_strength",String(1-a)),c.append("text_prompts[0][text]",yt[r]),c.append("text_prompts[0][weight]","1"),c.append("cfg_scale","7"),c.append("samples","1"),c.append("steps","30");const u=await fetch(`${ft}/stable-diffusion-xl-1024-v1-0/image-to-image`,{method:"POST",headers:{Authorization:`Bearer ${t}`,Accept:"application/json"},body:c});if(!u.ok){const b=await u.text();throw console.error("Stability AI error:",b),u.status===401?new Error("Invalid API key"):u.status===402?new Error("Insufficient credits"):u.status===429?new Error("Rate limit exceeded. Please try again later."):new Error(`API error: ${u.status}`)}const g=await u.json();if(!g.artifacts||g.artifacts.length===0)throw new Error("No image generated");return`data:image/png;base64,${g.artifacts[0].base64}`}async function Ye(e){if(Z){const l=await fetch(Z,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({imageDataUrl:e})});if(!l.ok){const d=await l.json().catch(()=>({}));throw new Error(d.error||`API error: ${l.status}`)}return(await l.json()).imageDataUrl}const t=he();if(!t)throw new Error("No Stability AI API key configured");const n=Me(e),r=new FormData;r.append("image",n,"image.png"),r.append("output_format","png");const a=await fetch(bt,{method:"POST",headers:{Authorization:`Bearer ${t}`,Accept:"image/*"},body:r});if(!a.ok){const l=await a.text();throw console.error("Stability AI remove background error:",l),a.status===401?new Error("Invalid API key"):a.status===402?new Error("Insufficient credits"):a.status===429?new Error("Rate limit exceeded. Please try again later."):new Error(`API error: ${a.status}`)}const s=await a.blob();return new Promise((l,c)=>{const d=new FileReader;d.onload=()=>l(d.result),d.onerror=()=>c(new Error("Failed to read result")),d.readAsDataURL(s)})}function jt(){const[e,t]=m.useState(!1),[n,r]=m.useState(!1),[a,s]=m.useState(null),[l,c]=m.useState(()=>we());m.useEffect(()=>{c(we())},[]);const d=m.useCallback(()=>s(null),[]),u=m.useCallback(w=>{_e(w),c(!0),s(null)},[]),g=m.useCallback(async(w,x=.5)=>{t(!0),s(null);try{return await Le(w,x)}catch(h){const N=h instanceof Error?h.message:"Enhancement failed";throw s(N),h}finally{t(!1)}},[]),b=m.useCallback(async w=>{r(!0),s(null);try{return await Ye(w)}catch(x){const h=x instanceof Error?x.message:"Background removal failed";throw s(h),x}finally{r(!1)}},[]);return{enhance:g,removeBg:b,isEnhancing:e,isRemovingBg:n,error:a,hasKey:l,setApiKey:u,clearError:d}}function $e({isOpen:e,onClose:t,onSubmit:n}){const r=f(u=>u.voucherConfig.language),[a,s]=m.useState("");if(!e)return null;const l=u=>{u.preventDefault(),a.trim()&&(n(a.trim()),s(""),t())},d={de:{title:"Stability AI API Key",description:"Um die AI-Bildverbesserung zu nutzen, benötigst du einen Stability AI API Key. Du kannst ihn auf platform.stability.ai erhalten.",placeholder:"sk-...",submit:"Speichern",cancel:"Abbrechen",hint:"Der Key wird lokal in deinem Browser gespeichert."},en:{title:"Stability AI API Key",description:"To use AI image enhancement, you need a Stability AI API key. You can get one at platform.stability.ai.",placeholder:"sk-...",submit:"Save",cancel:"Cancel",hint:"The key is stored locally in your browser."}}[r];return o.jsxs("dialog",{className:"modal modal-open",children:[o.jsxs("div",{className:"modal-box",children:[o.jsx("h3",{className:"font-bold text-lg",children:d.title}),o.jsx("p",{className:"py-4 text-sm opacity-80",children:d.description}),o.jsxs("form",{onSubmit:l,children:[o.jsxs("div",{className:"form-control",children:[o.jsx("input",{type:"password",placeholder:d.placeholder,className:"input input-bordered w-full",value:a,onChange:u=>s(u.target.value),autoFocus:!0}),o.jsx("label",{className:"label",children:o.jsx("span",{className:"label-text-alt",children:d.hint})})]}),o.jsxs("div",{className:"modal-action",children:[o.jsx("button",{type:"button",className:"btn btn-ghost",onClick:t,children:d.cancel}),o.jsx("button",{type:"submit",className:"btn btn-primary",disabled:!a.trim(),children:d.submit})]})]})]}),o.jsx("form",{method:"dialog",className:"modal-backdrop",children:o.jsx("button",{onClick:t,children:"close"})})]})}function Et(){const e=f(i=>i.voucherConfig.language),t=f(i=>i.portrait),n=f(i=>i.setPortrait),r=f(i=>i.setPortraitZoom),a=f(i=>i.setPortraitPan),s=f(i=>i.setPortraitRawImage),l=f(i=>i.setPortraitBgRemoved),c=f(i=>i.setPortraitBgOpacity),d=f(i=>i.setPortraitBgBlur),u=f(i=>i.setPortraitEngravingIntensity),{enhance:g,removeBg:b,isEnhancing:w,isRemovingBg:x,error:h,hasKey:N,setApiKey:I}=jt(),y=H(e),k=m.useRef(null),C=m.useRef(null),[D,j]=m.useState(!1),[E,$]=m.useState(!1),[z,U]=m.useState(!1),A=m.useRef(null),X=m.useRef(null),L=m.useRef(null),S=t.rawImage,te=t.bgRemovedImage,K=t.bgRemoved,ne=t.bgOpacity,ae=t.bgBlur,re=t.engravingIntensity;m.useEffect(()=>{t.original&&!t.rawImage&&s(t.original)},[t.original,t.rawImage,s]),m.useEffect(()=>()=>{X.current&&clearTimeout(X.current),L.current&&clearTimeout(L.current)},[]);const p=m.useCallback(async i=>{if(!i.type.startsWith("image/"))return;const v=new FileReader;v.onload=async P=>{var le;const T=(le=P.target)==null?void 0:le.result,se=await mt(T);s(se),n(se),l(!1,null),u(0)},v.readAsDataURL(i)},[n,s,l,u]),R=m.useCallback(i=>{i.preventDefault(),j(!1);const v=i.dataTransfer.files[0];v&&p(v)},[p]),M=m.useCallback(i=>{i.preventDefault(),j(!0)},[]),W=m.useCallback(i=>{i.preventDefault(),j(!1)},[]),J=()=>{var i;(i=k.current)==null||i.click()},Q=i=>{var P;const v=(P=i.target.files)==null?void 0:P[0];v&&p(v)},_=m.useCallback(async(i,v)=>{try{return await g(i,v)}catch(P){return console.error("Enhancement failed:",P),i}},[g]),q=m.useCallback(async i=>{try{const v=await b(i);return l(!0,v),v}catch(v){return console.error("Background removal failed:",v),i}},[b,l]),ee=m.useCallback(async()=>{const i=f.getState().portrait;if(!i.rawImage)return;let v;i.bgRemoved&&i.bgRemovedImage?v=await be(i.bgRemovedImage,i.rawImage,i.bgOpacity,i.bgBlur):v=i.rawImage,i.engravingIntensity>0&&(v=await _(v,i.engravingIntensity)),n(v)},[_,n]),ge=async()=>{if(!S)return;if(!K&&!N){$(!0);return}if(!K){const v=await q(S),P=f.getState().portrait;let T=await be(v,S,P.bgOpacity,P.bgBlur);P.engravingIntensity>0&&(T=await _(T,P.engravingIntensity)),n(T)}else{l(!1,null);const v=f.getState().portrait.engravingIntensity;if(v>0){const P=await _(S,v);n(P)}else n(S)}},me=i=>{u(i),X.current&&clearTimeout(X.current),S&&(X.current=setTimeout(ee,150))},oe=async i=>{if(I(i),!S)return;const v=await q(S),P=f.getState().portrait;let T=await be(v,S,P.bgOpacity,P.bgBlur);P.engravingIntensity>0&&(T=await _(T,P.engravingIntensity)),n(T)},ie=i=>{c(i),L.current&&clearTimeout(L.current),!(!S||!te)&&(L.current=setTimeout(ee,150))},Ve=i=>{d(i),L.current&&clearTimeout(L.current),!(!S||!te)&&(L.current=setTimeout(ee,150))},Ge=()=>{n(null),s(null),l(!1,null),c(0),d(0),u(0),gt()},Se=(i,v)=>{t.zoom<=1||(U(!0),A.current={x:i,y:v,panX:t.panX,panY:t.panY})},Ce=(i,v)=>{if(!z||!A.current||!C.current)return;const P=C.current.getBoundingClientRect(),T=2/Math.max(P.width,P.height),se=(i-A.current.x)*T,le=(v-A.current.y)*T,rt=Math.max(-1,Math.min(1,A.current.panX+se)),ot=Math.max(-1,Math.min(1,A.current.panY+le));a(rt,ot)},fe=()=>{U(!1),A.current=null},Ze=i=>{i.preventDefault(),Se(i.clientX,i.clientY)},Je=i=>{Ce(i.clientX,i.clientY)},Qe=()=>{fe()},et=()=>{fe()},tt=i=>{i.touches.length===1&&Se(i.touches[0].clientX,i.touches[0].clientY)},nt=i=>{i.touches.length===1&&Ce(i.touches[0].clientX,i.touches[0].clientY)},at=()=>{fe()};return o.jsxs("div",{className:"space-y-4",children:[t.original?o.jsxs("div",{className:"flex flex-col items-center space-y-4",children:[o.jsxs("div",{className:"relative",children:[o.jsx("div",{ref:C,className:`w-32 h-32 rounded-full overflow-hidden border-4 border-currency-gold shadow-lg ${t.zoom>1?"cursor-grab":""} ${z?"cursor-grabbing":""}`,onMouseDown:Ze,onMouseMove:Je,onMouseUp:Qe,onMouseLeave:et,onTouchStart:tt,onTouchMove:nt,onTouchEnd:at,children:o.jsx("img",{src:t.original||"",alt:"Portrait",className:"w-full h-full object-cover pointer-events-none select-none",style:{transform:`scale(${t.zoom}) translate(${t.panX*50*(t.zoom-1)}%, ${t.panY*50*(t.zoom-1)}%)`},draggable:!1})}),o.jsx("button",{className:"btn btn-circle btn-xs btn-error absolute -top-1 -right-1",onClick:Ge,children:o.jsx("svg",{xmlns:"http://www.w3.org/2000/svg",className:"h-4 w-4",fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",children:o.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M6 18L18 6M6 6l12 12"})})})]}),o.jsxs("div",{className:"grid grid-cols-1 sm:grid-cols-2 gap-4 w-full",children:[o.jsxs("div",{className:"form-control w-full",children:[o.jsxs("label",{className:"label",children:[o.jsx("span",{className:"label-text",children:y.form.portrait.zoom}),o.jsxs("span",{className:"label-text-alt",children:[Math.round(t.zoom*100),"%"]})]}),o.jsx("input",{type:"range",min:"0.5",max:"2",step:"0.05",value:t.zoom,onChange:i=>{const v=parseFloat(i.target.value);r(v),v<=1&&(t.panX!==0||t.panY!==0)&&a(0,0)},className:"range range-primary range-sm"})]}),o.jsxs("div",{className:"form-control w-full",children:[o.jsxs("label",{className:"label",children:[o.jsxs("span",{className:"label-text flex items-center gap-2",children:[e==="de"?"Sepia-Effekt":"Sepia effect",w&&o.jsx("span",{className:"loading loading-spinner loading-xs"})]}),o.jsxs("span",{className:"label-text-alt",children:[Math.round(re*100),"%"]})]}),o.jsx("input",{type:"range",min:"0",max:"1",step:"0.05",value:re,onChange:i=>me(parseFloat(i.target.value)),className:"range range-secondary range-sm",disabled:w||!S})]})]}),K?o.jsxs("div",{className:"grid grid-cols-1 sm:grid-cols-2 gap-4 w-full",children:[o.jsxs("div",{className:"form-control w-full",children:[o.jsxs("label",{className:"label",children:[o.jsx("span",{className:"label-text",children:e==="de"?"Hintergrund":"Background"}),o.jsxs("span",{className:"label-text-alt",children:[Math.round(ne*100),"%"]})]}),o.jsx("input",{type:"range",min:"0",max:"1",step:"0.05",value:ne,onChange:i=>ie(parseFloat(i.target.value)),className:"range range-primary range-sm"})]}),o.jsxs("div",{className:"form-control w-full",children:[o.jsxs("label",{className:"label",children:[o.jsx("span",{className:"label-text",children:e==="de"?"Unschärfe":"Blur"}),o.jsxs("span",{className:"label-text-alt",children:[Math.round(ae*100),"%"]})]}),o.jsx("input",{type:"range",min:"0",max:"1",step:"0.05",value:ae,onChange:i=>Ve(parseFloat(i.target.value)),className:"range range-primary range-sm"})]})]}):o.jsx("div",{className:"form-control",children:o.jsxs("label",{className:"label cursor-pointer justify-start gap-3",children:[o.jsx("input",{type:"checkbox",className:`toggle toggle-primary ${x?"opacity-50":""}`,checked:K,onChange:ge,disabled:x||!S}),o.jsxs("span",{className:"label-text flex items-center gap-2",children:[x?o.jsxs(o.Fragment,{children:[o.jsx("span",{className:"loading loading-spinner loading-xs"}),e==="de"?"Hintergrund wird entfernt...":"Removing background..."]}):e==="de"?"Hintergrund entfernen":"Remove background",!N&&o.jsx("span",{className:"badge badge-sm badge-outline",children:"API"})]})]})}),h&&o.jsxs("div",{className:"alert alert-warning text-sm py-2",children:[o.jsx("svg",{xmlns:"http://www.w3.org/2000/svg",className:"stroke-current shrink-0 h-5 w-5",fill:"none",viewBox:"0 0 24 24",children:o.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:"2",d:"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"})}),o.jsx("span",{children:h})]})]}):o.jsxs("div",{className:`border-2 border-dashed rounded-lg p-8 text-center cursor-pointer transition-colors ${x?"border-primary bg-primary/10 pointer-events-none":D?"border-primary bg-primary/10":"border-base-300 hover:border-primary hover:bg-base-200"}`,onDrop:R,onDragOver:M,onDragLeave:W,onClick:J,children:[o.jsx("input",{ref:k,type:"file",accept:"image/*",className:"hidden",onChange:Q}),x?o.jsxs("div",{className:"flex flex-col items-center gap-2",children:[o.jsx("span",{className:"loading loading-spinner loading-lg text-primary"}),o.jsx("p",{className:"font-medium",children:e==="de"?"Hintergrund wird entfernt...":"Removing background..."})]}):o.jsxs("div",{className:"flex flex-col items-center gap-2",children:[o.jsx("svg",{xmlns:"http://www.w3.org/2000/svg",className:"h-12 w-12 text-base-content/50",fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",children:o.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z"})}),o.jsx("p",{className:"font-medium",children:y.form.portrait.upload}),o.jsx("p",{className:"text-sm text-base-content/60",children:y.form.portrait.dragDrop})]})]}),o.jsx($e,{isOpen:E,onClose:()=>$(!1),onSubmit:oe})]})}function St(){return null}const B=typeof{url:typeof document>"u"?require("url").pathToFileURL(__filename).href:G&&G.tagName.toUpperCase()==="SCRIPT"&&G.src||new URL("index.cjs",document.baseURI).href}<"u"&&"/"||"/",ze={en:{1:{front:`${B}templates/front_ldpi_en.png`,back:`${B}templates/back_ldpi_en.png`,width:1536,height:1024},5:{front:`${B}templates/front_ldpi_en.png`,back:`${B}templates/back_ldpi_en.png`,width:1536,height:1024},10:{front:`${B}templates/front_ldpi_en.png`,back:`${B}templates/back_ldpi_en.png`,width:1536,height:1024}},de:{1:{front:`${B}templates/front_hdpi_de.webp`,back:`${B}templates/back_hdpi_de.webp`,width:6144,height:3200},5:{front:`${B}templates/front_hdpi_de.webp`,back:`${B}templates/back_hdpi_de.webp`,width:6144,height:3200},10:{front:`${B}templates/front_hdpi_de.webp`,back:`${B}templates/back_hdpi_de.webp`,width:6144,height:3200}}},F={front:{portrait:{x:768,y:490,radiusX:236,radiusY:258},namePlate:{x:768,y:848,fontSize:36,maxWidth:380,align:"center"}},back:{portrait:{x:0,y:0,radiusX:0,radiusY:0},namePlate:{x:768,y:832,fontSize:36,maxWidth:380,align:"center"},contactInfo:{x:380,y:500,fontSize:38,lineHeight:55,align:"center"},description:{x:1150,y:500,fontSize:38,maxWidth:480,lineHeight:42,align:"center"}}},O={front:{portrait:{x:3074,y:1530,radiusX:942,radiusY:1020},namePlate:{x:3072,y:2950,fontSize:144,maxWidth:1520,align:"center"}},back:{portrait:{x:0,y:0,radiusX:0,radiusY:0},namePlate:{x:3072,y:2930,fontSize:144,maxWidth:1520,align:"center"},contactInfo:{x:1520,y:1680,fontSize:160,lineHeight:280,align:"center"},description:{x:4600,y:1680,fontSize:145,maxWidth:2e3,lineHeight:210,align:"center"}}};function ye(e){return e==="de"?O:F}function Ie(e,t){return ze[e][t]}const ve=new Map;async function de(e){return ve.has(e)?ve.get(e):new Promise((t,n)=>{const r=new Image;r.crossOrigin="anonymous",r.onload=()=>{ve.set(e,r),t(r)},r.onerror=n,r.src=e})}function Pe(e,t,n,r){e.drawImage(t,0,0,n,r)}function Ue(e,t,n,r,a,s,l=1,c=0,d=0){e.save(),e.beginPath(),e.ellipse(n,r,a,s,0,0,Math.PI*2),e.closePath(),e.clip();const u=t.width/t.height,g=a/s,b=a*2,w=s*2;let x,h;u>g?(h=w,x=w*u):(x=b,h=b/u),x*=l,h*=l;const N=Math.max(0,(x-b)/2),I=Math.max(0,(h-w)/2),y=n-x/2+c*N,k=r-h/2+d*I;e.drawImage(t,y,k,x,h),e.restore()}function ke(e,t,n,r="#2a3a2a"){e.save(),e.font=`${n.fontSize}px "Times New Roman", serif`,e.textAlign=n.align||"center",e.textBaseline="middle",e.fillStyle=r,n.maxWidth?e.fillText(t,n.x,n.y,n.maxWidth):e.fillText(t,n.x,n.y),e.restore()}function Xe(e,t,n,r="#2a3a2a"){e.save(),e.font=`${n.fontSize}px "Times New Roman", serif`,e.textAlign=n.align||"center",e.textBaseline="top",e.fillStyle=r;const a=n.maxWidth||400,s=n.lineHeight||n.fontSize*1.4,l=t.split(" "),c=[];let d="";for(const b of l){const w=d?`${d} ${b}`:b;e.measureText(w).width>a&&d?(c.push(d),d=b):d=w}d&&c.push(d);const u=c.length*s;let g=n.y-u/2;for(const b of c)e.fillText(b,n.x,g),g+=s;e.restore()}function Fe(e,t,n,r,a,s="#2a3a2a"){e.save(),e.font=`${a.fontSize}px "Times New Roman", serif`,e.textAlign=a.align||"center",e.textBaseline="middle",e.fillStyle=s;const l=a.lineHeight||a.fontSize*1.8,c=[t,n,r].filter(Boolean),d=(c.length-1)*l;let u=a.y-d/2;for(const g of c)g&&(e.fillText(g,a.x,u),u+=l);e.restore()}async function je(e,t,n,r,a,s,l,c=1,d=0,u=0){const g=e.getContext("2d");if(!g)return;e.width=s,e.height=l,g.clearRect(0,0,s,l);const b=await de(t);if(Pe(g,b,s,l),n)try{const w=await de(n);Ue(g,w,a.portrait.x,a.portrait.y,a.portrait.radiusX,a.portrait.radiusY,c,d,u)}catch(w){console.error("Failed to load portrait:",w)}r&&ke(g,r,a.namePlate)}async function Ee(e,t,n,r,a,s,l,c,d){const u=e.getContext("2d");if(!u)return;e.width=c,e.height=d,u.clearRect(0,0,c,d);const g=await de(t);Pe(u,g,c,d),l.contactInfo&&(n||r||a)&&Fe(u,n,r,a,l.contactInfo),l.description&&s&&Xe(u,s,l.description),n&&ke(u,n,l.namePlate)}function Ct(){const e=f(p=>p.voucherConfig.language),t=f(p=>p.voucherConfig.hours),n=f(p=>p.voucherConfig.description),r=f(p=>p.personalInfo),a=f(p=>p.portrait),s=f(p=>p.currentSide),l=f(p=>p.flipSide),c=f(p=>p.setPortraitPan),d=H(e),u=m.useRef(null),g=m.useRef(null),b=m.useRef(null),[w,x]=m.useState(!1),[h,N]=m.useState(!1),I=m.useRef(null),y=Ie(e,t),k=ye(e),C=a.useEnhanced&&a.enhanced?a.enhanced:a.original,D=xe(e,t,n);m.useEffect(()=>{u.current&&je(u.current,y.front,C,r.name,k.front,y.width,y.height,a.zoom,a.panX,a.panY)},[y,C,r.name,k,a.zoom,a.panX,a.panY]),m.useEffect(()=>{g.current&&Ee(g.current,y.back,r.name,r.email,r.phone,D,k.back,y.width,y.height)},[y,r,D,k]);const j=()=>{x(!0),setTimeout(()=>{l(),x(!1)},150)},E=m.useCallback((p,R)=>{if(!b.current||s!=="front"||!a.original)return!1;const M=b.current.getBoundingClientRect(),W=M.width/y.width,J=M.height/y.height,Q=(p-M.left)/W,_=(R-M.top)/J,{x:q,y:ee,radiusX:ge,radiusY:me}=k.front.portrait,oe=(Q-q)/ge,ie=(_-ee)/me;return oe*oe+ie*ie<=1},[s,a.original,y.width,y.height,k.front.portrait]),$=m.useCallback((p,R)=>{a.zoom<=1||!E(p,R)||(N(!0),I.current={x:p,y:R,panX:a.panX,panY:a.panY})},[a.zoom,a.panX,a.panY,E]),z=m.useCallback((p,R)=>{if(!h||!I.current||!b.current)return;const M=b.current.getBoundingClientRect(),W=3/Math.max(M.width,M.height),J=(p-I.current.x)*W,Q=(R-I.current.y)*W,_=Math.max(-1,Math.min(1,I.current.panX+J)),q=Math.max(-1,Math.min(1,I.current.panY+Q));c(_,q)},[h,c]),U=m.useCallback(()=>{N(!1),I.current=null},[]),A=p=>{a.zoom>1&&E(p.clientX,p.clientY)&&(p.preventDefault(),$(p.clientX,p.clientY))},X=p=>{z(p.clientX,p.clientY)},L=()=>U(),S=()=>U(),te=p=>{if(p.touches.length===1){const R=p.touches[0];a.zoom>1&&E(R.clientX,R.clientY)&&$(R.clientX,R.clientY)}},K=p=>{p.touches.length===1&&z(p.touches[0].clientX,p.touches[0].clientY)},ne=()=>U(),ae=s==="front"&&a.original&&a.zoom>1,re=y.width/y.height;return o.jsxs("div",{className:"space-y-4",children:[o.jsxs("div",{className:"flex justify-between items-center",children:[o.jsxs("div",{className:"tabs tabs-boxed bg-base-200",children:[o.jsx("button",{className:`tab ${s==="front"?"tab-active bg-primary text-primary-content font-semibold":""}`,onClick:()=>s!=="front"&&j(),children:d.preview.front}),o.jsx("button",{className:`tab ${s==="back"?"tab-active bg-primary text-primary-content font-semibold":""}`,onClick:()=>s!=="back"&&j(),children:d.preview.back})]}),o.jsxs("button",{className:"btn btn-ghost btn-sm",onClick:j,children:[o.jsx("svg",{xmlns:"http://www.w3.org/2000/svg",className:"h-5 w-5",fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",children:o.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"})}),d.preview.flip]})]}),o.jsxs("div",{ref:b,className:`relative w-full overflow-hidden shadow-lg ${ae?h?"cursor-grabbing":"cursor-grab":""}`,style:{aspectRatio:re},onMouseDown:A,onMouseMove:X,onMouseUp:L,onMouseLeave:S,onTouchStart:te,onTouchMove:K,onTouchEnd:ne,children:[o.jsx("canvas",{ref:u,className:`absolute inset-0 w-full h-full transition-all duration-300 ${s==="front"?w?"opacity-0 scale-95":"opacity-100 scale-100":"opacity-0 scale-95 pointer-events-none"}`}),o.jsx("canvas",{ref:g,className:`absolute inset-0 w-full h-full transition-all duration-300 ${s==="back"?w?"opacity-0 scale-95":"opacity-100 scale-100":"opacity-0 scale-95 pointer-events-none"}`})]})]})}function Nt(){const e=m.useRef(null),t=m.useRef(null);return{frontCanvasRef:e,backCanvasRef:t}}async function Oe(e){const{frontTemplateSrc:t,backTemplateSrc:n,templateWidth:r,templateHeight:a,layout:s,portrait:l,portraitZoom:c=1,portraitPanX:d=0,portraitPanY:u=0,name:g,email:b,phone:w,description:x}=e,h=document.createElement("canvas"),N=document.createElement("canvas");await Promise.all([je(h,t,l,g,s.front,r,a,c,d,u),Ee(N,n,g,b,w,x,s.back,r,a)]);const I=new lt({orientation:"landscape",unit:"mm",format:"a4"}),y=297,k=210,C=10,D=r/a;let j=y-C*2,E=j/D;E>k-C*2&&(E=k-C*2,j=E*D);const $=(y-j)/2,z=(k-E)/2,U=h.toDataURL("image/jpeg",.95);I.addImage(U,"JPEG",$,z,j,E),I.addPage();const A=N.toDataURL("image/jpeg",.95);return I.addImage(A,"JPEG",$,z,j,E),I.output("blob")}function He(e,t){const n=URL.createObjectURL(e),r=document.createElement("a");r.href=n,r.download=t,document.body.appendChild(r),r.click(),document.body.removeChild(r),URL.revokeObjectURL(n)}async function Ke(e){const t=await Oe(e);He(t,e.filename)}function Rt(){const e=f(h=>h.voucherConfig.language),t=f(h=>h.voucherConfig.hours),n=f(h=>h.voucherConfig.description),r=f(h=>h.personalInfo),a=f(h=>h.portrait),s=f(h=>h.isExporting),l=f(h=>h.setIsExporting),c=H(e),d=Ie(e,t),u=ye(e),g=a.useEnhanced&&a.enhanced?a.enhanced:a.original,b=xe(e,t,n),w=r.name.trim().length>0&&r.email.trim().length>0&&r.phone.trim().length>0&&a.original!==null,x=async()=>{if(!(!w||s)){l(!0);try{const h=`zeitgutschein-${t}h-${r.name.replace(/\s+/g,"-").toLowerCase()}.pdf`;await Ke({frontTemplateSrc:d.front,backTemplateSrc:d.back,templateWidth:d.width,templateHeight:d.height,layout:u,portrait:g,portraitZoom:a.zoom,portraitPanX:a.panX,portraitPanY:a.panY,name:r.name,email:r.email,phone:r.phone,description:b,filename:h})}catch(h){console.error("PDF export failed:",h)}finally{l(!1)}}};return o.jsx("button",{className:`btn btn-primary flex-1 ${s?"loading":""}`,onClick:x,disabled:!w||s,children:s?o.jsxs(o.Fragment,{children:[o.jsx("span",{className:"loading loading-spinner loading-sm"}),c.export.exporting]}):o.jsxs(o.Fragment,{children:[o.jsx("svg",{xmlns:"http://www.w3.org/2000/svg",className:"h-5 w-5",fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",children:o.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"})}),c.export.button]})})}function We(){const e=f(r=>r.voucherConfig.language),t=f(r=>r.setLanguage),n=r=>{t(r)};return o.jsxs("div",{className:"join",children:[o.jsx("button",{className:`join-item btn btn-sm ${e==="de"?"btn-active btn-primary":"btn-ghost"}`,onClick:()=>n("de"),children:"DE"}),o.jsx("button",{className:`join-item btn btn-sm ${e==="en"?"btn-active btn-primary":"btn-ghost"}`,onClick:()=>n("en"),children:"EN"})]})}function Tt(){const e=f(n=>n.voucherConfig.language),t=H(e);return o.jsxs("div",{className:"navbar bg-currency-green text-currency-cream shadow-lg",children:[o.jsx("div",{className:"navbar-start",children:o.jsx("a",{className:"btn btn-ghost text-xl font-currency font-bold",children:t.header.title})}),o.jsx("div",{className:"navbar-center hidden sm:flex",children:o.jsx("span",{className:"text-sm opacity-80",children:t.header.subtitle})}),o.jsx("div",{className:"navbar-end",children:o.jsx(We,{})})]})}const ue="/",Bt={id:"time-voucher-classic-de",name:"Zeitgutschein Classic",type:"time-voucher",category:"classic",images:{front:`${ue}templates/front_hdpi_de.jpg`,back:`${ue}templates/back_hdpi_de.jpg`,width:6144,height:4096},fields:[{id:"name",type:"text",label:{de:"Name",en:"Name"},required:!0,validation:{minLength:1,maxLength:50}},{id:"hours",type:"select",label:{de:"Stunden",en:"Hours"},required:!0,options:["1","5","10"]},{id:"portrait",type:"image",label:{de:"Portrait",en:"Portrait"},required:!1},{id:"email",type:"text",label:{de:"E-Mail",en:"Email"},required:!1},{id:"phone",type:"text",label:{de:"Telefon",en:"Phone"},required:!1},{id:"description",type:"textarea",label:{de:"Beschreibung",en:"Description"},required:!1,validation:{maxLength:200}}],layout:{front:{portrait:O.front.portrait,name:O.front.namePlate},back:{name:O.back.namePlate,contactInfo:O.back.contactInfo,description:O.back.description}},languages:["de"]},At={id:"time-voucher-classic-en",name:"Time Voucher Classic",type:"time-voucher",category:"classic",images:{front:`${ue}templates/front_ldpi_en.png`,back:`${ue}templates/back_ldpi_en.png`,width:1536,height:1024},fields:[{id:"name",type:"text",label:{de:"Name",en:"Name"},required:!0,validation:{minLength:1,maxLength:50}},{id:"hours",type:"select",label:{de:"Stunden",en:"Hours"},required:!0,options:["1","5","10"]},{id:"portrait",type:"image",label:{de:"Portrait",en:"Portrait"},required:!1},{id:"email",type:"text",label:{de:"E-Mail",en:"Email"},required:!1},{id:"phone",type:"text",label:{de:"Telefon",en:"Phone"},required:!1},{id:"description",type:"textarea",label:{de:"Beschreibung",en:"Description"},required:!1,validation:{maxLength:200}}],layout:{front:{portrait:F.front.portrait,name:F.front.namePlate},back:{name:F.back.namePlate,contactInfo:F.back.contactInfo,description:F.back.description}},languages:["en"]},Be=[Bt,At],qe={async listTemplates(e){let t=[...Be];return e!=null&&e.type&&(t=t.filter(n=>n.type===e.type)),e!=null&&e.category&&(t=t.filter(n=>n.category===e.category)),e!=null&&e.language&&(t=t.filter(n=>n.languages.includes(e.language))),t},async getTemplate(e){const t=Be.find(n=>n.id===e);if(!t)throw new Error(`Template not found: ${e}`);return t}};function Lt(e){return e==="de"?"time-voucher-classic-de":"time-voucher-classic-en"}let pe=qe;function Dt(e){pe=e}function Mt(){return pe}async function _t(e){return pe.listTemplates(e)}async function Yt(e){return pe.getTemplate(e)}exports.ApiKeyModal=$e;exports.BillPreview=Ct;exports.ExportButton=Rt;exports.Header=Tt;exports.LAYOUT_HDPI=O;exports.LAYOUT_LDPI=F;exports.LanguageToggle=We;exports.PersonalInfoForm=ht;exports.PortraitUpload=Et;exports.TEMPLATES=ze;exports.VoucherConfig=St;exports.applyEngravingEffect=Le;exports.downloadBlob=He;exports.drawContactInfo=Fe;exports.drawMultilineText=Xe;exports.drawOvalPortrait=Ue;exports.drawTemplate=Pe;exports.drawText=ke;exports.enhancePortrait=kt;exports.exportBillAsPDF=Ke;exports.formatDescription=xe;exports.generateBillPDF=Oe;exports.getApiKey=he;exports.getDefaultTemplateId=Lt;exports.getLayout=ye;exports.getRemoveBackgroundEndpoint=wt;exports.getTemplate=Ie;exports.getTemplateById=Yt;exports.getTemplateProvider=Mt;exports.hasApiKey=we;exports.hasCustomEndpoint=xt;exports.listTemplates=_t;exports.loadImage=de;exports.removeBackground=Ye;exports.renderBackSide=Ee;exports.renderFrontSide=je;exports.setApiKey=_e;exports.setRemoveBackgroundEndpoint=vt;exports.setTemplateProvider=Dt;exports.staticTemplateProvider=qe;exports.t=H;exports.useBillCanvasRefs=Nt;exports.useBillStore=f;
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const i=require("react/jsx-runtime"),ht=require("zustand"),pt=require("zustand/middleware"),m=require("react"),mt=require("jspdf");var F=typeof document<"u"?document.currentScript:null;const Le={personalInfo:{name:"",email:"",phone:""},voucherConfig:{hours:1,description:"",language:"de",templateHue:160},portrait:{original:null,enhanced:null,useEnhanced:!1,zoom:1,panX:0,panY:0,rawImage:null,bgRemovedImage:null,bgRemoved:!1,bgOpacity:0,bgBlur:0,engravingIntensity:0},currentSide:"front",isEnhancing:!1,isExporting:!1},w=ht.create()(pt.persist(e=>({...Le,setPersonalInfo:t=>e(n=>({personalInfo:{...n.personalInfo,...t}})),setVoucherConfig:t=>e(n=>({voucherConfig:{...n.voucherConfig,...t}})),setPortrait:(t,n=null)=>e(r=>({portrait:{...r.portrait,original:t,enhanced:n,useEnhanced:!1,zoom:r.portrait.original&&t?r.portrait.zoom:1,panX:r.portrait.original&&t?r.portrait.panX:0,panY:r.portrait.original&&t?r.portrait.panY:0}})),setEnhancedPortrait:t=>e(n=>({portrait:{...n.portrait,enhanced:t,useEnhanced:t!==null}})),toggleUseEnhanced:()=>e(t=>({portrait:{...t.portrait,useEnhanced:t.portrait.enhanced?!t.portrait.useEnhanced:!1}})),setPortraitZoom:t=>e(n=>({portrait:{...n.portrait,zoom:t}})),setPortraitPan:(t,n)=>e(r=>({portrait:{...r.portrait,panX:t,panY:n}})),setPortraitRawImage:t=>e(n=>({portrait:{...n.portrait,rawImage:t}})),setPortraitBgRemoved:(t,n)=>e(r=>({portrait:{...r.portrait,bgRemoved:t,bgRemovedImage:n!==void 0?n:r.portrait.bgRemovedImage}})),setPortraitBgOpacity:t=>e(n=>({portrait:{...n.portrait,bgOpacity:t}})),setPortraitBgBlur:t=>e(n=>({portrait:{...n.portrait,bgBlur:t}})),setPortraitEngravingIntensity:t=>e(n=>({portrait:{...n.portrait,engravingIntensity:t}})),setCurrentSide:t=>e({currentSide:t}),flipSide:()=>e(t=>({currentSide:t.currentSide==="front"?"back":"front"})),setIsEnhancing:t=>e({isEnhancing:t}),setIsExporting:t=>e({isExporting:t}),setLanguage:t=>e(n=>({voucherConfig:{...n.voucherConfig,language:t}})),setHours:t=>e(n=>({voucherConfig:{...n.voucherConfig,hours:t}})),setTemplateHue:t=>e(n=>({voucherConfig:{...n.voucherConfig,templateHue:t}})),reset:()=>e(Le)}),{name:"money-generator-storage",partialize:e=>({personalInfo:e.personalInfo,voucherConfig:e.voucherConfig,portrait:{original:null,enhanced:null,useEnhanced:e.portrait.useEnhanced,zoom:e.portrait.zoom,panX:e.portrait.panX,panY:e.portrait.panY,rawImage:null,bgRemovedImage:null,bgRemoved:!1,bgOpacity:0,bgBlur:0,engravingIntensity:e.portrait.engravingIntensity}})})),gt={header:{title:"Money Generator",subtitle:"Erstelle deinen persönlichen Zeitgutschein"},form:{personalInfo:{title:"Persönliche Daten",name:"Name",namePlaceholder:"Dein Name",email:"E-Mail",emailPlaceholder:"deine@email.de",phone:"Telefon",phonePlaceholder:"+49 123 456789"},portrait:{title:"Portrait",upload:"Bild hochladen",dragDrop:"oder hierher ziehen",enhance:"Mit AI verbessern",enhancing:"Wird verbessert...",useOriginal:"Original verwenden",useEnhanced:"Verbessertes verwenden",zoom:"Zoom"},voucher:{title:"Gutschein",hours:"Stunden",hourLabel:"Stunde",hoursLabel:"Stunden",description:"Beschreibung",descriptionPlaceholder:"Was kann mit diesem Gutschein eingelöst werden?"},billColor:{title:"Scheinfarbe",label:"Farbton"}},preview:{front:"Vorderseite",back:"Rückseite",flip:"Umdrehen"},export:{button:"Als PDF herunterladen",exporting:"PDF wird erstellt...",success:"Download gestartet!"},bill:{descriptionText:"Für diesen Schein erhältst du {hours} {hourLabel} meiner Zeit oder ein gleichwertiges Dankeschön"}},ft={header:{title:"Money Generator",subtitle:"Create your personal time voucher"},form:{personalInfo:{title:"Personal Information",name:"Name",namePlaceholder:"Your name",email:"Email",emailPlaceholder:"your@email.com",phone:"Phone",phonePlaceholder:"+1 234 567890"},portrait:{title:"Portrait",upload:"Upload image",dragDrop:"or drag and drop",enhance:"Enhance with AI",enhancing:"Enhancing...",useOriginal:"Use original",useEnhanced:"Use enhanced",zoom:"Zoom"},voucher:{title:"Voucher",hours:"Hours",hourLabel:"hour",hoursLabel:"hours",description:"Description",descriptionPlaceholder:"What can be redeemed with this voucher?"},billColor:{title:"Bill Color",label:"Hue"}},preview:{front:"Front",back:"Back",flip:"Flip"},export:{button:"Download as PDF",exporting:"Creating PDF...",success:"Download started!"},bill:{descriptionText:"This voucher entitles you to {hours} {hourLabel} of my time or an equivalent thank you"}},bt={de:gt,en:ft};function V(e){return bt[e]}function Ee(e,t,n){if(n&&n.trim())return n;const r=V(e),a=t===1?r.form.voucher.hourLabel:r.form.voucher.hoursLabel;return r.bill.descriptionText.replace("{hours}",t.toString()).replace("{hourLabel}",a)}function wt(){const e=w(a=>a.voucherConfig.language),t=w(a=>a.personalInfo),n=w(a=>a.setPersonalInfo),r=V(e);return i.jsxs("div",{className:"space-y-4",children:[i.jsxs("div",{className:"form-control",children:[i.jsx("label",{className:"label",children:i.jsx("span",{className:"label-text font-medium",children:r.form.personalInfo.name})}),i.jsx("input",{type:"text",placeholder:r.form.personalInfo.namePlaceholder,className:"input input-bordered w-full",value:t.name,onChange:a=>n({name:a.target.value})})]}),i.jsxs("div",{className:"form-control",children:[i.jsx("label",{className:"label",children:i.jsx("span",{className:"label-text font-medium",children:r.form.personalInfo.email})}),i.jsx("input",{type:"email",placeholder:r.form.personalInfo.emailPlaceholder,className:"input input-bordered w-full",value:t.email,onChange:a=>n({email:a.target.value})})]}),i.jsxs("div",{className:"form-control",children:[i.jsx("label",{className:"label",children:i.jsx("span",{className:"label-text font-medium",children:r.form.personalInfo.phone})}),i.jsx("input",{type:"tel",placeholder:r.form.personalInfo.phonePlaceholder,className:"input input-bordered w-full",value:t.phone,onChange:a=>n({phone:a.target.value})})]})]})}const vt=1024,Q=new Map;let z=null,Me=null;function je(e,t){return z||(z=document.createElement("canvas"),Me=z.getContext("2d",{willReadFrequently:!0})),(z.width!==e||z.height!==t)&&(z.width=e,z.height=t),Me}function re(e){const t=Q.get(e);return t?Promise.resolve(t):new Promise((n,r)=>{const a=new Image;a.onload=()=>{if(Q.size>10){const o=Q.keys().next().value;o&&Q.delete(o)}Q.set(e,a),n(a)},a.onerror=()=>r(new Error("Failed to load image")),a.src=e})}function yt(){Q.clear()}async function xt(e,t=vt){const n=await re(e),r=Math.max(n.width,n.height);if(r<=t)return e;const a=t/r,o=Math.round(n.width*a),s=Math.round(n.height*a),d=document.createElement("canvas"),c=d.getContext("2d");if(!c)throw new Error("Failed to get canvas context");return d.width=o,d.height=s,c.drawImage(n,0,0,o,s),d.toDataURL("image/jpeg",.9)}async function ye(e,t,n,r=0){const[a,o]=await Promise.all([re(e),re(t)]),s=je(a.width,a.height);if(s.clearRect(0,0,a.width,a.height),n>0){const d=r*20;s.save(),s.globalAlpha=n,d>0&&(s.filter=`blur(${d}px)`),s.drawImage(o,0,0,a.width,a.height),s.restore()}return s.drawImage(a,0,0),z.toDataURL("image/png")}async function Ue(e,t=.5){const n=await re(e),r=je(n.width,n.height);r.drawImage(n,0,0);const a=r.getImageData(0,0,n.width,n.height),o=a.data,s=new Uint32Array(o.buffer),d=1+t*.8,c=1-t;for(let u=0;u<s.length;u++){const h=s[u],p=h&255,v=h>>8&255,g=h>>16&255,x=h>>24&255;if(x===0)continue;let j=(((77*p+150*v+29*g>>8)/255-.5)*d+.5)*255;j<0?j=0:j>255&&(j=255);let S=j*.9+25,P=j*.78+15,I=j*.55+5;S>255&&(S=255),P>255&&(P=255),I>255&&(I=255);const C=p*c+S*t|0,R=v*c+P*t|0,B=g*c+I*t|0;s[u]=x<<24|B<<16|R<<8|C}return r.putImageData(a,0,0),z.toDataURL("image/png")}function It(e,t,n){e/=255,t/=255,n/=255;const r=Math.max(e,t,n),a=Math.min(e,t,n),o=(r+a)/2;let s=0,d=0;if(r!==a){const c=r-a;switch(d=o>.5?c/(2-r-a):c/(r+a),r){case e:s=((t-n)/c+(t<n?6:0))/6;break;case t:s=((n-e)/c+2)/6;break;case n:s=((e-t)/c+4)/6;break}}return[s,d,o]}function Pt(e,t,n){if(t===0){const s=Math.round(n*255);return[s,s,s]}const r=(s,d,c)=>(c<0&&(c+=1),c>1&&(c-=1),c<1/6?s+(d-s)*6*c:c<1/2?d:c<2/3?s+(d-s)*(2/3-c)*6:s),a=n<.5?n*(1+t):n+t-n*t,o=2*n-a;return[Math.round(r(o,a,e+1/3)*255),Math.round(r(o,a,e)*255),Math.round(r(o,a,e-1/3)*255)]}async function He(e,t){if(t>=155&&t<=165)return e;const n=await re(e),r=je(n.width,n.height);r.drawImage(n,0,0);const a=r.getImageData(0,0,n.width,n.height),o=a.data,s=new Uint32Array(o.buffer),d=t%360/360,c=60/360,u=260/360,h=1e5,p=s.length;for(let v=0;v<p;v+=h){const g=Math.min(v+h,p);for(let x=v;x<g;x++){const f=s[x],j=f&255,S=f>>8&255,P=f>>16&255,I=f>>24&255;if(I===0)continue;const[C,R,B]=It(j,S,P);if(R<.06||C<c||C>u)continue;const _=d,[M,H,N]=Pt(_,R,B);s[x]=I<<24|N<<16|H<<8|M}g<p&&await new Promise(x=>setTimeout(x,0))}return r.putImageData(a,0,0),z.toDataURL("image/png")}const kt="https://api.stability.ai/v1/generation",Et="https://api.stability.ai/v2beta/stable-image/edit/remove-background",ze="stability_api_key";let te=null;function jt(e){te=e}function St(){return te}function Ct(){return te!==null}const Rt={vintage:"portrait in the style of vintage currency engraving, fine line work, crosshatching, sepia tones, detailed stippling, classic bank note portrait style",engraved:"portrait as detailed intaglio engraving, currency bill style, fine parallel lines, high contrast, official government portrait",currency:"portrait rendered as US dollar bill engraving, official currency portrait style, green tint, fine line engraving technique"},De=[{width:1024,height:1024},{width:1152,height:896},{width:1216,height:832},{width:1344,height:768},{width:1536,height:640},{width:640,height:1536},{width:768,height:1344},{width:832,height:1216},{width:896,height:1152}];function Tt(e,t){const n=e/t;let r=De[0],a=1/0;for(const o of De){const s=o.width/o.height,d=Math.abs(n-s);d<a&&(a=d,r=o)}return r}function Nt(e){return new Promise((t,n)=>{const r=new Image;r.onload=()=>{const a=Tt(r.width,r.height),o=document.createElement("canvas");o.width=a.width,o.height=a.height;const s=o.getContext("2d");if(!s){n(new Error("Failed to get canvas context"));return}const d=r.width/r.height,c=a.width/a.height;let u=0,h=0,p=r.width,v=r.height;d>c?(p=r.height*c,u=(r.width-p)/2):(v=r.width/c,h=(r.height-v)/2),s.drawImage(r,u,h,p,v,0,0,a.width,a.height),t(o.toDataURL("image/png"))},r.onerror=()=>n(new Error("Failed to load image")),r.src=e})}function Ye(e){var s;const t=e.split(","),n=((s=t[0].match(/:(.*?);/))==null?void 0:s[1])||"image/png",r=atob(t[1]),a=r.length,o=new Uint8Array(a);for(let d=0;d<a;d++)o[d]=r.charCodeAt(d);return new Blob([o],{type:n})}function pe(){var t;const e=typeof{url:typeof document>"u"?require("url").pathToFileURL(__filename).href:F&&F.tagName.toUpperCase()==="SCRIPT"&&F.src||new URL("index.cjs",document.baseURI).href}<"u"&&"sk-7mEKklrqaltdtgbX0VoZbkA8E1cl939Spn75jSIYRvp1BW0b"||typeof process<"u"&&((t=process.env)==null?void 0:t.NEXT_PUBLIC_STABILITY_API_KEY);return e&&e!=="your-api-key-here"?e:typeof localStorage<"u"?localStorage.getItem(ze):null}function $e(e){localStorage.setItem(ze,e)}function Pe(){return te?!0:pe()!==null}async function Bt(e){const t=pe();if(!t)throw new Error("No Stability AI API key configured");const{imageDataUrl:n,style:r,strength:a=.35}=e,o=await Nt(n),s=Ye(o),d=new FormData;d.append("init_image",s,"portrait.png"),d.append("init_image_mode","IMAGE_STRENGTH"),d.append("image_strength",String(1-a)),d.append("text_prompts[0][text]",Rt[r]),d.append("text_prompts[0][weight]","1"),d.append("cfg_scale","7"),d.append("samples","1"),d.append("steps","30");const u=await fetch(`${kt}/stable-diffusion-xl-1024-v1-0/image-to-image`,{method:"POST",headers:{Authorization:`Bearer ${t}`,Accept:"application/json"},body:d});if(!u.ok){const p=await u.text();throw console.error("Stability AI error:",p),u.status===401?new Error("Invalid API key"):u.status===402?new Error("Insufficient credits"):u.status===429?new Error("Rate limit exceeded. Please try again later."):new Error(`API error: ${u.status}`)}const h=await u.json();if(!h.artifacts||h.artifacts.length===0)throw new Error("No image generated");return`data:image/png;base64,${h.artifacts[0].base64}`}async function Xe(e){if(te){const s=await fetch(te,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({imageDataUrl:e})});if(!s.ok){const c=await s.json().catch(()=>({}));throw new Error(c.error||`API error: ${s.status}`)}return(await s.json()).imageDataUrl}const t=pe();if(!t)throw new Error("No Stability AI API key configured");const n=Ye(e),r=new FormData;r.append("image",n,"image.png"),r.append("output_format","png");const a=await fetch(Et,{method:"POST",headers:{Authorization:`Bearer ${t}`,Accept:"image/*"},body:r});if(!a.ok){const s=await a.text();throw console.error("Stability AI remove background error:",s),a.status===401?new Error("Invalid API key"):a.status===402?new Error("Insufficient credits"):a.status===429?new Error("Rate limit exceeded. Please try again later."):new Error(`API error: ${a.status}`)}const o=await a.blob();return new Promise((s,d)=>{const c=new FileReader;c.onload=()=>s(c.result),c.onerror=()=>d(new Error("Failed to read result")),c.readAsDataURL(o)})}function Lt(){const[e,t]=m.useState(!1),[n,r]=m.useState(!1),[a,o]=m.useState(null),[s,d]=m.useState(()=>Pe());m.useEffect(()=>{d(Pe())},[]);const c=m.useCallback(()=>o(null),[]),u=m.useCallback(v=>{$e(v),d(!0),o(null)},[]),h=m.useCallback(async(v,g=.5)=>{t(!0),o(null);try{return await Ue(v,g)}catch(x){const f=x instanceof Error?x.message:"Enhancement failed";throw o(f),x}finally{t(!1)}},[]),p=m.useCallback(async v=>{r(!0),o(null);try{return await Xe(v)}catch(g){const x=g instanceof Error?g.message:"Background removal failed";throw o(x),g}finally{r(!1)}},[]);return{enhance:h,removeBg:p,isEnhancing:e,isRemovingBg:n,error:a,hasKey:s,setApiKey:u,clearError:c}}function Fe({isOpen:e,onClose:t,onSubmit:n}){const r=w(u=>u.voucherConfig.language),[a,o]=m.useState("");if(!e)return null;const s=u=>{u.preventDefault(),a.trim()&&(n(a.trim()),o(""),t())},c={de:{title:"Stability AI API Key",description:"Um die AI-Bildverbesserung zu nutzen, benötigst du einen Stability AI API Key. Du kannst ihn auf platform.stability.ai erhalten.",placeholder:"sk-...",submit:"Speichern",cancel:"Abbrechen",hint:"Der Key wird lokal in deinem Browser gespeichert."},en:{title:"Stability AI API Key",description:"To use AI image enhancement, you need a Stability AI API key. You can get one at platform.stability.ai.",placeholder:"sk-...",submit:"Save",cancel:"Cancel",hint:"The key is stored locally in your browser."}}[r];return i.jsxs("dialog",{className:"modal modal-open",children:[i.jsxs("div",{className:"modal-box",children:[i.jsx("h3",{className:"font-bold text-lg",children:c.title}),i.jsx("p",{className:"py-4 text-sm opacity-80",children:c.description}),i.jsxs("form",{onSubmit:s,children:[i.jsxs("div",{className:"form-control",children:[i.jsx("input",{type:"password",placeholder:c.placeholder,className:"input input-bordered w-full",value:a,onChange:u=>o(u.target.value),autoFocus:!0}),i.jsx("label",{className:"label",children:i.jsx("span",{className:"label-text-alt",children:c.hint})})]}),i.jsxs("div",{className:"modal-action",children:[i.jsx("button",{type:"button",className:"btn btn-ghost",onClick:t,children:c.cancel}),i.jsx("button",{type:"submit",className:"btn btn-primary",disabled:!a.trim(),children:c.submit})]})]})]}),i.jsx("form",{method:"dialog",className:"modal-backdrop",children:i.jsx("button",{onClick:t,children:"close"})})]})}function Mt(){const e=w(l=>l.voucherConfig.language),t=w(l=>l.portrait),n=w(l=>l.setPortrait),r=w(l=>l.setPortraitZoom),a=w(l=>l.setPortraitPan),o=w(l=>l.setPortraitRawImage),s=w(l=>l.setPortraitBgRemoved),d=w(l=>l.setPortraitBgOpacity),c=w(l=>l.setPortraitBgBlur),u=w(l=>l.setPortraitEngravingIntensity),{enhance:h,removeBg:p,isEnhancing:v,isRemovingBg:g,error:x,hasKey:f,setApiKey:j}=Lt(),S=V(e),P=m.useRef(null),I=m.useRef(null),[C,R]=m.useState(!1),[B,_]=m.useState(!1),[M,H]=m.useState(!1),N=m.useRef(null),U=m.useRef(null),T=m.useRef(null),k=t.rawImage,Y=t.bgRemovedImage,O=t.bgRemoved,oe=t.bgOpacity,ie=t.bgBlur,se=t.engravingIntensity;m.useEffect(()=>{t.original&&!t.rawImage&&o(t.original)},[t.original,t.rawImage,o]),m.useEffect(()=>()=>{U.current&&clearTimeout(U.current),T.current&&clearTimeout(T.current)},[]);const ne=m.useCallback(async l=>{if(!l.type.startsWith("image/"))return;const y=new FileReader;y.onload=async E=>{var ue;const D=(ue=E.target)==null?void 0:ue.result,de=await xt(D);o(de),n(de),s(!1,null),u(0)},y.readAsDataURL(l)},[n,o,s,u]),ge=m.useCallback(l=>{l.preventDefault(),R(!1);const y=l.dataTransfer.files[0];y&&ne(y)},[ne]),b=m.useCallback(l=>{l.preventDefault(),R(!0)},[]),L=m.useCallback(l=>{l.preventDefault(),R(!1)},[]),$=()=>{var l;(l=P.current)==null||l.click()},Z=l=>{var E;const y=(E=l.target.files)==null?void 0:E[0];y&&ne(y)},X=m.useCallback(async(l,y)=>{try{return await h(l,y)}catch(E){return console.error("Enhancement failed:",E),l}},[h]),G=m.useCallback(async l=>{try{const y=await p(l);return s(!0,y),y}catch(y){return console.error("Background removal failed:",y),l}},[p,s]),W=m.useCallback(async()=>{const l=w.getState().portrait;if(!l.rawImage)return;let y;l.bgRemoved&&l.bgRemovedImage?y=await ye(l.bgRemovedImage,l.rawImage,l.bgOpacity,l.bgBlur):y=l.rawImage,l.engravingIntensity>0&&(y=await X(y,l.engravingIntensity)),n(y)},[X,n]),ae=async()=>{if(!k)return;if(!O&&!f){_(!0);return}if(!O){const y=await G(k),E=w.getState().portrait;let D=await ye(y,k,E.bgOpacity,E.bgBlur);E.engravingIntensity>0&&(D=await X(D,E.engravingIntensity)),n(D)}else{s(!1,null);const y=w.getState().portrait.engravingIntensity;if(y>0){const E=await X(k,y);n(E)}else n(k)}},fe=l=>{u(l),U.current&&clearTimeout(U.current),k&&(U.current=setTimeout(W,150))},be=async l=>{if(j(l),!k)return;const y=await G(k),E=w.getState().portrait;let D=await ye(y,k,E.bgOpacity,E.bgBlur);E.engravingIntensity>0&&(D=await X(D,E.engravingIntensity)),n(D)},we=l=>{d(l),T.current&&clearTimeout(T.current),!(!k||!Y)&&(T.current=setTimeout(W,150))},le=l=>{c(l),T.current&&clearTimeout(T.current),!(!k||!Y)&&(T.current=setTimeout(W,150))},ce=()=>{n(null),o(null),s(!1,null),d(0),c(0),u(0),yt()},Ne=(l,y)=>{t.zoom<=1||(H(!0),N.current={x:l,y,panX:t.panX,panY:t.panY})},Be=(l,y)=>{if(!M||!N.current||!I.current)return;const E=I.current.getBoundingClientRect(),D=2/Math.max(E.width,E.height),de=(l-N.current.x)*D,ue=(y-N.current.y)*D,dt=Math.max(-1,Math.min(1,N.current.panX+de)),ut=Math.max(-1,Math.min(1,N.current.panY+ue));a(dt,ut)},ve=()=>{H(!1),N.current=null},at=l=>{l.preventDefault(),Ne(l.clientX,l.clientY)},rt=l=>{Be(l.clientX,l.clientY)},ot=()=>{ve()},it=()=>{ve()},st=l=>{l.touches.length===1&&Ne(l.touches[0].clientX,l.touches[0].clientY)},lt=l=>{l.touches.length===1&&Be(l.touches[0].clientX,l.touches[0].clientY)},ct=()=>{ve()};return i.jsxs("div",{className:"space-y-4",children:[t.original?i.jsxs("div",{className:"flex flex-col items-center space-y-4",children:[i.jsxs("div",{className:"relative",children:[i.jsx("div",{ref:I,className:`w-32 h-32 rounded-full overflow-hidden border-4 border-currency-gold shadow-lg ${t.zoom>1?"cursor-grab":""} ${M?"cursor-grabbing":""}`,onMouseDown:at,onMouseMove:rt,onMouseUp:ot,onMouseLeave:it,onTouchStart:st,onTouchMove:lt,onTouchEnd:ct,children:i.jsx("img",{src:t.original||"",alt:"Portrait",className:"w-full h-full object-cover pointer-events-none select-none",style:{transform:`scale(${t.zoom}) translate(${t.panX*50*(t.zoom-1)}%, ${t.panY*50*(t.zoom-1)}%)`},draggable:!1})}),i.jsx("button",{className:"btn btn-circle btn-xs btn-error absolute -top-1 -right-1",onClick:ce,children:i.jsx("svg",{xmlns:"http://www.w3.org/2000/svg",className:"h-4 w-4",fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",children:i.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M6 18L18 6M6 6l12 12"})})})]}),i.jsxs("div",{className:"grid grid-cols-1 sm:grid-cols-2 gap-4 w-full",children:[i.jsxs("div",{className:"form-control w-full",children:[i.jsxs("label",{className:"label",children:[i.jsx("span",{className:"label-text",children:S.form.portrait.zoom}),i.jsxs("span",{className:"label-text-alt",children:[Math.round(t.zoom*100),"%"]})]}),i.jsx("input",{type:"range",min:"0.5",max:"2",step:"0.05",value:t.zoom,onChange:l=>{const y=parseFloat(l.target.value);r(y),y<=1&&(t.panX!==0||t.panY!==0)&&a(0,0)},className:"range range-primary range-sm"})]}),i.jsxs("div",{className:"form-control w-full",children:[i.jsxs("label",{className:"label",children:[i.jsxs("span",{className:"label-text flex items-center gap-2",children:[e==="de"?"Sepia-Effekt":"Sepia effect",v&&i.jsx("span",{className:"loading loading-spinner loading-xs"})]}),i.jsxs("span",{className:"label-text-alt",children:[Math.round(se*100),"%"]})]}),i.jsx("input",{type:"range",min:"0",max:"1",step:"0.05",value:se,onChange:l=>fe(parseFloat(l.target.value)),className:"range range-secondary range-sm",disabled:v||!k})]})]}),O?i.jsxs("div",{className:"grid grid-cols-1 sm:grid-cols-2 gap-4 w-full",children:[i.jsxs("div",{className:"form-control w-full",children:[i.jsxs("label",{className:"label",children:[i.jsx("span",{className:"label-text",children:e==="de"?"Hintergrund":"Background"}),i.jsxs("span",{className:"label-text-alt",children:[Math.round(oe*100),"%"]})]}),i.jsx("input",{type:"range",min:"0",max:"1",step:"0.05",value:oe,onChange:l=>we(parseFloat(l.target.value)),className:"range range-primary range-sm"})]}),i.jsxs("div",{className:"form-control w-full",children:[i.jsxs("label",{className:"label",children:[i.jsx("span",{className:"label-text",children:e==="de"?"Unschärfe":"Blur"}),i.jsxs("span",{className:"label-text-alt",children:[Math.round(ie*100),"%"]})]}),i.jsx("input",{type:"range",min:"0",max:"1",step:"0.05",value:ie,onChange:l=>le(parseFloat(l.target.value)),className:"range range-primary range-sm"})]})]}):i.jsx("div",{className:"form-control",children:i.jsxs("label",{className:"label cursor-pointer justify-start gap-3",children:[i.jsx("input",{type:"checkbox",className:`toggle toggle-primary ${g?"opacity-50":""}`,checked:O,onChange:ae,disabled:g||!k}),i.jsxs("span",{className:"label-text flex items-center gap-2",children:[g?i.jsxs(i.Fragment,{children:[i.jsx("span",{className:"loading loading-spinner loading-xs"}),e==="de"?"Hintergrund wird entfernt...":"Removing background..."]}):e==="de"?"Hintergrund entfernen":"Remove background",!f&&i.jsx("span",{className:"badge badge-sm badge-outline",children:"API"})]})]})}),x&&i.jsxs("div",{className:"alert alert-warning text-sm py-2",children:[i.jsx("svg",{xmlns:"http://www.w3.org/2000/svg",className:"stroke-current shrink-0 h-5 w-5",fill:"none",viewBox:"0 0 24 24",children:i.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:"2",d:"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"})}),i.jsx("span",{children:x})]})]}):i.jsxs("div",{className:`border-2 border-dashed rounded-lg p-8 text-center cursor-pointer transition-colors ${g?"border-primary bg-primary/10 pointer-events-none":C?"border-primary bg-primary/10":"border-base-300 hover:border-primary hover:bg-base-200"}`,onDrop:ge,onDragOver:b,onDragLeave:L,onClick:$,children:[i.jsx("input",{ref:P,type:"file",accept:"image/*",className:"hidden",onChange:Z}),g?i.jsxs("div",{className:"flex flex-col items-center gap-2",children:[i.jsx("span",{className:"loading loading-spinner loading-lg text-primary"}),i.jsx("p",{className:"font-medium",children:e==="de"?"Hintergrund wird entfernt...":"Removing background..."})]}):i.jsxs("div",{className:"flex flex-col items-center gap-2",children:[i.jsx("svg",{xmlns:"http://www.w3.org/2000/svg",className:"h-12 w-12 text-base-content/50",fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",children:i.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z"})}),i.jsx("p",{className:"font-medium",children:S.form.portrait.upload}),i.jsx("p",{className:"text-sm text-base-content/60",children:S.form.portrait.dragDrop})]})]}),i.jsx(Fe,{isOpen:B,onClose:()=>_(!1),onSubmit:be})]})}function Dt(){return null}const A=typeof{url:typeof document>"u"?require("url").pathToFileURL(__filename).href:F&&F.tagName.toUpperCase()==="SCRIPT"&&F.src||new URL("index.cjs",document.baseURI).href}<"u"&&"/"||"/",Se={en:{1:{front:`${A}templates/front_ldpi_en.png`,back:`${A}templates/back_ldpi_en.png`,width:1536,height:1024},5:{front:`${A}templates/front_ldpi_en.png`,back:`${A}templates/back_ldpi_en.png`,width:1536,height:1024},10:{front:`${A}templates/front_ldpi_en.png`,back:`${A}templates/back_ldpi_en.png`,width:1536,height:1024}},de:{1:{front:`${A}templates/front_hdpi_de.webp`,back:`${A}templates/back_hdpi_de.webp`,width:6144,height:3200},5:{front:`${A}templates/front_hdpi_de.webp`,back:`${A}templates/back_hdpi_de.webp`,width:6144,height:3200},10:{front:`${A}templates/front_hdpi_de.webp`,back:`${A}templates/back_hdpi_de.webp`,width:6144,height:3200}}},K={front:{portrait:{x:768,y:490,radiusX:236,radiusY:258},namePlate:{x:768,y:848,fontSize:36,maxWidth:380,align:"center"}},back:{portrait:{x:0,y:0,radiusX:0,radiusY:0},namePlate:{x:768,y:832,fontSize:36,maxWidth:380,align:"center"},contactInfo:{x:380,y:500,fontSize:38,lineHeight:55,align:"center"},description:{x:1150,y:500,fontSize:38,maxWidth:480,lineHeight:42,align:"center"}}},q={front:{portrait:{x:3074,y:1530,radiusX:942,radiusY:1020},namePlate:{x:3072,y:2950,fontSize:144,maxWidth:1520,align:"center"}},back:{portrait:{x:0,y:0,radiusX:0,radiusY:0},namePlate:{x:3072,y:2930,fontSize:144,maxWidth:1520,align:"center"},contactInfo:{x:1520,y:1680,fontSize:160,lineHeight:280,align:"center"},description:{x:4600,y:1680,fontSize:145,maxWidth:2e3,lineHeight:210,align:"center"}}};function Ce(e){return e==="de"?q:K}function Oe(e,t){return Se[e][t]}const ke=.25;function At(e){const t=Ce(e),n=ke,r=a=>({portrait:{x:a.portrait.x*n,y:a.portrait.y*n,radiusX:a.portrait.radiusX*n,radiusY:a.portrait.radiusY*n},namePlate:{x:a.namePlate.x*n,y:a.namePlate.y*n,fontSize:a.namePlate.fontSize*n,maxWidth:a.namePlate.maxWidth?a.namePlate.maxWidth*n:void 0,lineHeight:a.namePlate.lineHeight?a.namePlate.lineHeight*n:void 0,align:a.namePlate.align},contactInfo:a.contactInfo?{x:a.contactInfo.x*n,y:a.contactInfo.y*n,fontSize:a.contactInfo.fontSize*n,maxWidth:a.contactInfo.maxWidth?a.contactInfo.maxWidth*n:void 0,lineHeight:a.contactInfo.lineHeight?a.contactInfo.lineHeight*n:void 0,align:a.contactInfo.align}:void 0,description:a.description?{x:a.description.x*n,y:a.description.y*n,fontSize:a.description.fontSize*n,maxWidth:a.description.maxWidth?a.description.maxWidth*n:void 0,lineHeight:a.description.lineHeight?a.description.lineHeight*n:void 0,align:a.description.align}:void 0});return{front:r(t.front),back:r(t.back)}}function _t(e,t){const n=Se[e][t];return{...n,width:Math.round(n.width*ke),height:Math.round(n.height*ke)}}const xe=new Map,J=new Map;async function ee(e){return xe.has(e)?xe.get(e):new Promise((t,n)=>{const r=new Image;r.crossOrigin="anonymous",r.onload=()=>{xe.set(e,r),t(r)},r.onerror=n,r.src=e})}function Re(e,t,n,r){e.drawImage(t,0,0,n,r)}async function We(e,t,n,r){if(t>=155&&t<=165)return ee(e);const a=`${e}:${t}:${n}x${r}`;if(J.has(a))return ee(J.get(a));const o=await ee(e),s=document.createElement("canvas");s.width=n,s.height=r;const d=s.getContext("2d");if(!d)throw new Error("Failed to get canvas context");d.drawImage(o,0,0,n,r);const c=s.toDataURL("image/png"),u=await He(c,t);if(J.size>20){const h=J.keys().next().value;h&&J.delete(h)}return J.set(a,u),ee(u)}function Ke(e,t,n,r,a,o,s=1,d=0,c=0){e.save(),e.beginPath(),e.ellipse(n,r,a,o,0,0,Math.PI*2),e.closePath(),e.clip();const u=t.width/t.height,h=a/o,p=a*2,v=o*2;let g,x;u>h?(x=v,g=v*u):(g=p,x=p/u),g*=s,x*=s;const f=Math.max(0,(g-p)/2),j=Math.max(0,(x-v)/2),S=n-g/2+d*f,P=r-x/2+c*j;e.drawImage(t,S,P,g,x),e.restore()}function Te(e,t,n,r="#2a3a2a"){e.save(),e.font=`${n.fontSize}px "Times New Roman", serif`,e.textAlign=n.align||"center",e.textBaseline="middle",e.fillStyle=r,n.maxWidth?e.fillText(t,n.x,n.y,n.maxWidth):e.fillText(t,n.x,n.y),e.restore()}function qe(e,t,n,r="#2a3a2a"){e.save(),e.font=`${n.fontSize}px "Times New Roman", serif`,e.textAlign=n.align||"center",e.textBaseline="top",e.fillStyle=r;const a=n.maxWidth||400,o=n.lineHeight||n.fontSize*1.4,s=t.split(" "),d=[];let c="";for(const p of s){const v=c?`${c} ${p}`:p;e.measureText(v).width>a&&c?(d.push(c),c=p):c=v}c&&d.push(c);const u=d.length*o;let h=n.y-u/2;for(const p of d)e.fillText(p,n.x,h),h+=o;e.restore()}function Ve(e,t,n,r,a,o="#2a3a2a"){e.save(),e.font=`${a.fontSize}px "Times New Roman", serif`,e.textAlign=a.align||"center",e.textBaseline="middle",e.fillStyle=o;const s=a.lineHeight||a.fontSize*1.8,d=[t,n,r].filter(Boolean),c=(d.length-1)*s;let u=a.y-c/2;for(const h of d)h&&(e.fillText(h,a.x,u),u+=s);e.restore()}async function Ze(e,t,n,r,a,o,s,d=1,c=0,u=0,h=0){const p=e.getContext("2d");if(!p)return;e.width=o,e.height=s,p.clearRect(0,0,o,s);const v=await We(t,h,o,s);if(Re(p,v,o,s),n)try{const g=await ee(n);Ke(p,g,a.portrait.x,a.portrait.y,a.portrait.radiusX,a.portrait.radiusY,d,c,u)}catch(g){console.error("Failed to load portrait:",g)}r&&Te(p,r,a.namePlate)}async function Ge(e,t,n,r,a,o,s,d,c,u=0){const h=e.getContext("2d");if(!h)return;e.width=d,e.height=c,h.clearRect(0,0,d,c);const p=await We(t,u,d,c);Re(h,p,d,c),s.contactInfo&&(n||r||a)&&Ve(h,n,r,a,s.contactInfo),s.description&&o&&qe(h,o,s.description),n&&Te(h,n,s.namePlate)}function Ut(e,t){const[n,r]=m.useState(e);return m.useEffect(()=>{const a=setTimeout(()=>r(e),t);return()=>clearTimeout(a)},[e,t]),n}function Ht(){const e=w(b=>b.voucherConfig.language),t=w(b=>b.voucherConfig.hours),n=w(b=>b.voucherConfig.description),r=w(b=>b.voucherConfig.templateHue),a=w(b=>b.personalInfo),o=w(b=>b.portrait),s=w(b=>b.currentSide),d=w(b=>b.flipSide),c=w(b=>b.setPortraitPan),u=Ut(r,150),h=V(e),p=m.useRef(null),v=m.useRef(null),g=m.useRef(null),[x,f]=m.useState(!1),[j,S]=m.useState(!1),P=m.useRef(null),I=_t(e,t),C=At(e),R=o.useEnhanced&&o.enhanced?o.enhanced:o.original,B=Ee(e,t,n);m.useEffect(()=>{p.current&&Ze(p.current,I.front,R,a.name,C.front,I.width,I.height,o.zoom,o.panX,o.panY,u)},[I,R,a.name,C,o.zoom,o.panX,o.panY,u]),m.useEffect(()=>{v.current&&Ge(v.current,I.back,a.name,a.email,a.phone,B,C.back,I.width,I.height,u)},[I,a,B,C,u]);const _=()=>{f(!0),setTimeout(()=>{d(),f(!1)},150)},M=m.useCallback((b,L)=>{if(!g.current||s!=="front"||!o.original)return!1;const $=g.current.getBoundingClientRect(),Z=$.width/I.width,X=$.height/I.height,G=(b-$.left)/Z,W=(L-$.top)/X,{x:ae,y:fe,radiusX:be,radiusY:we}=C.front.portrait,le=(G-ae)/be,ce=(W-fe)/we;return le*le+ce*ce<=1},[s,o.original,I.width,I.height,C.front.portrait]),H=m.useCallback((b,L)=>{o.zoom<=1||!M(b,L)||(S(!0),P.current={x:b,y:L,panX:o.panX,panY:o.panY})},[o.zoom,o.panX,o.panY,M]),N=m.useCallback((b,L)=>{if(!j||!P.current||!g.current)return;const $=g.current.getBoundingClientRect(),Z=3/Math.max($.width,$.height),X=(b-P.current.x)*Z,G=(L-P.current.y)*Z,W=Math.max(-1,Math.min(1,P.current.panX+X)),ae=Math.max(-1,Math.min(1,P.current.panY+G));c(W,ae)},[j,c]),U=m.useCallback(()=>{S(!1),P.current=null},[]),T=b=>{o.zoom>1&&M(b.clientX,b.clientY)&&(b.preventDefault(),H(b.clientX,b.clientY))},k=b=>{N(b.clientX,b.clientY)},Y=()=>U(),O=()=>U(),oe=b=>{if(b.touches.length===1){const L=b.touches[0];o.zoom>1&&M(L.clientX,L.clientY)&&H(L.clientX,L.clientY)}},ie=b=>{b.touches.length===1&&N(b.touches[0].clientX,b.touches[0].clientY)},se=()=>U(),ne=s==="front"&&o.original&&o.zoom>1,ge=I.width/I.height;return i.jsxs("div",{className:"space-y-4",children:[i.jsxs("div",{className:"flex justify-between items-center",children:[i.jsxs("div",{className:"tabs tabs-boxed bg-base-200",children:[i.jsx("button",{className:`tab ${s==="front"?"tab-active bg-primary text-primary-content font-semibold":""}`,onClick:()=>s!=="front"&&_(),children:h.preview.front}),i.jsx("button",{className:`tab ${s==="back"?"tab-active bg-primary text-primary-content font-semibold":""}`,onClick:()=>s!=="back"&&_(),children:h.preview.back})]}),i.jsxs("button",{className:"btn btn-ghost btn-sm",onClick:_,children:[i.jsx("svg",{xmlns:"http://www.w3.org/2000/svg",className:"h-5 w-5",fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",children:i.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"})}),h.preview.flip]})]}),i.jsxs("div",{ref:g,className:`relative w-full overflow-hidden shadow-lg ${ne?j?"cursor-grabbing":"cursor-grab":""}`,style:{aspectRatio:ge},onMouseDown:T,onMouseMove:k,onMouseUp:Y,onMouseLeave:O,onTouchStart:oe,onTouchMove:ie,onTouchEnd:se,children:[i.jsx("canvas",{ref:p,className:`absolute inset-0 w-full h-full transition-all duration-300 ${s==="front"?x?"opacity-0 scale-95":"opacity-100 scale-100":"opacity-0 scale-95 pointer-events-none"}`}),i.jsx("canvas",{ref:v,className:`absolute inset-0 w-full h-full transition-all duration-300 ${s==="back"?x?"opacity-0 scale-95":"opacity-100 scale-100":"opacity-0 scale-95 pointer-events-none"}`})]})]})}function zt(){const e=m.useRef(null),t=m.useRef(null);return{frontCanvasRef:e,backCanvasRef:t}}let Ie=null;function Yt(){return Ie||(Ie=new Worker(new URL("/assets/pdfWorker-DjqCcfZq.js",typeof document>"u"?require("url").pathToFileURL(__filename).href:F&&F.tagName.toUpperCase()==="SCRIPT"&&F.src||new URL("index.cjs",document.baseURI).href),{type:"module"})),Ie}async function $t(e){const n=await(await fetch(e)).blob();return URL.createObjectURL(n)}function Ae(e,t){const n=new Uint8Array(e);let r="";for(let a=0;a<n.length;a++)r+=String.fromCharCode(n[a]);return`data:${t};base64,${btoa(r)}`}async function Je(e){const{frontTemplateSrc:t,backTemplateSrc:n,templateWidth:r,templateHeight:a,layout:o,portrait:s,portraitZoom:d=1,portraitPanX:c=0,portraitPanY:u=0,templateHue:h=160,name:p,email:v,phone:g,description:x}=e;let f=null;return s&&(f=await $t(s)),new Promise((j,S)=>{const P=Yt(),I=R=>{if(P.removeEventListener("message",I),P.removeEventListener("error",C),f&&URL.revokeObjectURL(f),R.data.type==="success")try{const{frontImageData:B,backImageData:_,width:M,height:H}=R.data;if(!B||!_||!M||!H){S(new Error("Invalid response from worker"));return}const N=Ae(B,"image/jpeg"),U=Ae(_,"image/jpeg"),T=M/96*25.4,k=H/96*25.4,Y=new mt.jsPDF({orientation:T>k?"landscape":"portrait",unit:"mm",format:[T,k]});Y.addImage(N,"JPEG",0,0,T,k),Y.addPage([T,k]),Y.addImage(U,"JPEG",0,0,T,k);const O=Y.output("blob");j(O)}catch(B){S(B)}else S(new Error(R.data.error))},C=R=>{P.removeEventListener("message",I),P.removeEventListener("error",C),f&&URL.revokeObjectURL(f),S(new Error(R.message))};P.addEventListener("message",I),P.addEventListener("error",C),P.postMessage({type:"generate",frontTemplateUrl:t,backTemplateUrl:n,portraitUrl:f,templateWidth:r,templateHeight:a,templateHue:h,portraitZoom:d,portraitPanX:c,portraitPanY:u,layout:{front:{portrait:o.front.portrait,namePlate:o.front.namePlate},back:{namePlate:o.back.namePlate,contactInfo:o.back.contactInfo,description:o.back.description}},name:p,email:v,phone:g,description:x})})}function Qe(e,t){const n=URL.createObjectURL(e),r=document.createElement("a");r.href=n,r.download=t,document.body.appendChild(r),r.click(),document.body.removeChild(r),URL.revokeObjectURL(n)}async function et(e){const t=await Je(e);Qe(t,e.filename)}function Xt(){const e=w(f=>f.voucherConfig.language),t=w(f=>f.voucherConfig.hours),n=w(f=>f.voucherConfig.description),r=w(f=>f.voucherConfig.templateHue),a=w(f=>f.personalInfo),o=w(f=>f.portrait),s=w(f=>f.isExporting),d=w(f=>f.setIsExporting),c=V(e),u=Oe(e,t),h=Ce(e),p=o.useEnhanced&&o.enhanced?o.enhanced:o.original,v=Ee(e,t,n),g=a.name.trim().length>0&&a.email.trim().length>0&&a.phone.trim().length>0&&o.original!==null,x=async()=>{if(!(!g||s)){d(!0);try{const f=`zeitgutschein-${t}h-${a.name.replace(/\s+/g,"-").toLowerCase()}.pdf`;await et({frontTemplateSrc:u.front,backTemplateSrc:u.back,templateWidth:u.width,templateHeight:u.height,layout:h,portrait:p,portraitZoom:o.zoom,portraitPanX:o.panX,portraitPanY:o.panY,templateHue:r,name:a.name,email:a.email,phone:a.phone,description:v,filename:f})}catch(f){console.error("PDF export failed:",f)}finally{d(!1)}}};return i.jsxs("button",{className:`btn btn-primary flex-1 ${g?"":"btn-disabled"}`,onClick:x,disabled:!g,"aria-busy":s,children:[s?i.jsx("span",{className:"loading loading-spinner loading-sm"}):i.jsx("svg",{xmlns:"http://www.w3.org/2000/svg",className:"h-5 w-5",fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",children:i.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"})}),c.export.button]})}function tt(){const e=w(r=>r.voucherConfig.language),t=w(r=>r.setLanguage),n=r=>{t(r)};return i.jsxs("div",{className:"join",children:[i.jsx("button",{className:`join-item btn btn-sm ${e==="de"?"btn-active btn-primary":"btn-ghost"}`,onClick:()=>n("de"),children:"DE"}),i.jsx("button",{className:`join-item btn btn-sm ${e==="en"?"btn-active btn-primary":"btn-ghost"}`,onClick:()=>n("en"),children:"EN"})]})}function Ft(){const e=w(n=>n.voucherConfig.language),t=V(e);return i.jsxs("div",{className:"navbar bg-currency-green text-currency-cream shadow-lg",children:[i.jsx("div",{className:"navbar-start",children:i.jsx("a",{className:"btn btn-ghost text-xl font-currency font-bold",children:t.header.title})}),i.jsx("div",{className:"navbar-center hidden sm:flex",children:i.jsx("span",{className:"text-sm opacity-80",children:t.header.subtitle})}),i.jsx("div",{className:"navbar-end",children:i.jsx(tt,{})})]})}const he="/",Ot={id:"time-voucher-classic-de",name:"Zeitgutschein Classic",type:"time-voucher",category:"classic",images:{front:`${he}templates/front_hdpi_de.jpg`,back:`${he}templates/back_hdpi_de.jpg`,width:6144,height:4096},fields:[{id:"name",type:"text",label:{de:"Name",en:"Name"},required:!0,validation:{minLength:1,maxLength:50}},{id:"hours",type:"select",label:{de:"Stunden",en:"Hours"},required:!0,options:["1","5","10"]},{id:"portrait",type:"image",label:{de:"Portrait",en:"Portrait"},required:!1},{id:"email",type:"text",label:{de:"E-Mail",en:"Email"},required:!1},{id:"phone",type:"text",label:{de:"Telefon",en:"Phone"},required:!1},{id:"description",type:"textarea",label:{de:"Beschreibung",en:"Description"},required:!1,validation:{maxLength:200}}],layout:{front:{portrait:q.front.portrait,name:q.front.namePlate},back:{name:q.back.namePlate,contactInfo:q.back.contactInfo,description:q.back.description}},languages:["de"]},Wt={id:"time-voucher-classic-en",name:"Time Voucher Classic",type:"time-voucher",category:"classic",images:{front:`${he}templates/front_ldpi_en.png`,back:`${he}templates/back_ldpi_en.png`,width:1536,height:1024},fields:[{id:"name",type:"text",label:{de:"Name",en:"Name"},required:!0,validation:{minLength:1,maxLength:50}},{id:"hours",type:"select",label:{de:"Stunden",en:"Hours"},required:!0,options:["1","5","10"]},{id:"portrait",type:"image",label:{de:"Portrait",en:"Portrait"},required:!1},{id:"email",type:"text",label:{de:"E-Mail",en:"Email"},required:!1},{id:"phone",type:"text",label:{de:"Telefon",en:"Phone"},required:!1},{id:"description",type:"textarea",label:{de:"Beschreibung",en:"Description"},required:!1,validation:{maxLength:200}}],layout:{front:{portrait:K.front.portrait,name:K.front.namePlate},back:{name:K.back.namePlate,contactInfo:K.back.contactInfo,description:K.back.description}},languages:["en"]},_e=[Ot,Wt],nt={async listTemplates(e){let t=[..._e];return e!=null&&e.type&&(t=t.filter(n=>n.type===e.type)),e!=null&&e.category&&(t=t.filter(n=>n.category===e.category)),e!=null&&e.language&&(t=t.filter(n=>n.languages.includes(e.language))),t},async getTemplate(e){const t=_e.find(n=>n.id===e);if(!t)throw new Error(`Template not found: ${e}`);return t}};function Kt(e){return e==="de"?"time-voucher-classic-de":"time-voucher-classic-en"}let me=nt;function qt(e){me=e}function Vt(){return me}async function Zt(e){return me.listTemplates(e)}async function Gt(e){return me.getTemplate(e)}exports.ApiKeyModal=Fe;exports.BillPreview=Ht;exports.ExportButton=Xt;exports.Header=Ft;exports.LAYOUT_HDPI=q;exports.LAYOUT_LDPI=K;exports.LanguageToggle=tt;exports.PersonalInfoForm=wt;exports.PortraitUpload=Mt;exports.TEMPLATES=Se;exports.VoucherConfig=Dt;exports.applyEngravingEffect=Ue;exports.applyHueShift=He;exports.downloadBlob=Qe;exports.drawContactInfo=Ve;exports.drawMultilineText=qe;exports.drawOvalPortrait=Ke;exports.drawTemplate=Re;exports.drawText=Te;exports.enhancePortrait=Bt;exports.exportBillAsPDF=et;exports.formatDescription=Ee;exports.generateBillPDF=Je;exports.getApiKey=pe;exports.getDefaultTemplateId=Kt;exports.getLayout=Ce;exports.getRemoveBackgroundEndpoint=St;exports.getTemplate=Oe;exports.getTemplateById=Gt;exports.getTemplateProvider=Vt;exports.hasApiKey=Pe;exports.hasCustomEndpoint=Ct;exports.listTemplates=Zt;exports.loadImage=ee;exports.removeBackground=Xe;exports.renderBackSide=Ge;exports.renderFrontSide=Ze;exports.setApiKey=$e;exports.setRemoveBackgroundEndpoint=jt;exports.setTemplateProvider=qt;exports.staticTemplateProvider=nt;exports.t=V;exports.useBillCanvasRefs=zt;exports.useBillStore=w;
|
package/dist/index.d.ts
CHANGED
|
@@ -21,6 +21,15 @@ declare interface ApiKeyModalProps {
|
|
|
21
21
|
*/
|
|
22
22
|
export declare function applyEngravingEffect(imageDataUrl: string, intensity?: number): Promise<string>;
|
|
23
23
|
|
|
24
|
+
/**
|
|
25
|
+
* Replace cool colors (greens, cyans, blues) with target hue
|
|
26
|
+
* Maps all cool colors to the target hue while preserving saturation and lightness
|
|
27
|
+
* @param imageDataUrl - Base64 data URL of the image
|
|
28
|
+
* @param targetHue - Target hue in degrees (0-360). 160 = original (no shift)
|
|
29
|
+
* @returns Image with cool colors replaced by target hue
|
|
30
|
+
*/
|
|
31
|
+
export declare function applyHueShift(imageDataUrl: string, targetHue: number): Promise<string>;
|
|
32
|
+
|
|
24
33
|
declare interface BillActions {
|
|
25
34
|
setPersonalInfo: (info: Partial<PersonalInfo>) => void;
|
|
26
35
|
setVoucherConfig: (config: Partial<VoucherConfigType>) => void;
|
|
@@ -40,6 +49,7 @@ declare interface BillActions {
|
|
|
40
49
|
setIsExporting: (value: boolean) => void;
|
|
41
50
|
setLanguage: (language: Language) => void;
|
|
42
51
|
setHours: (hours: HourValue) => void;
|
|
52
|
+
setTemplateHue: (hue: number) => void;
|
|
43
53
|
reset: () => void;
|
|
44
54
|
}
|
|
45
55
|
|
|
@@ -183,6 +193,7 @@ declare interface PDFGeneratorOptions {
|
|
|
183
193
|
portraitZoom?: number;
|
|
184
194
|
portraitPanX?: number;
|
|
185
195
|
portraitPanY?: number;
|
|
196
|
+
templateHue?: number;
|
|
186
197
|
name: string;
|
|
187
198
|
email: string;
|
|
188
199
|
phone: string;
|
|
@@ -222,9 +233,9 @@ export declare function PortraitUpload(): JSX.Element;
|
|
|
222
233
|
|
|
223
234
|
export declare function removeBackground(imageDataUrl: string): Promise<string>;
|
|
224
235
|
|
|
225
|
-
export declare function renderBackSide(canvas: HTMLCanvasElement, templateSrc: string, name: string, email: string, phone: string, description: string, layout: TemplateLayout, width: number, height: number): Promise<void>;
|
|
236
|
+
export declare function renderBackSide(canvas: HTMLCanvasElement, templateSrc: string, name: string, email: string, phone: string, description: string, layout: TemplateLayout, width: number, height: number, templateHue?: number): Promise<void>;
|
|
226
237
|
|
|
227
|
-
export declare function renderFrontSide(canvas: HTMLCanvasElement, templateSrc: string, portraitSrc: string | null, name: string, layout: TemplateLayout, width: number, height: number, portraitZoom?: number, portraitPanX?: number, portraitPanY?: number): Promise<void>;
|
|
238
|
+
export declare function renderFrontSide(canvas: HTMLCanvasElement, templateSrc: string, portraitSrc: string | null, name: string, layout: TemplateLayout, width: number, height: number, portraitZoom?: number, portraitPanX?: number, portraitPanY?: number, templateHue?: number): Promise<void>;
|
|
228
239
|
|
|
229
240
|
export declare function setApiKey(key: string): void;
|
|
230
241
|
|
|
@@ -375,6 +386,10 @@ declare interface Translations {
|
|
|
375
386
|
description: string;
|
|
376
387
|
descriptionPlaceholder: string;
|
|
377
388
|
};
|
|
389
|
+
billColor: {
|
|
390
|
+
title: string;
|
|
391
|
+
label: string;
|
|
392
|
+
};
|
|
378
393
|
};
|
|
379
394
|
preview: {
|
|
380
395
|
front: string;
|
|
@@ -450,6 +465,7 @@ export declare interface VoucherConfigType {
|
|
|
450
465
|
hours: HourValue;
|
|
451
466
|
description: string;
|
|
452
467
|
language: Language;
|
|
468
|
+
templateHue: number;
|
|
453
469
|
}
|
|
454
470
|
|
|
455
471
|
export { }
|