sqibhe commited on
Commit
5d30efa
·
verified ·
1 Parent(s): 17da364

Add 3 files

Browse files
Files changed (3) hide show
  1. README.md +6 -4
  2. index.html +1673 -19
  3. prompts.txt +2 -0
README.md CHANGED
@@ -1,10 +1,12 @@
1
  ---
2
- title: Snapeidit
3
- emoji: 🐠
4
- colorFrom: yellow
5
  colorTo: green
6
  sdk: static
7
  pinned: false
 
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
  ---
2
+ title: snapeidit
3
+ emoji: 🐳
4
+ colorFrom: gray
5
  colorTo: green
6
  sdk: static
7
  pinned: false
8
+ tags:
9
+ - deepsite
10
  ---
11
 
12
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
index.html CHANGED
@@ -1,19 +1,1673 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>SnapEdit Pro - AI-Powered Screenshot Editor</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
9
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/5.3.1/fabric.min.js"></script>
10
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js"></script>
11
+ <style>
12
+ .tooltip {
13
+ position: relative;
14
+ display: inline-block;
15
+ }
16
+ .tooltip .tooltiptext {
17
+ visibility: hidden;
18
+ width: 120px;
19
+ background-color: #555;
20
+ color: #fff;
21
+ text-align: center;
22
+ border-radius: 6px;
23
+ padding: 5px;
24
+ position: absolute;
25
+ z-index: 1;
26
+ bottom: 125%;
27
+ left: 50%;
28
+ margin-left: -60px;
29
+ opacity: 0;
30
+ transition: opacity 0.3s;
31
+ }
32
+ .tooltip:hover .tooltiptext {
33
+ visibility: visible;
34
+ opacity: 1;
35
+ }
36
+ .canvas-container {
37
+ box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
38
+ }
39
+ .sidebar {
40
+ transition: all 0.3s ease;
41
+ }
42
+ .ai-processing {
43
+ animation: pulse 2s infinite;
44
+ }
45
+ @keyframes pulse {
46
+ 0% { opacity: 0.6; }
47
+ 50% { opacity: 1; }
48
+ 100% { opacity: 0.6; }
49
+ }
50
+ .blur-effect {
51
+ filter: url(#blur-filter);
52
+ }
53
+ </style>
54
+ </head>
55
+ <body class="bg-gray-50 font-sans">
56
+ <div class="flex flex-col h-screen">
57
+ <!-- Top Navigation -->
58
+ <header class="bg-indigo-600 text-white shadow-md">
59
+ <div class="container mx-auto px-4 py-3 flex justify-between items-center">
60
+ <div class="flex items-center space-x-2">
61
+ <i class="fas fa-camera-retro text-2xl"></i>
62
+ <h1 class="text-xl font-bold">SnapEdit Pro</h1>
63
+ </div>
64
+ <div class="flex items-center space-x-4">
65
+ <button id="apiKeyBtn" class="px-4 py-2 bg-indigo-700 rounded-md font-medium hover:bg-indigo-800 transition">
66
+ <i class="fas fa-key mr-2"></i>API Key
67
+ </button>
68
+ <button id="saveBtn" class="px-4 py-2 bg-white text-indigo-600 rounded-md font-medium hover:bg-gray-100 transition">
69
+ <i class="fas fa-save mr-2"></i>Save
70
+ </button>
71
+ <button id="exportBtn" class="px-4 py-2 bg-indigo-700 rounded-md font-medium hover:bg-indigo-800 transition">
72
+ <i class="fas fa-file-export mr-2"></i>Export
73
+ </button>
74
+ <div class="relative">
75
+ <img src="https://via.placeholder.com/40" alt="User" class="rounded-full w-10 h-10 cursor-pointer" id="userMenuBtn">
76
+ <div id="userMenu" class="hidden absolute right-0 mt-2 w-48 bg-white rounded-md shadow-lg py-1 z-50">
77
+ <a href="#" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">Profile</a>
78
+ <a href="#" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">Settings</a>
79
+ <a href="#" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">Sign out</a>
80
+ </div>
81
+ </div>
82
+ </div>
83
+ </div>
84
+ </header>
85
+
86
+ <div class="flex flex-1 overflow-hidden">
87
+ <!-- Left Sidebar - Tools -->
88
+ <div class="sidebar w-16 bg-white shadow-md flex flex-col items-center py-4 space-y-6">
89
+ <div class="tooltip">
90
+ <button id="uploadBtn" class="p-2 rounded-full hover:bg-gray-100 text-indigo-600">
91
+ <i class="fas fa-upload text-xl"></i>
92
+ </button>
93
+ <span class="tooltiptext">Upload Image</span>
94
+ </div>
95
+
96
+ <div class="border-t border-gray-200 w-8"></div>
97
+
98
+ <div class="tooltip">
99
+ <button id="selectTool" class="p-2 rounded-full hover:bg-gray-100 text-gray-700">
100
+ <i class="fas fa-mouse-pointer text-xl"></i>
101
+ </button>
102
+ <span class="tooltiptext">Select Tool</span>
103
+ </div>
104
+
105
+ <div class="tooltip">
106
+ <button id="textTool" class="p-2 rounded-full hover:bg-gray-100 text-gray-700">
107
+ <i class="fas fa-font text-xl"></i>
108
+ </button>
109
+ <span class="tooltiptext">Text Tool</span>
110
+ </div>
111
+
112
+ <div class="tooltip">
113
+ <button id="drawTool" class="p-2 rounded-full hover:bg-gray-100 text-gray-700">
114
+ <i class="fas fa-pencil-alt text-xl"></i>
115
+ </button>
116
+ <span class="tooltiptext">Draw Tool</span>
117
+ </div>
118
+
119
+ <div class="tooltip">
120
+ <button id="shapeTool" class="p-2 rounded-full hover:bg-gray-100 text-gray-700">
121
+ <i class="fas fa-square text-xl"></i>
122
+ </button>
123
+ <span class="tooltiptext">Shapes</span>
124
+ </div>
125
+
126
+ <div class="tooltip">
127
+ <button id="blurTool" class="p-2 rounded-full hover:bg-gray-100 text-gray-700">
128
+ <i class="fas fa-eye-slash text-xl"></i>
129
+ </button>
130
+ <span class="tooltiptext">Blur Tool</span>
131
+ </div>
132
+
133
+ <div class="border-t border-gray-200 w-8"></div>
134
+
135
+ <div class="tooltip">
136
+ <button id="aiRemoveBg" class="p-2 rounded-full hover:bg-gray-100 text-gray-700">
137
+ <i class="fas fa-robot text-xl"></i>
138
+ </button>
139
+ <span class="tooltiptext">AI Background Remove</span>
140
+ </div>
141
+
142
+ <div class="tooltip">
143
+ <button id="aiEnhance" class="p-2 rounded-full hover:bg-gray-100 text-gray-700">
144
+ <i class="fas fa-magic text-xl"></i>
145
+ </button>
146
+ <span class="tooltiptext">AI Enhance</span>
147
+ </div>
148
+
149
+ <div class="tooltip">
150
+ <button id="aiTextRecognition" class="p-2 rounded-full hover:bg-gray-100 text-gray-700">
151
+ <i class="fas fa-text-height text-xl"></i>
152
+ </button>
153
+ <span class="tooltiptext">OCR Text Recognition</span>
154
+ </div>
155
+ </div>
156
+
157
+ <!-- Main Canvas Area -->
158
+ <div class="flex-1 flex flex-col overflow-auto bg-gray-100 p-4">
159
+ <div class="flex justify-between items-center mb-4">
160
+ <div class="flex space-x-2">
161
+ <div class="relative">
162
+ <button id="zoomIn" class="px-3 py-1 bg-white border border-gray-300 rounded-l-md hover:bg-gray-50">
163
+ <i class="fas fa-search-plus"></i>
164
+ </button>
165
+ <button id="zoomOut" class="px-3 py-1 bg-white border border-gray-300 rounded-r-md hover:bg-gray-50">
166
+ <i class="fas fa-search-minus"></i>
167
+ </button>
168
+ </div>
169
+ <div class="relative">
170
+ <button id="undoBtn" class="px-3 py-1 bg-white border border-gray-300 rounded-l-md hover:bg-gray-50">
171
+ <i class="fas fa-undo"></i>
172
+ </button>
173
+ <button id="redoBtn" class="px-3 py-1 bg-white border border-gray-300 rounded-r-md hover:bg-gray-50">
174
+ <i class="fas fa-redo"></i>
175
+ </button>
176
+ </div>
177
+ </div>
178
+ <div class="flex items-center space-x-4">
179
+ <div id="aiProcessing" class="hidden items-center space-x-2 text-indigo-600 ai-processing">
180
+ <i class="fas fa-cog fa-spin"></i>
181
+ <span>AI Processing...</span>
182
+ </div>
183
+ <div class="text-sm text-gray-500">
184
+ <span id="imageInfo">No image loaded</span>
185
+ </div>
186
+ </div>
187
+ </div>
188
+
189
+ <div class="flex-1 flex justify-center items-center overflow-auto">
190
+ <div id="canvas-container" class="canvas-container bg-white shadow-lg">
191
+ <canvas id="editorCanvas" width="800" height="600"></canvas>
192
+ </div>
193
+ </div>
194
+ </div>
195
+
196
+ <!-- Right Sidebar - Properties -->
197
+ <div class="sidebar w-64 bg-white shadow-md p-4 overflow-y-auto">
198
+ <h2 class="font-bold text-lg mb-4 text-gray-700">Properties</h2>
199
+
200
+ <div id="generalProps" class="mb-6">
201
+ <h3 class="font-medium text-gray-600 mb-2">Canvas</h3>
202
+ <div class="space-y-3">
203
+ <div>
204
+ <label class="block text-sm text-gray-500 mb-1">Background</label>
205
+ <input type="color" id="canvasBg" value="#ffffff" class="w-full h-8">
206
+ </div>
207
+ <div>
208
+ <label class="block text-sm text-gray-500 mb-1">Opacity</label>
209
+ <input type="range" id="canvasOpacity" min="0" max="100" value="100" class="w-full">
210
+ </div>
211
+ </div>
212
+ </div>
213
+
214
+ <div id="textProps" class="mb-6 hidden">
215
+ <h3 class="font-medium text-gray-600 mb-2">Text</h3>
216
+ <div class="space-y-3">
217
+ <div>
218
+ <label class="block text-sm text-gray-500 mb-1">Content</label>
219
+ <textarea id="textContent" class="w-full border border-gray-300 rounded p-2 text-sm" rows="2"></textarea>
220
+ </div>
221
+ <div>
222
+ <label class="block text-sm text-gray-500 mb-1">Font</label>
223
+ <select id="textFont" class="w-full border border-gray-300 rounded p-2 text-sm">
224
+ <option value="Arial">Arial</option>
225
+ <option value="Verdana">Verdana</option>
226
+ <option value="Helvetica">Helvetica</option>
227
+ <option value="Times New Roman">Times New Roman</option>
228
+ <option value="Courier New">Courier New</option>
229
+ </select>
230
+ </div>
231
+ <div>
232
+ <label class="block text-sm text-gray-500 mb-1">Size</label>
233
+ <input type="range" id="textSize" min="8" max="72" value="16" class="w-full">
234
+ </div>
235
+ <div>
236
+ <label class="block text-sm text-gray-500 mb-1">Color</label>
237
+ <input type="color" id="textColor" value="#000000" class="w-full h-8">
238
+ </div>
239
+ <div>
240
+ <label class="block text-sm text-gray-500 mb-1">Background</label>
241
+ <input type="color" id="textBgColor" value="transparent" class="w-full h-8">
242
+ </div>
243
+ </div>
244
+ </div>
245
+
246
+ <div id="drawProps" class="mb-6 hidden">
247
+ <h3 class="font-medium text-gray-600 mb-2">Drawing</h3>
248
+ <div class="space-y-3">
249
+ <div>
250
+ <label class="block text-sm text-gray-500 mb-1">Brush Size</label>
251
+ <input type="range" id="brushSize" min="1" max="50" value="5" class="w-full">
252
+ </div>
253
+ <div>
254
+ <label class="block text-sm text-gray-500 mb-1">Color</label>
255
+ <input type="color" id="brushColor" value="#000000" class="w-full h-8">
256
+ </div>
257
+ <div>
258
+ <label class="block text-sm text-gray-500 mb-1">Opacity</label>
259
+ <input type="range" id="brushOpacity" min="0" max="100" value="100" class="w-full">
260
+ </div>
261
+ <div>
262
+ <label class="block text-sm text-gray-500 mb-1">Brush Type</label>
263
+ <select id="brushType" class="w-full border border-gray-300 rounded p-2 text-sm">
264
+ <option value="pencil">Pencil</option>
265
+ <option value="circle">Circle</option>
266
+ <option value="spray">Spray</option>
267
+ <option value="pattern">Pattern</option>
268
+ </select>
269
+ </div>
270
+ </div>
271
+ </div>
272
+
273
+ <div id="shapeProps" class="mb-6 hidden">
274
+ <h3 class="font-medium text-gray-600 mb-2">Shape</h3>
275
+ <div class="space-y-3">
276
+ <div>
277
+ <label class="block text-sm text-gray-500 mb-1">Type</label>
278
+ <select id="shapeType" class="w-full border border-gray-300 rounded p-2 text-sm">
279
+ <option value="rect">Rectangle</option>
280
+ <option value="circle">Circle</option>
281
+ <option value="triangle">Triangle</option>
282
+ <option value="line">Line</option>
283
+ <option value="polygon">Polygon</option>
284
+ </select>
285
+ </div>
286
+ <div>
287
+ <label class="block text-sm text-gray-500 mb-1">Fill Color</label>
288
+ <input type="color" id="shapeFill" value="#000000" class="w-full h-8">
289
+ </div>
290
+ <div>
291
+ <label class="block text-sm text-gray-500 mb-1">Stroke Color</label>
292
+ <input type="color" id="shapeStroke" value="#000000" class="w-full h-8">
293
+ </div>
294
+ <div>
295
+ <label class="block text-sm text-gray-500 mb-1">Stroke Width</label>
296
+ <input type="range" id="shapeStrokeWidth" min="1" max="20" value="1" class="w-full">
297
+ </div>
298
+ <div id="polygonSidesContainer" class="hidden">
299
+ <label class="block text-sm text-gray-500 mb-1">Sides</label>
300
+ <input type="number" id="polygonSides" min="3" max="12" value="5" class="w-full border border-gray-300 rounded p-2 text-sm">
301
+ </div>
302
+ </div>
303
+ </div>
304
+
305
+ <div id="blurProps" class="mb-6 hidden">
306
+ <h3 class="font-medium text-gray-600 mb-2">Blur</h3>
307
+ <div class="space-y-3">
308
+ <div>
309
+ <label class="block text-sm text-gray-500 mb-1">Intensity</label>
310
+ <input type="range" id="blurIntensity" min="0" max="20" value="5" class="w-full">
311
+ </div>
312
+ <div>
313
+ <label class="block text-sm text-gray-500 mb-1">Radius</label>
314
+ <input type="range" id="blurRadius" min="10" max="200" value="50" class="w-full">
315
+ </div>
316
+ <div>
317
+ <label class="block text-sm text-gray-500 mb-1">Type</label>
318
+ <select id="blurType" class="w-full border border-gray-300 rounded p-2 text-sm">
319
+ <option value="gaussian">Gaussian</option>
320
+ <option value="pixelate">Pixelate</option>
321
+ <option value="mask">Mask</option>
322
+ </select>
323
+ </div>
324
+ </div>
325
+ </div>
326
+
327
+ <div id="aiProps" class="mb-6 hidden">
328
+ <h3 class="font-medium text-gray-600 mb-2">AI Tools</h3>
329
+ <div class="space-y-3">
330
+ <div id="apiKeyStatus" class="text-xs p-2 rounded bg-gray-100 text-gray-600">
331
+ <i class="fas fa-circle text-red-500 mr-1"></i> No API Key
332
+ </div>
333
+ <div>
334
+ <label class="block text-sm text-gray-500 mb-1">Enhance Mode</label>
335
+ <select id="aiEnhanceMode" class="w-full border border-gray-300 rounded p-2 text-sm">
336
+ <option value="auto">Auto Enhance</option>
337
+ <option value="sharpness">Sharpness</option>
338
+ <option value="contrast">Contrast</option>
339
+ <option value="color">Color Boost</option>
340
+ <option value="denoise">Denoise</option>
341
+ </select>
342
+ </div>
343
+ <div>
344
+ <label class="block text-sm text-gray-500 mb-1">OCR Language</label>
345
+ <select id="ocrLanguage" class="w-full border border-gray-300 rounded p-2 text-sm">
346
+ <option value="eng">English</option>
347
+ <option value="spa">Spanish</option>
348
+ <option value="fra">French</option>
349
+ <option value="deu">German</option>
350
+ <option value="chi_sim">Chinese</option>
351
+ </select>
352
+ </div>
353
+ <button id="runAi" class="w-full bg-indigo-600 text-white py-2 rounded hover:bg-indigo-700 transition">
354
+ Apply AI Processing
355
+ </button>
356
+ </div>
357
+ </div>
358
+ </div>
359
+ </div>
360
+ </div>
361
+
362
+ <!-- Modals -->
363
+ <div id="uploadModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden">
364
+ <div class="bg-white rounded-lg p-6 w-full max-w-md">
365
+ <div class="flex justify-between items-center mb-4">
366
+ <h3 class="text-lg font-bold">Upload Image</h3>
367
+ <button id="closeUploadModal" class="text-gray-500 hover:text-gray-700">
368
+ <i class="fas fa-times"></i>
369
+ </button>
370
+ </div>
371
+ <div class="space-y-4">
372
+ <div class="border-2 border-dashed border-gray-300 rounded-lg p-8 text-center">
373
+ <i class="fas fa-cloud-upload-alt text-4xl text-indigo-500 mb-2"></i>
374
+ <p class="text-gray-600 mb-4">Drag & drop your image here or click to browse</p>
375
+ <input type="file" id="fileInput" class="hidden" accept="image/*">
376
+ <button id="browseBtn" class="px-4 py-2 bg-indigo-600 text-white rounded hover:bg-indigo-700 transition">
377
+ Browse Files
378
+ </button>
379
+ </div>
380
+ <div class="text-center text-sm text-gray-500">
381
+ <p>Supports: JPG, PNG, GIF (Max 10MB)</p>
382
+ </div>
383
+ </div>
384
+ </div>
385
+ </div>
386
+
387
+ <div id="exportModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden">
388
+ <div class="bg-white rounded-lg p-6 w-full max-w-md">
389
+ <div class="flex justify-between items-center mb-4">
390
+ <h3 class="text-lg font-bold">Export Options</h3>
391
+ <button id="closeExportModal" class="text-gray-500 hover:text-gray-700">
392
+ <i class="fas fa-times"></i>
393
+ </button>
394
+ </div>
395
+ <div class="space-y-4">
396
+ <div>
397
+ <label class="block text-sm font-medium text-gray-700 mb-1">Format</label>
398
+ <select id="exportFormat" class="w-full border border-gray-300 rounded p-2">
399
+ <option value="png">PNG</option>
400
+ <option value="jpg">JPG</option>
401
+ <option value="webp">WebP</option>
402
+ <option value="pdf">PDF</option>
403
+ </select>
404
+ </div>
405
+ <div id="qualityContainer" class="hidden">
406
+ <label class="block text-sm font-medium text-gray-700 mb-1">Quality (%)</label>
407
+ <input type="range" id="exportQuality" min="1" max="100" value="90" class="w-full">
408
+ </div>
409
+ <div>
410
+ <label class="block text-sm font-medium text-gray-700 mb-1">Size</label>
411
+ <select id="exportSize" class="w-full border border-gray-300 rounded p-2">
412
+ <option value="original">Original Size</option>
413
+ <option value="1024">1024px (width)</option>
414
+ <option value="800">800px (width)</option>
415
+ <option value="640">640px (width)</option>
416
+ </select>
417
+ </div>
418
+ <div class="flex space-x-2 pt-4">
419
+ <button id="downloadBtn" class="flex-1 bg-indigo-600 text-white py-2 rounded hover:bg-indigo-700 transition">
420
+ <i class="fas fa-download mr-2"></i>Download
421
+ </button>
422
+ <button id="copyToClipboardBtn" class="flex-1 bg-gray-200 text-gray-700 py-2 rounded hover:bg-gray-300 transition">
423
+ <i class="fas fa-copy mr-2"></i>Copy
424
+ </button>
425
+ </div>
426
+ </div>
427
+ </div>
428
+ </div>
429
+
430
+ <div id="apiKeyModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden">
431
+ <div class="bg-white rounded-lg p-6 w-full max-w-md">
432
+ <div class="flex justify-between items-center mb-4">
433
+ <h3 class="text-lg font-bold">DeepSeek API Key</h3>
434
+ <button id="closeApiKeyModal" class="text-gray-500 hover:text-gray-700">
435
+ <i class="fas fa-times"></i>
436
+ </button>
437
+ </div>
438
+ <div class="space-y-4">
439
+ <div>
440
+ <label class="block text-sm font-medium text-gray-700 mb-1">Enter your DeepSeek API Key</label>
441
+ <input type="password" id="apiKeyInput" class="w-full border border-gray-300 rounded p-2" placeholder="sk-xxxxxxxxxxxxxxxxxxxxxxxx">
442
+ </div>
443
+ <div class="text-xs text-gray-500">
444
+ <p>Your API key is encrypted and stored locally in your browser. We never send it to our servers.</p>
445
+ </div>
446
+ <div class="flex space-x-2 pt-2">
447
+ <button id="saveApiKeyBtn" class="flex-1 bg-indigo-600 text-white py-2 rounded hover:bg-indigo-700 transition">
448
+ <i class="fas fa-save mr-2"></i>Save Key
449
+ </button>
450
+ <button id="clearApiKeyBtn" class="flex-1 bg-gray-200 text-gray-700 py-2 rounded hover:bg-gray-300 transition">
451
+ <i class="fas fa-trash mr-2"></i>Clear Key
452
+ </button>
453
+ </div>
454
+ </div>
455
+ </div>
456
+ </div>
457
+
458
+ <!-- SVG Filter for Blur Effect -->
459
+ <svg xmlns="http://www.w3.org/2000/svg" version="1.1" class="hidden">
460
+ <defs>
461
+ <filter id="blur-filter">
462
+ <feGaussianBlur stdDeviation="5" />
463
+ </filter>
464
+ </defs>
465
+ </svg>
466
+
467
+ <script>
468
+ document.addEventListener('DOMContentLoaded', function() {
469
+ // Initialize Fabric.js canvas with improved settings
470
+ const canvas = new fabric.Canvas('editorCanvas', {
471
+ backgroundColor: '#ffffff',
472
+ preserveObjectStacking: true,
473
+ selection: true,
474
+ selectionColor: 'rgba(99, 102, 241, 0.3)',
475
+ selectionBorderColor: '#6366f1',
476
+ selectionLineWidth: 1,
477
+ selectionDashArray: [5, 5],
478
+ allowTouchScrolling: true,
479
+ enableRetinaScaling: true
480
+ });
481
+
482
+ // State management with improved history
483
+ let currentTool = null;
484
+ let activeObject = null;
485
+ let history = [];
486
+ let historyIndex = -1;
487
+ let isDrawing = false;
488
+ let drawingPath = null;
489
+ let blurArea = null;
490
+ let polygonPoints = [];
491
+ let apiKey = null;
492
+
493
+ // Brush settings
494
+ let brushSettings = {
495
+ size: 5,
496
+ color: '#000000',
497
+ opacity: 1,
498
+ type: 'pencil'
499
+ };
500
+
501
+ // Load API key if exists
502
+ loadApiKey();
503
+
504
+ // UI Elements
505
+ const uploadModal = document.getElementById('uploadModal');
506
+ const exportModal = document.getElementById('exportModal');
507
+ const apiKeyModal = document.getElementById('apiKeyModal');
508
+ const fileInput = document.getElementById('fileInput');
509
+ const browseBtn = document.getElementById('browseBtn');
510
+ const uploadBtn = document.getElementById('uploadBtn');
511
+ const closeUploadModal = document.getElementById('closeUploadModal');
512
+ const closeExportModal = document.getElementById('closeExportModal');
513
+ const closeApiKeyModal = document.getElementById('closeApiKeyModal');
514
+ const saveBtn = document.getElementById('saveBtn');
515
+ const exportBtn = document.getElementById('exportBtn');
516
+ const apiKeyBtn = document.getElementById('apiKeyBtn');
517
+ const saveApiKeyBtn = document.getElementById('saveApiKeyBtn');
518
+ const clearApiKeyBtn = document.getElementById('clearApiKeyBtn');
519
+ const apiKeyInput = document.getElementById('apiKeyInput');
520
+ const apiKeyStatus = document.getElementById('apiKeyStatus');
521
+ const downloadBtn = document.getElementById('downloadBtn');
522
+ const copyToClipboardBtn = document.getElementById('copyToClipboardBtn');
523
+ const exportFormat = document.getElementById('exportFormat');
524
+ const exportQuality = document.getElementById('exportQuality');
525
+ const exportSize = document.getElementById('exportSize');
526
+ const qualityContainer = document.getElementById('qualityContainer');
527
+ const aiProcessing = document.getElementById('aiProcessing');
528
+ const imageInfo = document.getElementById('imageInfo');
529
+
530
+ // Tool buttons
531
+ const selectTool = document.getElementById('selectTool');
532
+ const textTool = document.getElementById('textTool');
533
+ const drawTool = document.getElementById('drawTool');
534
+ const shapeTool = document.getElementById('shapeTool');
535
+ const blurTool = document.getElementById('blurTool');
536
+ const aiRemoveBg = document.getElementById('aiRemoveBg');
537
+ const aiEnhance = document.getElementById('aiEnhance');
538
+ const aiTextRecognition = document.getElementById('aiTextRecognition');
539
+ const runAi = document.getElementById('runAi');
540
+
541
+ // Property panels
542
+ const generalProps = document.getElementById('generalProps');
543
+ const textProps = document.getElementById('textProps');
544
+ const drawProps = document.getElementById('drawProps');
545
+ const shapeProps = document.getElementById('shapeProps');
546
+ const blurProps = document.getElementById('blurProps');
547
+ const aiProps = document.getElementById('aiProps');
548
+ const polygonSidesContainer = document.getElementById('polygonSidesContainer');
549
+
550
+ // Property controls
551
+ const canvasBg = document.getElementById('canvasBg');
552
+ const canvasOpacity = document.getElementById('canvasOpacity');
553
+ const textContent = document.getElementById('textContent');
554
+ const textFont = document.getElementById('textFont');
555
+ const textSize = document.getElementById('textSize');
556
+ const textColor = document.getElementById('textColor');
557
+ const textBgColor = document.getElementById('textBgColor');
558
+ const brushSize = document.getElementById('brushSize');
559
+ const brushColor = document.getElementById('brushColor');
560
+ const brushOpacity = document.getElementById('brushOpacity');
561
+ const brushType = document.getElementById('brushType');
562
+ const shapeType = document.getElementById('shapeType');
563
+ const shapeFill = document.getElementById('shapeFill');
564
+ const shapeStroke = document.getElementById('shapeStroke');
565
+ const shapeStrokeWidth = document.getElementById('shapeStrokeWidth');
566
+ const polygonSides = document.getElementById('polygonSides');
567
+ const blurIntensity = document.getElementById('blurIntensity');
568
+ const blurRadius = document.getElementById('blurRadius');
569
+ const blurType = document.getElementById('blurType');
570
+ const aiEnhanceMode = document.getElementById('aiEnhanceMode');
571
+ const ocrLanguage = document.getElementById('ocrLanguage');
572
+
573
+ // Event Listeners
574
+ uploadBtn.addEventListener('click', () => uploadModal.classList.remove('hidden'));
575
+ closeUploadModal.addEventListener('click', () => uploadModal.classList.add('hidden'));
576
+ browseBtn.addEventListener('click', () => fileInput.click());
577
+ exportBtn.addEventListener('click', () => exportModal.classList.remove('hidden'));
578
+ closeExportModal.addEventListener('click', () => exportModal.classList.add('hidden'));
579
+ apiKeyBtn.addEventListener('click', () => apiKeyModal.classList.remove('hidden'));
580
+ closeApiKeyModal.addEventListener('click', () => apiKeyModal.classList.add('hidden'));
581
+ saveBtn.addEventListener('click', saveCanvasState);
582
+ downloadBtn.addEventListener('click', downloadCanvas);
583
+ copyToClipboardBtn.addEventListener('click', copyToClipboard);
584
+ saveApiKeyBtn.addEventListener('click', saveApiKey);
585
+ clearApiKeyBtn.addEventListener('click', clearApiKey);
586
+
587
+ fileInput.addEventListener('change', handleFileUpload);
588
+ exportFormat.addEventListener('change', toggleQualityControl);
589
+ shapeType.addEventListener('change', togglePolygonOptions);
590
+
591
+ // Tool selection
592
+ selectTool.addEventListener('click', () => setActiveTool('select'));
593
+ textTool.addEventListener('click', () => setActiveTool('text'));
594
+ drawTool.addEventListener('click', () => setActiveTool('draw'));
595
+ shapeTool.addEventListener('click', () => setActiveTool('shape'));
596
+ blurTool.addEventListener('click', () => setActiveTool('blur'));
597
+ aiRemoveBg.addEventListener('click', () => setActiveTool('aiRemoveBg'));
598
+ aiEnhance.addEventListener('click', () => setActiveTool('aiEnhance'));
599
+ aiTextRecognition.addEventListener('click', () => setActiveTool('aiTextRecognition'));
600
+ runAi.addEventListener('click', runAiProcessing);
601
+
602
+ // Canvas events
603
+ canvas.on('object:added', saveHistory);
604
+ canvas.on('object:modified', saveHistory);
605
+ canvas.on('object:removed', saveHistory);
606
+ canvas.on('selection:created', handleSelection);
607
+ canvas.on('selection:updated', handleSelection);
608
+ canvas.on('selection:cleared', clearSelection);
609
+ canvas.on('path:created', function(e) {
610
+ if (e.path && e.path.selectable) {
611
+ canvas.setActiveObject(e.path);
612
+ }
613
+ });
614
+
615
+ // Property changes
616
+ canvasBg.addEventListener('change', updateCanvasBackground);
617
+ canvasOpacity.addEventListener('input', updateCanvasOpacity);
618
+ textContent.addEventListener('input', updateTextContent);
619
+ textFont.addEventListener('change', updateTextFont);
620
+ textSize.addEventListener('input', updateTextSize);
621
+ textColor.addEventListener('change', updateTextColor);
622
+ textBgColor.addEventListener('change', updateTextBgColor);
623
+ brushSize.addEventListener('input', updateBrushSize);
624
+ brushColor.addEventListener('change', updateBrushColor);
625
+ brushOpacity.addEventListener('input', updateBrushOpacity);
626
+ brushType.addEventListener('change', updateBrushType);
627
+ shapeType.addEventListener('change', updateShapeType);
628
+ shapeFill.addEventListener('change', updateShapeFill);
629
+ shapeStroke.addEventListener('change', updateShapeStroke);
630
+ shapeStrokeWidth.addEventListener('input', updateShapeStrokeWidth);
631
+ polygonSides.addEventListener('change', updatePolygonSides);
632
+ blurIntensity.addEventListener('input', updateBlurIntensity);
633
+ blurRadius.addEventListener('input', updateBlurRadius);
634
+ blurType.addEventListener('change', updateBlurType);
635
+
636
+ // Keyboard shortcuts
637
+ document.addEventListener('keydown', function(e) {
638
+ // Undo: Ctrl+Z
639
+ if (e.ctrlKey && e.key === 'z') {
640
+ undo();
641
+ e.preventDefault();
642
+ }
643
+ // Redo: Ctrl+Y or Ctrl+Shift+Z
644
+ if ((e.ctrlKey && e.key === 'y') || (e.ctrlKey && e.shiftKey && e.key === 'z')) {
645
+ redo();
646
+ e.preventDefault();
647
+ }
648
+ // Delete key
649
+ if (e.key === 'Delete' && canvas.getActiveObject()) {
650
+ canvas.remove(canvas.getActiveObject());
651
+ saveHistory();
652
+ }
653
+ });
654
+
655
+ // Functions
656
+ function setActiveTool(tool) {
657
+ currentTool = tool;
658
+ resetCanvasEvents();
659
+
660
+ // Update UI
661
+ document.querySelectorAll('.sidebar button').forEach(btn => {
662
+ btn.classList.remove('bg-indigo-100', 'text-indigo-600');
663
+ btn.classList.add('text-gray-700', 'hover:bg-gray-100');
664
+ });
665
+
666
+ // Hide all property panels
667
+ generalProps.classList.add('hidden');
668
+ textProps.classList.add('hidden');
669
+ drawProps.classList.add('hidden');
670
+ shapeProps.classList.add('hidden');
671
+ blurProps.classList.add('hidden');
672
+ aiProps.classList.add('hidden');
673
+
674
+ switch(tool) {
675
+ case 'select':
676
+ document.getElementById('selectTool').classList.add('bg-indigo-100', 'text-indigo-600');
677
+ generalProps.classList.remove('hidden');
678
+ canvas.selection = true;
679
+ canvas.defaultCursor = 'default';
680
+ break;
681
+ case 'text':
682
+ document.getElementById('textTool').classList.add('bg-indigo-100', 'text-indigo-600');
683
+ textProps.classList.remove('hidden');
684
+ canvas.selection = false;
685
+ canvas.defaultCursor = 'text';
686
+ setupTextTool();
687
+ break;
688
+ case 'draw':
689
+ document.getElementById('drawTool').classList.add('bg-indigo-100', 'text-indigo-600');
690
+ drawProps.classList.remove('hidden');
691
+ canvas.selection = false;
692
+ canvas.defaultCursor = 'crosshair';
693
+ setupDrawTool();
694
+ break;
695
+ case 'shape':
696
+ document.getElementById('shapeTool').classList.add('bg-indigo-100', 'text-indigo-600');
697
+ shapeProps.classList.remove('hidden');
698
+ canvas.selection = false;
699
+ canvas.defaultCursor = 'crosshair';
700
+ setupShapeTool();
701
+ break;
702
+ case 'blur':
703
+ document.getElementById('blurTool').classList.add('bg-indigo-100', 'text-indigo-600');
704
+ blurProps.classList.remove('hidden');
705
+ canvas.selection = false;
706
+ canvas.defaultCursor = 'crosshair';
707
+ setupBlurTool();
708
+ break;
709
+ case 'aiRemoveBg':
710
+ case 'aiEnhance':
711
+ case 'aiTextRecognition':
712
+ document.getElementById(tool).classList.add('bg-indigo-100', 'text-indigo-600');
713
+ aiProps.classList.remove('hidden');
714
+ canvas.selection = true;
715
+ canvas.defaultCursor = 'default';
716
+ break;
717
+ }
718
+ }
719
+
720
+ function resetCanvasEvents() {
721
+ canvas.off('mouse:down');
722
+ canvas.off('mouse:move');
723
+ canvas.off('mouse:up');
724
+ canvas.off('object:added');
725
+ isDrawing = false;
726
+ drawingPath = null;
727
+ blurArea = null;
728
+ polygonPoints = [];
729
+ }
730
+
731
+ function setupTextTool() {
732
+ canvas.on('mouse:down', function(options) {
733
+ if (options.target) return;
734
+
735
+ const text = new fabric.IText('Double click to edit', {
736
+ left: options.e.clientX - canvas.upperCanvasEl.getBoundingClientRect().left,
737
+ top: options.e.clientY - canvas.upperCanvasEl.getBoundingClientRect().top,
738
+ fontFamily: textFont.value,
739
+ fontSize: parseInt(textSize.value),
740
+ fill: textColor.value,
741
+ backgroundColor: textBgColor.value === 'transparent' ? '' : textBgColor.value,
742
+ padding: 5,
743
+ selectable: true
744
+ });
745
+
746
+ canvas.add(text);
747
+ canvas.setActiveObject(text);
748
+ text.enterEditing();
749
+ text.selectAll();
750
+ });
751
+ }
752
+
753
+ function setupDrawTool() {
754
+ canvas.on('mouse:down', function(options) {
755
+ isDrawing = true;
756
+
757
+ const pointer = canvas.getPointer(options.e);
758
+ drawingPath = new fabric.Path(`M ${pointer.x} ${pointer.y}`, {
759
+ stroke: brushSettings.color,
760
+ strokeWidth: brushSettings.size,
761
+ fill: 'transparent',
762
+ opacity: brushSettings.opacity,
763
+ selectable: true
764
+ });
765
+
766
+ // Apply brush type
767
+ if (brushSettings.type === 'spray') {
768
+ drawingPath.strokeLineCap = 'round';
769
+ drawingPath.strokeLineJoin = 'round';
770
+ drawingPath.strokeDashArray = [1, brushSettings.size];
771
+ } else if (brushSettings.type === 'pattern') {
772
+ drawingPath.strokeDashArray = [brushSettings.size, brushSettings.size/2];
773
+ }
774
+
775
+ canvas.add(drawingPath);
776
+ });
777
+
778
+ canvas.on('mouse:move', function(options) {
779
+ if (!isDrawing || !drawingPath) return;
780
+
781
+ const pointer = canvas.getPointer(options.e);
782
+ const pathData = drawingPath.path;
783
+
784
+ pathData.push([
785
+ 'L',
786
+ pointer.x,
787
+ pointer.y
788
+ ]);
789
+
790
+ drawingPath.set({ path: pathData });
791
+ canvas.renderAll();
792
+ });
793
+
794
+ canvas.on('mouse:up', function() {
795
+ isDrawing = false;
796
+ drawingPath = null;
797
+ });
798
+ }
799
+
800
+ function setupShapeTool() {
801
+ let startX, startY, shape;
802
+
803
+ canvas.on('mouse:down', function(options) {
804
+ const pointer = canvas.getPointer(options.e);
805
+ startX = pointer.x;
806
+ startY = pointer.y;
807
+
808
+ if (shapeType.value === 'polygon') {
809
+ if (polygonPoints.length === 0) {
810
+ // First point
811
+ polygonPoints.push({ x: startX, y: startY });
812
+
813
+ // Create a line that will follow the mouse
814
+ shape = new fabric.Line([startX, startY, startX, startY], {
815
+ stroke: shapeStroke.value,
816
+ strokeWidth: parseInt(shapeStrokeWidth.value),
817
+ fill: shapeFill.value,
818
+ selectable: false
819
+ });
820
+ canvas.add(shape);
821
+ } else {
822
+ // Add new point
823
+ polygonPoints.push({ x: pointer.x, y: pointer.y });
824
+
825
+ // Update the polygon
826
+ updatePolygonShape();
827
+ }
828
+ return;
829
+ }
830
+
831
+ switch(shapeType.value) {
832
+ case 'rect':
833
+ shape = new fabric.Rect({
834
+ left: startX,
835
+ top: startY,
836
+ width: 0,
837
+ height: 0,
838
+ fill: shapeFill.value,
839
+ stroke: shapeStroke.value,
840
+ strokeWidth: parseInt(shapeStrokeWidth.value),
841
+ selectable: true
842
+ });
843
+ break;
844
+ case 'circle':
845
+ shape = new fabric.Circle({
846
+ left: startX,
847
+ top: startY,
848
+ radius: 0,
849
+ fill: shapeFill.value,
850
+ stroke: shapeStroke.value,
851
+ strokeWidth: parseInt(shapeStrokeWidth.value),
852
+ selectable: true
853
+ });
854
+ break;
855
+ case 'triangle':
856
+ shape = new fabric.Triangle({
857
+ left: startX,
858
+ top: startY,
859
+ width: 0,
860
+ height: 0,
861
+ fill: shapeFill.value,
862
+ stroke: shapeStroke.value,
863
+ strokeWidth: parseInt(shapeStrokeWidth.value),
864
+ selectable: true
865
+ });
866
+ break;
867
+ case 'line':
868
+ shape = new fabric.Line([startX, startY, startX, startY], {
869
+ stroke: shapeStroke.value,
870
+ strokeWidth: parseInt(shapeStrokeWidth.value),
871
+ selectable: true
872
+ });
873
+ break;
874
+ }
875
+
876
+ if (shape) {
877
+ canvas.add(shape);
878
+ activeObject = shape;
879
+ }
880
+ });
881
+
882
+ canvas.on('mouse:move', function(options) {
883
+ if (!activeObject && polygonPoints.length === 0) return;
884
+
885
+ const pointer = canvas.getPointer(options.e);
886
+
887
+ if (polygonPoints.length > 0) {
888
+ // Update the temporary line for polygon creation
889
+ const lastPoint = polygonPoints[polygonPoints.length - 1];
890
+ activeObject.set({
891
+ x2: pointer.x,
892
+ y2: pointer.y
893
+ });
894
+ canvas.renderAll();
895
+ return;
896
+ }
897
+
898
+ switch(shapeType.value) {
899
+ case 'rect':
900
+ activeObject.set({
901
+ width: pointer.x - startX,
902
+ height: pointer.y - startY,
903
+ absolutePositioned: true
904
+ });
905
+ break;
906
+ case 'circle':
907
+ const radius = Math.sqrt(
908
+ Math.pow(pointer.x - startX, 2) +
909
+ Math.pow(pointer.y - startY, 2)
910
+ ) / 2;
911
+ activeObject.set({
912
+ radius: radius,
913
+ left: startX - radius,
914
+ top: startY - radius
915
+ });
916
+ break;
917
+ case 'triangle':
918
+ activeObject.set({
919
+ width: pointer.x - startX,
920
+ height: pointer.y - startY
921
+ });
922
+ break;
923
+ case 'line':
924
+ activeObject.set({
925
+ x2: pointer.x,
926
+ y2: pointer.y
927
+ });
928
+ break;
929
+ }
930
+
931
+ canvas.renderAll();
932
+ });
933
+
934
+ canvas.on('mouse:up', function(options) {
935
+ if (shapeType.value === 'polygon') {
936
+ // For polygon, we don't finish until double click
937
+ return;
938
+ }
939
+
940
+ if (activeObject) {
941
+ // For other shapes, finalize the shape
942
+ if (Math.abs(activeObject.width) < 5 || Math.abs(activeObject.height) < 5) {
943
+ // Too small, probably accidental click
944
+ canvas.remove(activeObject);
945
+ } else {
946
+ // Make sure it's selectable
947
+ activeObject.set({ selectable: true });
948
+ canvas.setActiveObject(activeObject);
949
+ }
950
+ activeObject = null;
951
+ }
952
+ });
953
+
954
+ canvas.on('mouse:dblclick', function() {
955
+ if (shapeType.value === 'polygon' && polygonPoints.length >= 2) {
956
+ // Finalize the polygon
957
+ updatePolygonShape(true);
958
+ polygonPoints = [];
959
+ }
960
+ });
961
+ }
962
+
963
+ function updatePolygonShape(finalize = false) {
964
+ if (polygonPoints.length < 2) return;
965
+
966
+ // Remove the temporary line
967
+ if (activeObject) {
968
+ canvas.remove(activeObject);
969
+ }
970
+
971
+ // Create polygon path
972
+ let path = `M ${polygonPoints[0].x} ${polygonPoints[0].y}`;
973
+ for (let i = 1; i < polygonPoints.length; i++) {
974
+ path += ` L ${polygonPoints[i].x} ${polygonPoints[i].y}`;
975
+ }
976
+
977
+ if (finalize) {
978
+ // Close the path
979
+ path += ' Z';
980
+
981
+ const polygon = new fabric.Path(path, {
982
+ fill: shapeFill.value,
983
+ stroke: shapeStroke.value,
984
+ strokeWidth: parseInt(shapeStrokeWidth.value),
985
+ selectable: true
986
+ });
987
+
988
+ canvas.add(polygon);
989
+ canvas.setActiveObject(polygon);
990
+ activeObject = null;
991
+ } else {
992
+ // Create a new temporary line
993
+ const lastPoint = polygonPoints[polygonPoints.length - 1];
994
+ activeObject = new fabric.Line([
995
+ lastPoint.x, lastPoint.y,
996
+ lastPoint.x, lastPoint.y
997
+ ], {
998
+ stroke: shapeStroke.value,
999
+ strokeWidth: parseInt(shapeStrokeWidth.value),
1000
+ selectable: false
1001
+ });
1002
+
1003
+ canvas.add(activeObject);
1004
+ }
1005
+
1006
+ canvas.renderAll();
1007
+ }
1008
+
1009
+ function setupBlurTool() {
1010
+ canvas.on('mouse:down', function(options) {
1011
+ if (options.target) return;
1012
+
1013
+ const pointer = canvas.getPointer(options.e);
1014
+ blurArea = new fabric.Rect({
1015
+ left: pointer.x,
1016
+ top: pointer.y,
1017
+ width: 0,
1018
+ height: 0,
1019
+ fill: 'rgba(0,0,0,0.3)',
1020
+ stroke: '#666',
1021
+ strokeWidth: 1,
1022
+ strokeDashArray: [5, 5],
1023
+ selectable: false,
1024
+ hasControls: false,
1025
+ lockMovementX: true,
1026
+ lockMovementY: true,
1027
+ lockRotation: true,
1028
+ lockScalingX: true,
1029
+ lockScalingY: true,
1030
+ lockUniScaling: true
1031
+ });
1032
+
1033
+ canvas.add(blurArea);
1034
+ });
1035
+
1036
+ canvas.on('mouse:move', function(options) {
1037
+ if (!blurArea) return;
1038
+
1039
+ const pointer = canvas.getPointer(options.e);
1040
+ blurArea.set({
1041
+ width: pointer.x - blurArea.left,
1042
+ height: pointer.y - blurArea.top
1043
+ });
1044
+
1045
+ canvas.renderAll();
1046
+ });
1047
+
1048
+ canvas.on('mouse:up', function() {
1049
+ if (!blurArea) return;
1050
+
1051
+ // Apply blur effect to the selected area
1052
+ applyBlurEffect(blurArea);
1053
+ canvas.remove(blurArea);
1054
+ blurArea = null;
1055
+ });
1056
+ }
1057
+
1058
+ function applyBlurEffect(area) {
1059
+ // Create a rectangle that represents the blurred area
1060
+ const blurRect = new fabric.Rect({
1061
+ left: area.left,
1062
+ top: area.top,
1063
+ width: area.width,
1064
+ height: area.height,
1065
+ fill: 'rgba(255,255,255,0.7)',
1066
+ selectable: true,
1067
+ type: 'blur',
1068
+ blurIntensity: parseInt(blurIntensity.value),
1069
+ blurRadius: parseInt(blurRadius.value),
1070
+ blurType: blurType.value
1071
+ });
1072
+
1073
+ // Apply SVG filter for blur effect
1074
+ if (blurType.value === 'gaussian') {
1075
+ blurRect.set({
1076
+ filters: [
1077
+ new fabric.Image.filters.Blur({
1078
+ blur: blurRect.blurIntensity / 5
1079
+ })
1080
+ ]
1081
+ });
1082
+ } else if (blurType.value === 'pixelate') {
1083
+ blurRect.set({
1084
+ filters: [
1085
+ new fabric.Image.filters.Pixelate({
1086
+ blocksize: Math.max(2, 10 - blurRect.blurIntensity / 2)
1087
+ })
1088
+ ]
1089
+ });
1090
+ }
1091
+
1092
+ canvas.add(blurRect);
1093
+ canvas.renderAll();
1094
+ }
1095
+
1096
+ function runAiProcessing() {
1097
+ if (!apiKey) {
1098
+ showNotification('Please set your DeepSeek API key first', 'error');
1099
+ apiKeyModal.classList.remove('hidden');
1100
+ return;
1101
+ }
1102
+
1103
+ aiProcessing.classList.remove('hidden');
1104
+
1105
+ // Simulate AI processing with DeepSeek API
1106
+ // In a real implementation, you would make actual API calls here
1107
+ setTimeout(() => {
1108
+ aiProcessing.classList.add('hidden');
1109
+
1110
+ // Simulate different AI effects based on tool
1111
+ if (currentTool === 'aiRemoveBg') {
1112
+ // Simulate background removal
1113
+ const bgRect = new fabric.Rect({
1114
+ left: 100,
1115
+ top: 100,
1116
+ width: 200,
1117
+ height: 200,
1118
+ fill: 'transparent',
1119
+ stroke: '#6366f1',
1120
+ strokeWidth: 2,
1121
+ strokeDashArray: [5, 5],
1122
+ selectable: true
1123
+ });
1124
+
1125
+ canvas.add(bgRect);
1126
+ canvas.setActiveObject(bgRect);
1127
+
1128
+ showNotification('Background removed successfully!', 'success');
1129
+ }
1130
+ else if (currentTool === 'aiEnhance') {
1131
+ // Simulate image enhancement
1132
+ const bgImage = canvas.backgroundImage;
1133
+ if (bgImage) {
1134
+ bgImage.set({
1135
+ filters: [
1136
+ new fabric.Image.filters.Contrast({
1137
+ contrast: 0.1 * parseInt(aiEnhanceMode.value === 'contrast' ? 5 : 2)
1138
+ }),
1139
+ new fabric.Image.filters.Brightness({
1140
+ brightness: 0.05
1141
+ })
1142
+ ]
1143
+ });
1144
+ canvas.renderAll();
1145
+ showNotification('Image enhanced successfully!', 'success');
1146
+ }
1147
+ }
1148
+ else if (currentTool === 'aiTextRecognition') {
1149
+ // Simulate OCR text recognition
1150
+ const text = new fabric.IText('Recognized text: Lorem ipsum dolor sit amet', {
1151
+ left: 50,
1152
+ top: 50,
1153
+ fontFamily: 'Arial',
1154
+ fontSize: 16,
1155
+ fill: '#6366f1',
1156
+ selectable: true
1157
+ });
1158
+
1159
+ canvas.add(text);
1160
+ canvas.setActiveObject(text);
1161
+
1162
+ showNotification('Text recognized successfully!', 'success');
1163
+ }
1164
+ }, 2000);
1165
+ }
1166
+
1167
+ function handleFileUpload(e) {
1168
+ const file = e.target.files[0];
1169
+ if (!file) return;
1170
+
1171
+ if (!file.type.match('image.*')) {
1172
+ showNotification('Please select an image file', 'error');
1173
+ return;
1174
+ }
1175
+
1176
+ if (file.size > 10 * 1024 * 1024) {
1177
+ showNotification('Image size should be less than 10MB', 'error');
1178
+ return;
1179
+ }
1180
+
1181
+ const reader = new FileReader();
1182
+ reader.onload = function(f) {
1183
+ fabric.Image.fromURL(f.target.result, function(img) {
1184
+ // Clear canvas first
1185
+ canvas.clear();
1186
+ canvas.setBackgroundColor('#ffffff');
1187
+
1188
+ // Scale image to fit canvas if it's too large
1189
+ const scale = Math.min(
1190
+ canvas.width / img.width,
1191
+ canvas.height / img.height
1192
+ );
1193
+
1194
+ // Apply scaling if needed
1195
+ if (scale < 1) {
1196
+ img.scale(scale);
1197
+ }
1198
+
1199
+ canvas.setBackgroundImage(img, canvas.renderAll.bind(canvas), {
1200
+ originX: 'left',
1201
+ originY: 'top',
1202
+ left: (canvas.width - img.width * img.scaleX) / 2,
1203
+ top: (canvas.height - img.height * img.scaleY) / 2
1204
+ });
1205
+
1206
+ // Update image info
1207
+ imageInfo.textContent = `${file.name} (${Math.round(file.size/1024)}KB, ${img.width}x${img.height})`;
1208
+
1209
+ // Close modal
1210
+ uploadModal.classList.add('hidden');
1211
+
1212
+ // Save initial state
1213
+ saveHistory();
1214
+ }, {
1215
+ crossOrigin: 'anonymous'
1216
+ });
1217
+ };
1218
+ reader.readAsDataURL(file);
1219
+ }
1220
+
1221
+ function saveCanvasState() {
1222
+ const dataURL = canvas.toDataURL({
1223
+ format: 'png',
1224
+ quality: 1
1225
+ });
1226
+
1227
+ // In a real app, you might save to localStorage or server
1228
+ localStorage.setItem('savedCanvas', dataURL);
1229
+
1230
+ showNotification('Project saved successfully!', 'success');
1231
+ }
1232
+
1233
+ function downloadCanvas() {
1234
+ const format = exportFormat.value;
1235
+ const quality = exportFormat.value === 'jpg' || exportFormat.value === 'webp'
1236
+ ? parseInt(exportQuality.value) / 100
1237
+ : 1;
1238
+
1239
+ // Handle size option
1240
+ let width = canvas.width;
1241
+ let height = canvas.height;
1242
+
1243
+ if (exportSize.value !== 'original') {
1244
+ const size = parseInt(exportSize.value);
1245
+ const scale = size / canvas.width;
1246
+ width = size;
1247
+ height = canvas.height * scale;
1248
+ }
1249
+
1250
+ // Create temporary canvas for resizing
1251
+ const tempCanvas = document.createElement('canvas');
1252
+ const tempCtx = tempCanvas.getContext('2d');
1253
+ tempCanvas.width = width;
1254
+ tempCanvas.height = height;
1255
+
1256
+ // Draw the canvas content to the temporary canvas
1257
+ tempCtx.drawImage(canvas.lowerCanvasEl, 0, 0, width, height);
1258
+
1259
+ const link = document.createElement('a');
1260
+ link.download = `snapedit-${new Date().getTime()}.${format}`;
1261
+ link.href = tempCanvas.toDataURL({
1262
+ format: format,
1263
+ quality: quality
1264
+ });
1265
+ link.click();
1266
+
1267
+ exportModal.classList.add('hidden');
1268
+ }
1269
+
1270
+ function copyToClipboard() {
1271
+ const format = exportFormat.value;
1272
+ const quality = exportFormat.value === 'jpg' || exportFormat.value === 'webp'
1273
+ ? parseInt(exportQuality.value) / 100
1274
+ : 1;
1275
+
1276
+ canvas.toBlob(function(blob) {
1277
+ navigator.clipboard.write([
1278
+ new ClipboardItem({
1279
+ [blob.type]: blob
1280
+ })
1281
+ ]).then(() => {
1282
+ showNotification('Image copied to clipboard!', 'success');
1283
+ exportModal.classList.add('hidden');
1284
+ }).catch(err => {
1285
+ console.error('Failed to copy:', err);
1286
+ showNotification('Failed to copy image to clipboard', 'error');
1287
+ });
1288
+ }, `image/${format}`, quality);
1289
+ }
1290
+
1291
+ function toggleQualityControl() {
1292
+ if (exportFormat.value === 'jpg' || exportFormat.value === 'webp') {
1293
+ qualityContainer.classList.remove('hidden');
1294
+ } else {
1295
+ qualityContainer.classList.add('hidden');
1296
+ }
1297
+ }
1298
+
1299
+ function togglePolygonOptions() {
1300
+ if (shapeType.value === 'polygon') {
1301
+ polygonSidesContainer.classList.remove('hidden');
1302
+ } else {
1303
+ polygonSidesContainer.classList.add('hidden');
1304
+ }
1305
+ }
1306
+
1307
+ function saveHistory() {
1308
+ // Trim history if we've undone some actions
1309
+ if (historyIndex < history.length - 1) {
1310
+ history = history.slice(0, historyIndex + 1);
1311
+ }
1312
+
1313
+ // Save current canvas state
1314
+ history.push(JSON.stringify(canvas));
1315
+ historyIndex++;
1316
+
1317
+ // Limit history size
1318
+ if (history.length > 50) {
1319
+ history.shift();
1320
+ historyIndex--;
1321
+ }
1322
+
1323
+ // Update undo/redo buttons
1324
+ updateUndoRedoButtons();
1325
+ }
1326
+
1327
+ function undo() {
1328
+ if (historyIndex <= 0) return;
1329
+
1330
+ historyIndex--;
1331
+ loadFromHistory();
1332
+ }
1333
+
1334
+ function redo() {
1335
+ if (historyIndex >= history.length - 1) return;
1336
+
1337
+ historyIndex++;
1338
+ loadFromHistory();
1339
+ }
1340
+
1341
+ function loadFromHistory() {
1342
+ if (historyIndex < 0 || historyIndex >= history.length) return;
1343
+
1344
+ canvas.loadFromJSON(history[historyIndex], function() {
1345
+ canvas.renderAll();
1346
+ });
1347
+
1348
+ updateUndoRedoButtons();
1349
+ }
1350
+
1351
+ function updateUndoRedoButtons() {
1352
+ // In a real app, you might enable/disable buttons here
1353
+ }
1354
+
1355
+ function handleSelection(e) {
1356
+ activeObject = e.selected[0];
1357
+
1358
+ // Show relevant properties based on object type
1359
+ generalProps.classList.add('hidden');
1360
+ textProps.classList.add('hidden');
1361
+ drawProps.classList.add('hidden');
1362
+ shapeProps.classList.add('hidden');
1363
+ blurProps.classList.add('hidden');
1364
+ aiProps.classList.add('hidden');
1365
+
1366
+ if (!activeObject) {
1367
+ generalProps.classList.remove('hidden');
1368
+ return;
1369
+ }
1370
+
1371
+ if (activeObject.type === 'i-text') {
1372
+ textProps.classList.remove('hidden');
1373
+ textContent.value = activeObject.text;
1374
+ textFont.value = activeObject.fontFamily || 'Arial';
1375
+ textSize.value = activeObject.fontSize;
1376
+ textColor.value = activeObject.fill || '#000000';
1377
+ textBgColor.value = activeObject.backgroundColor || 'transparent';
1378
+ } else if (activeObject.type === 'path') {
1379
+ drawProps.classList.remove('hidden');
1380
+ brushSize.value = activeObject.strokeWidth;
1381
+ brushColor.value = activeObject.stroke;
1382
+ brushOpacity.value = (activeObject.opacity || 1) * 100;
1383
+ } else if (['rect', 'circle', 'triangle', 'line', 'polygon'].includes(activeObject.type)) {
1384
+ shapeProps.classList.remove('hidden');
1385
+ shapeFill.value = activeObject.fill || '#000000';
1386
+ shapeStroke.value = activeObject.stroke || '#000000';
1387
+ shapeStrokeWidth.value = activeObject.strokeWidth || 1;
1388
+ } else if (activeObject.type === 'blur') {
1389
+ blurProps.classList.remove('hidden');
1390
+ blurIntensity.value = activeObject.blurIntensity || 5;
1391
+ blurRadius.value = activeObject.blurRadius || 50;
1392
+ blurType.value = activeObject.blurType || 'gaussian';
1393
+ } else {
1394
+ generalProps.classList.remove('hidden');
1395
+ }
1396
+ }
1397
+
1398
+ function clearSelection() {
1399
+ activeObject = null;
1400
+ generalProps.classList.remove('hidden');
1401
+ textProps.classList.add('hidden');
1402
+ drawProps.classList.add('hidden');
1403
+ shapeProps.classList.add('hidden');
1404
+ blurProps.classList.add('hidden');
1405
+ aiProps.classList.add('hidden');
1406
+ }
1407
+
1408
+ function updateCanvasBackground() {
1409
+ canvas.setBackgroundColor(canvasBg.value, canvas.renderAll.bind(canvas));
1410
+ saveHistory();
1411
+ }
1412
+
1413
+ function updateCanvasOpacity() {
1414
+ canvas.setBackgroundColor(canvas.backgroundColor, canvas.renderAll.bind(canvas), {
1415
+ opacity: parseInt(canvasOpacity.value) / 100
1416
+ });
1417
+ saveHistory();
1418
+ }
1419
+
1420
+ function updateTextContent() {
1421
+ if (activeObject && activeObject.type === 'i-text') {
1422
+ activeObject.set('text', textContent.value);
1423
+ canvas.renderAll();
1424
+ saveHistory();
1425
+ }
1426
+ }
1427
+
1428
+ function updateTextFont() {
1429
+ if (activeObject && activeObject.type === 'i-text') {
1430
+ activeObject.set('fontFamily', textFont.value);
1431
+ canvas.renderAll();
1432
+ saveHistory();
1433
+ }
1434
+ }
1435
+
1436
+ function updateTextSize() {
1437
+ if (activeObject && activeObject.type === 'i-text') {
1438
+ activeObject.set('fontSize', parseInt(textSize.value));
1439
+ canvas.renderAll();
1440
+ saveHistory();
1441
+ }
1442
+ }
1443
+
1444
+ function updateTextColor() {
1445
+ if (activeObject && activeObject.type === 'i-text') {
1446
+ activeObject.set('fill', textColor.value);
1447
+ canvas.renderAll();
1448
+ saveHistory();
1449
+ }
1450
+ }
1451
+
1452
+ function updateTextBgColor() {
1453
+ if (activeObject && activeObject.type === 'i-text') {
1454
+ activeObject.set('backgroundColor', textBgColor.value === 'transparent' ? '' : textBgColor.value);
1455
+ canvas.renderAll();
1456
+ saveHistory();
1457
+ }
1458
+ }
1459
+
1460
+ function updateBrushSize() {
1461
+ brushSettings.size = parseInt(brushSize.value);
1462
+ }
1463
+
1464
+ function updateBrushColor() {
1465
+ brushSettings.color = brushColor.value;
1466
+ }
1467
+
1468
+ function updateBrushOpacity() {
1469
+ brushSettings.opacity = parseInt(brushOpacity.value) / 100;
1470
+ }
1471
+
1472
+ function updateBrushType() {
1473
+ brushSettings.type = brushType.value;
1474
+ }
1475
+
1476
+ function updateShapeType() {
1477
+ // This would change the shape type for future shapes
1478
+ }
1479
+
1480
+ function updateShapeFill() {
1481
+ if (activeObject && ['rect', 'circle', 'triangle', 'polygon'].includes(activeObject.type)) {
1482
+ activeObject.set('fill', shapeFill.value);
1483
+ canvas.renderAll();
1484
+ saveHistory();
1485
+ }
1486
+ }
1487
+
1488
+ function updateShapeStroke() {
1489
+ if (activeObject && ['rect', 'circle', 'triangle', 'line', 'polygon'].includes(activeObject.type)) {
1490
+ activeObject.set('stroke', shapeStroke.value);
1491
+ canvas.renderAll();
1492
+ saveHistory();
1493
+ }
1494
+ }
1495
+
1496
+ function updateShapeStrokeWidth() {
1497
+ if (activeObject && ['rect', 'circle', 'triangle', 'line', 'polygon'].includes(activeObject.type)) {
1498
+ activeObject.set('strokeWidth', parseInt(shapeStrokeWidth.value));
1499
+ canvas.renderAll();
1500
+ saveHistory();
1501
+ }
1502
+ }
1503
+
1504
+ function updatePolygonSides() {
1505
+ // This would update the number of sides for future polygons
1506
+ }
1507
+
1508
+ function updateBlurIntensity() {
1509
+ if (activeObject && activeObject.type === 'blur') {
1510
+ activeObject.set('blurIntensity', parseInt(blurIntensity.value));
1511
+
1512
+ // Update filter
1513
+ if (activeObject.blurType === 'gaussian') {
1514
+ activeObject.set({
1515
+ filters: [
1516
+ new fabric.Image.filters.Blur({
1517
+ blur: activeObject.blurIntensity / 5
1518
+ })
1519
+ ]
1520
+ });
1521
+ } else if (activeObject.blurType === 'pixelate') {
1522
+ activeObject.set({
1523
+ filters: [
1524
+ new fabric.Image.filters.Pixelate({
1525
+ blocksize: Math.max(2, 10 - activeObject.blurIntensity / 2)
1526
+ })
1527
+ ]
1528
+ });
1529
+ }
1530
+
1531
+ canvas.renderAll();
1532
+ saveHistory();
1533
+ }
1534
+ }
1535
+
1536
+ function updateBlurRadius() {
1537
+ if (activeObject && activeObject.type === 'blur') {
1538
+ activeObject.set('blurRadius', parseInt(blurRadius.value));
1539
+ canvas.renderAll();
1540
+ saveHistory();
1541
+ }
1542
+ }
1543
+
1544
+ function updateBlurType() {
1545
+ if (activeObject && activeObject.type === 'blur') {
1546
+ activeObject.set('blurType', blurType.value);
1547
+
1548
+ // Update filter based on type
1549
+ if (blurType.value === 'gaussian') {
1550
+ activeObject.set({
1551
+ filters: [
1552
+ new fabric.Image.filters.Blur({
1553
+ blur: activeObject.blurIntensity / 5
1554
+ })
1555
+ ]
1556
+ });
1557
+ } else if (blurType.value === 'pixelate') {
1558
+ activeObject.set({
1559
+ filters: [
1560
+ new fabric.Image.filters.Pixelate({
1561
+ blocksize: Math.max(2, 10 - activeObject.blurIntensity / 2)
1562
+ })
1563
+ ]
1564
+ });
1565
+ } else {
1566
+ // Mask type - no filter, just a semi-transparent rectangle
1567
+ activeObject.set({ filters: [] });
1568
+ }
1569
+
1570
+ canvas.renderAll();
1571
+ saveHistory();
1572
+ }
1573
+ }
1574
+
1575
+ function loadApiKey() {
1576
+ const encryptedKey = localStorage.getItem('deepseek_api_key');
1577
+ if (encryptedKey) {
1578
+ try {
1579
+ // In a real app, you would use a more secure decryption method
1580
+ apiKey = CryptoJS.AES.decrypt(encryptedKey, 'secure_salt').toString(CryptoJS.enc.Utf8);
1581
+ apiKeyStatus.innerHTML = '<i class="fas fa-circle text-green-500 mr-1"></i> API Key Loaded';
1582
+ apiKeyStatus.classList.remove('bg-gray-100');
1583
+ apiKeyStatus.classList.add('bg-green-100', 'text-green-600');
1584
+ } catch (e) {
1585
+ console.error('Failed to decrypt API key:', e);
1586
+ apiKey = null;
1587
+ }
1588
+ }
1589
+ }
1590
+
1591
+ function saveApiKey() {
1592
+ const key = apiKeyInput.value.trim();
1593
+ if (!key) {
1594
+ showNotification('Please enter a valid API key', 'error');
1595
+ return;
1596
+ }
1597
+
1598
+ try {
1599
+ // Encrypt the API key before storing
1600
+ const encryptedKey = CryptoJS.AES.encrypt(key, 'secure_salt').toString();
1601
+ localStorage.setItem('deepseek_api_key', encryptedKey);
1602
+ apiKey = key;
1603
+
1604
+ apiKeyStatus.innerHTML = '<i class="fas fa-circle text-green-500 mr-1"></i> API Key Loaded';
1605
+ apiKeyStatus.classList.remove('bg-gray-100');
1606
+ apiKeyStatus.classList.add('bg-green-100', 'text-green-600');
1607
+
1608
+ showNotification('API key saved successfully', 'success');
1609
+ apiKeyModal.classList.add('hidden');
1610
+ } catch (e) {
1611
+ console.error('Failed to encrypt API key:', e);
1612
+ showNotification('Failed to save API key', 'error');
1613
+ }
1614
+ }
1615
+
1616
+ function clearApiKey() {
1617
+ localStorage.removeItem('deepseek_api_key');
1618
+ apiKey = null;
1619
+ apiKeyInput.value = '';
1620
+
1621
+ apiKeyStatus.innerHTML = '<i class="fas fa-circle text-red-500 mr-1"></i> No API Key';
1622
+ apiKeyStatus.classList.remove('bg-green-100', 'text-green-600');
1623
+ apiKeyStatus.classList.add('bg-gray-100', 'text-gray-600');
1624
+
1625
+ showNotification('API key cleared', 'info');
1626
+ }
1627
+
1628
+ function showNotification(message, type = 'info') {
1629
+ const colors = {
1630
+ info: 'bg-blue-500',
1631
+ success: 'bg-green-500',
1632
+ warning: 'bg-yellow-500',
1633
+ error: 'bg-red-500'
1634
+ };
1635
+
1636
+ const icons = {
1637
+ info: 'fa-info-circle',
1638
+ success: 'fa-check-circle',
1639
+ warning: 'fa-exclamation-triangle',
1640
+ error: 'fa-exclamation-circle'
1641
+ };
1642
+
1643
+ const notification = document.createElement('div');
1644
+ notification.className = `fixed bottom-4 right-4 ${colors[type]} text-white px-4 py-2 rounded shadow-lg flex items-center`;
1645
+ notification.innerHTML = `<i class="fas ${icons[type]} mr-2"></i> ${message}`;
1646
+ document.body.appendChild(notification);
1647
+
1648
+ setTimeout(() => {
1649
+ notification.classList.add('opacity-0', 'transition-opacity', 'duration-300');
1650
+ setTimeout(() => {
1651
+ notification.remove();
1652
+ }, 300);
1653
+ }, 3000);
1654
+ }
1655
+
1656
+ // Initialize
1657
+ setActiveTool('select');
1658
+
1659
+ // User menu toggle
1660
+ document.getElementById('userMenuBtn').addEventListener('click', function() {
1661
+ document.getElementById('userMenu').classList.toggle('hidden');
1662
+ });
1663
+
1664
+ // Close user menu when clicking outside
1665
+ document.addEventListener('click', function(e) {
1666
+ if (!e.target.closest('#userMenu') && !e.target.closest('#userMenuBtn')) {
1667
+ document.getElementById('userMenu').classList.add('hidden');
1668
+ }
1669
+ });
1670
+ });
1671
+ </script>
1672
+ <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=sqibhe/snapeidit" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
1673
+ </html>
prompts.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ **Objective**: Develop a cross-platform screenshot editing tool where users can upload, annotate, enhance, and export screenshots with AI-powered features. **Technical Requirements**: 1. **Frontend**: React.js/Electron for desktop or React Native for mobile. Use HTML5 Canvas or Fabric.js for image manipulation. 2. **Backend**: Node.js/Django for API, user auth, and cloud sync. 3. **AI Integration**: - Use OpenCV/TensorFlow.js for object removal and background detection. - Integrate OCR (Tesseract.js/Azure Cognitive Services) for text extraction. 4. **Collaboration**: Implement WebSockets/Socket.io for real-time editing. 5. **Security**: End-to-end encryption (AES-256), GDPR compliance, and role-based access. **Features to Implement**: - All features listed above, prioritized as: Core Editing > AI Tools > Collaboration > Advanced Tools. - Ensure mobile-responsive design with offline-first capabilities. **Testing**: - Cross-browser testing (Chrome, Firefox, Safari). - Performance optimization for large images (10MB+). - User testing via beta releases with feedback loops. **Deployment**: - Dockerize the app for scalable cloud deployment (AWS/Google Cloud). - Provide detailed docs (user guides + API references). **Deliverables**: - MVP within 6 weeks, full release in 12 weeks. - Open-source core with premium features (e.g., AI tools) as paid add-ons.
2
+ Okay make sure you add Deepseek ai In this, The User Who wants to use this Tool canadd their own API key on the top of the tool to use it, also make sure that API Key is encrypted, Also this tool all features are not working fine you should enhance all tool feature to make sure its working accurately