devisor commited on
Commit
9d8aac1
·
verified ·
1 Parent(s): 7d64250

Add 2 files

Browse files
Files changed (2) hide show
  1. README.md +6 -4
  2. index.html +1081 -19
README.md CHANGED
@@ -1,10 +1,12 @@
1
  ---
2
- title: Editor Imagem Pixel
3
- emoji: 🌍
4
  colorFrom: pink
5
- colorTo: yellow
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: editor-imagem-pixel
3
+ emoji: 🐳
4
  colorFrom: pink
5
+ colorTo: blue
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,1081 @@
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="pt-br">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>PixelFlow - Editor de Imagens Online</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>
10
+ tailwind.config = {
11
+ darkMode: 'class',
12
+ theme: {
13
+ extend: {
14
+ colors: {
15
+ primary: {
16
+ 50: '#f0f9ff',
17
+ 100: '#e0f2fe',
18
+ 200: '#bae6fd',
19
+ 300: '#7dd3fc',
20
+ 400: '#38bdf8',
21
+ 500: '#0ea5e9',
22
+ 600: '#0284c7',
23
+ 700: '#0369a1',
24
+ 800: '#075985',
25
+ 900: '#0c4a6e',
26
+ },
27
+ secondary: {
28
+ 50: '#f8fafc',
29
+ 100: '#f1f5f9',
30
+ 200: '#e2e8f0',
31
+ 300: '#cbd5e1',
32
+ 400: '#94a3b8',
33
+ 500: '#64748b',
34
+ 600: '#475569',
35
+ 700: '#334155',
36
+ 800: '#1e293b',
37
+ 900: '#0f172a',
38
+ }
39
+ }
40
+ }
41
+ }
42
+ }
43
+ </script>
44
+ <style>
45
+ .dropzone {
46
+ border: 2px dashed #94a3b8;
47
+ border-radius: 0.5rem;
48
+ transition: all 0.3s ease;
49
+ }
50
+ .dropzone.active {
51
+ border-color: #0ea5e9;
52
+ background-color: rgba(14, 165, 233, 0.1);
53
+ }
54
+ .canvas-container {
55
+ position: relative;
56
+ overflow: hidden;
57
+ box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
58
+ }
59
+ .tool-btn {
60
+ transition: all 0.2s ease;
61
+ }
62
+ .tool-btn:hover {
63
+ transform: translateY(-2px);
64
+ box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
65
+ }
66
+ .tool-btn.active {
67
+ background-color: #0ea5e9 !important;
68
+ color: white !important;
69
+ }
70
+ .slider-thumb::-webkit-slider-thumb {
71
+ -webkit-appearance: none;
72
+ appearance: none;
73
+ width: 18px;
74
+ height: 18px;
75
+ border-radius: 50%;
76
+ background: #0ea5e9;
77
+ cursor: pointer;
78
+ }
79
+ .slider-thumb::-moz-range-thumb {
80
+ width: 18px;
81
+ height: 18px;
82
+ border-radius: 50%;
83
+ background: #0ea5e9;
84
+ cursor: pointer;
85
+ }
86
+ .dark .dropzone {
87
+ border-color: #475569;
88
+ }
89
+ .dark .dropzone.active {
90
+ border-color: #38bdf8;
91
+ background-color: rgba(56, 189, 248, 0.1);
92
+ }
93
+ @media (max-width: 768px) {
94
+ .mobile-collapse {
95
+ display: none;
96
+ }
97
+ .mobile-expand {
98
+ display: block;
99
+ }
100
+ }
101
+ #editor-canvas {
102
+ max-width: 100%;
103
+ max-height: 70vh;
104
+ display: block;
105
+ }
106
+ .filter-preview {
107
+ width: 100%;
108
+ height: 60px;
109
+ background-size: cover;
110
+ background-position: center;
111
+ }
112
+ </style>
113
+ </head>
114
+ <body class="bg-gray-50 dark:bg-gray-900 text-gray-800 dark:text-gray-200 transition-colors duration-200">
115
+ <div class="min-h-screen flex flex-col">
116
+ <!-- Header -->
117
+ <header class="bg-white dark:bg-gray-800 shadow-sm py-4 px-6 flex justify-between items-center">
118
+ <div class="flex items-center space-x-2">
119
+ <div class="w-8 h-8 rounded-full bg-gradient-to-r from-blue-500 to-teal-400 flex items-center justify-center">
120
+ <i class="fas fa-paint-brush text-white"></i>
121
+ </div>
122
+ <h1 class="text-xl font-bold">PixelFlow</h1>
123
+ </div>
124
+ <div class="flex items-center space-x-4">
125
+ <button id="theme-toggle" class="p-2 rounded-full hover:bg-gray-200 dark:hover:bg-gray-700">
126
+ <i class="fas fa-moon dark:hidden"></i>
127
+ <i class="fas fa-sun hidden dark:block"></i>
128
+ </button>
129
+ <button class="px-4 py-2 bg-primary-500 hover:bg-primary-600 text-white rounded-lg font-medium transition-colors">
130
+ <i class="fas fa-sign-in-alt mr-2"></i>Login
131
+ </button>
132
+ </div>
133
+ </header>
134
+
135
+ <!-- Main Content -->
136
+ <main class="flex-1 flex flex-col md:flex-row overflow-hidden">
137
+ <!-- Sidebar -->
138
+ <aside class="w-full md:w-16 lg:w-64 bg-white dark:bg-gray-800 shadow-sm p-2 md:p-4 flex flex-row md:flex-col overflow-x-auto md:overflow-x-hidden overflow-y-hidden md:overflow-y-auto">
139
+ <div class="flex md:flex-col space-x-2 md:space-x-0 md:space-y-2">
140
+ <!-- File Actions -->
141
+ <div class="tool-group">
142
+ <h3 class="text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wider mb-1 hidden md:block">Arquivo</h3>
143
+ <div class="flex md:flex-col space-x-2 md:space-x-0 md:space-y-2">
144
+ <button id="open-btn" class="tool-btn w-12 h-12 flex flex-col items-center justify-center rounded-lg bg-gray-100 dark:bg-gray-700 hover:bg-gray-200 dark:hover:bg-gray-600">
145
+ <i class="fas fa-folder-open text-lg"></i>
146
+ <span class="text-xs mt-1 hidden md:block">Abrir</span>
147
+ </button>
148
+ <button id="save-btn" class="tool-btn w-12 h-12 flex flex-col items-center justify-center rounded-lg bg-gray-100 dark:bg-gray-700 hover:bg-gray-200 dark:hover:bg-gray-600">
149
+ <i class="fas fa-save text-lg"></i>
150
+ <span class="text-xs mt-1 hidden md:block">Salvar</span>
151
+ </button>
152
+ <button id="export-btn" class="tool-btn w-12 h-12 flex flex-col items-center justify-center rounded-lg bg-gray-100 dark:bg-gray-700 hover:bg-gray-200 dark:hover:bg-gray-600">
153
+ <i class="fas fa-upload text-lg"></i>
154
+ <span class="text-xs mt-1 hidden md:block">Exportar</span>
155
+ </button>
156
+ </div>
157
+ </div>
158
+
159
+ <!-- Edit Tools -->
160
+ <div class="tool-group">
161
+ <h3 class="text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wider mb-1 hidden md:block">Editar</h3>
162
+ <div class="flex md:flex-col space-x-2 md:space-x-0 md:space-y-2">
163
+ <button id="undo-btn" class="tool-btn w-12 h-12 flex flex-col items-center justify-center rounded-lg bg-gray-100 dark:bg-gray-700 hover:bg-gray-200 dark:hover:bg-gray-600">
164
+ <i class="fas fa-undo text-lg"></i>
165
+ <span class="text-xs mt-1 hidden md:block">Desfazer</span>
166
+ </button>
167
+ <button id="redo-btn" class="tool-btn w-12 h-12 flex flex-col items-center justify-center rounded-lg bg-gray-100 dark:bg-gray-700 hover:bg-gray-200 dark:hover:bg-gray-600">
168
+ <i class="fas fa-redo text-lg"></i>
169
+ <span class="text-xs mt-1 hidden md:block">Refazer</span>
170
+ </button>
171
+ <button id="crop-btn" class="tool-btn w-12 h-12 flex flex-col items-center justify-center rounded-lg bg-gray-100 dark:bg-gray-700 hover:bg-gray-200 dark:hover:bg-gray-600">
172
+ <i class="fas fa-crop text-lg"></i>
173
+ <span class="text-xs mt-1 hidden md:block">Cortar</span>
174
+ </button>
175
+ <button id="resize-btn" class="tool-btn w-12 h-12 flex flex-col items-center justify-center rounded-lg bg-gray-100 dark:bg-gray-700 hover:bg-gray-200 dark:hover:bg-gray-600">
176
+ <i class="fas fa-expand text-lg"></i>
177
+ <span class="text-xs mt-1 hidden md:block">Redimensionar</span>
178
+ </button>
179
+ <button id="rotate-btn" class="tool-btn w-12 h-12 flex flex-col items-center justify-center rounded-lg bg-gray-100 dark:bg-gray-700 hover:bg-gray-200 dark:hover:bg-gray-600">
180
+ <i class="fas fa-sync-alt text-lg"></i>
181
+ <span class="text-xs mt-1 hidden md:block">Girar</span>
182
+ </button>
183
+ </div>
184
+ </div>
185
+
186
+ <!-- AI Tools -->
187
+ <div class="tool-group">
188
+ <h3 class="text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wider mb-1 hidden md:block">IA</h3>
189
+ <div class="flex md:flex-col space-x-2 md:space-x-0 md:space-y-2">
190
+ <button id="remove-bg-btn" class="tool-btn w-12 h-12 flex flex-col items-center justify-center rounded-lg bg-gradient-to-r from-purple-500 to-pink-500 text-white hover:from-purple-600 hover:to-pink-600">
191
+ <i class="fas fa-magic text-lg"></i>
192
+ <span class="text-xs mt-1 hidden md:block">Remover Fundo</span>
193
+ </button>
194
+ <button id="upscale-btn" class="tool-btn w-12 h-12 flex flex-col items-center justify-center rounded-lg bg-gradient-to-r from-blue-500 to-indigo-500 text-white hover:from-blue-600 hover:to-indigo-600">
195
+ <i class="fas fa-search-plus text-lg"></i>
196
+ <span class="text-xs mt-1 hidden md:block">Upscale 4K</span>
197
+ </button>
198
+ </div>
199
+ </div>
200
+
201
+ <!-- Drawing Tools -->
202
+ <div class="tool-group">
203
+ <h3 class="text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wider mb-1 hidden md:block">Desenho</h3>
204
+ <div class="flex md:flex-col space-x-2 md:space-x-0 md:space-y-2">
205
+ <button id="brush-btn" class="tool-btn w-12 h-12 flex flex-col items-center justify-center rounded-lg bg-gray-100 dark:bg-gray-700 hover:bg-gray-200 dark:hover:bg-gray-600">
206
+ <i class="fas fa-pencil-alt text-lg"></i>
207
+ <span class="text-xs mt-1 hidden md:block">Pincel</span>
208
+ </button>
209
+ <button id="eraser-btn" class="tool-btn w-12 h-12 flex flex-col items-center justify-center rounded-lg bg-gray-100 dark:bg-gray-700 hover:bg-gray-200 dark:hover:bg-gray-600">
210
+ <i class="fas fa-eraser text-lg"></i>
211
+ <span class="text-xs mt-1 hidden md:block">Borracha</span>
212
+ </button>
213
+ <button id="text-btn" class="tool-btn w-12 h-12 flex flex-col items-center justify-center rounded-lg bg-gray-100 dark:bg-gray-700 hover:bg-gray-200 dark:hover:bg-gray-600">
214
+ <i class="fas fa-font text-lg"></i>
215
+ <span class="text-xs mt-1 hidden md:block">Texto</span>
216
+ </button>
217
+ <button id="layers-btn" class="tool-btn w-12 h-12 flex flex-col items-center justify-center rounded-lg bg-gray-100 dark:bg-gray-700 hover:bg-gray-200 dark:hover:bg-gray-600">
218
+ <i class="fas fa-layer-group text-lg"></i>
219
+ <span class="text-xs mt-1 hidden md:block">Camadas</span>
220
+ </button>
221
+ </div>
222
+ </div>
223
+ </div>
224
+ </aside>
225
+
226
+ <!-- Canvas Area -->
227
+ <div class="flex-1 flex flex-col overflow-hidden">
228
+ <!-- Toolbar -->
229
+ <div class="bg-white dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700 p-2 flex items-center justify-between">
230
+ <div class="flex items-center space-x-2 overflow-x-auto">
231
+ <button id="adjustments-btn" class="tool-btn px-3 py-2 rounded-lg bg-gray-100 dark:bg-gray-700 hover:bg-gray-200 dark:hover:bg-gray-600 flex items-center">
232
+ <i class="fas fa-sliders-h mr-2"></i>
233
+ <span>Ajustes</span>
234
+ </button>
235
+ <button id="filters-btn" class="tool-btn px-3 py-2 rounded-lg bg-gray-100 dark:bg-gray-700 hover:bg-gray-200 dark:hover:bg-gray-600 flex items-center">
236
+ <i class="fas fa-filter mr-2"></i>
237
+ <span>Filtros</span>
238
+ </button>
239
+ <button id="colors-btn" class="tool-btn px-3 py-2 rounded-lg bg-gray-100 dark:bg-gray-700 hover:bg-gray-200 dark:hover:bg-gray-600 flex items-center">
240
+ <i class="fas fa-palette mr-2"></i>
241
+ <span>Cores</span>
242
+ </button>
243
+ <button id="measure-btn" class="tool-btn px-3 py-2 rounded-lg bg-gray-100 dark:bg-gray-700 hover:bg-gray-200 dark:hover:bg-gray-600 flex items-center">
244
+ <i class="fas fa-ruler-combined mr-2"></i>
245
+ <span>Medidas</span>
246
+ </button>
247
+ </div>
248
+ <div class="flex items-center space-x-2">
249
+ <button id="help-btn" class="tool-btn w-10 h-10 rounded-lg bg-gray-100 dark:bg-gray-700 hover:bg-gray-200 dark:hover:bg-gray-600 flex items-center justify-center">
250
+ <i class="fas fa-question"></i>
251
+ </button>
252
+ <button id="settings-btn" class="tool-btn w-10 h-10 rounded-lg bg-gray-100 dark:bg-gray-700 hover:bg-gray-200 dark:hover:bg-gray-600 flex items-center justify-center">
253
+ <i class="fas fa-cog"></i>
254
+ </button>
255
+ </div>
256
+ </div>
257
+
258
+ <!-- Canvas and Properties Panel -->
259
+ <div class="flex-1 flex flex-col md:flex-row overflow-hidden">
260
+ <!-- Canvas -->
261
+ <div class="flex-1 p-4 overflow-auto flex items-center justify-center">
262
+ <div id="dropzone" class="dropzone w-full h-full max-w-4xl max-h-[70vh] flex items-center justify-center">
263
+ <div id="canvas-container" class="canvas-container relative bg-gray-100 dark:bg-gray-700 rounded-lg">
264
+ <canvas id="editor-canvas" class="max-w-full max-h-[60vh]"></canvas>
265
+ <div id="drop-message" class="absolute inset-0 flex flex-col items-center justify-center p-8 text-center pointer-events-none">
266
+ <i class="fas fa-cloud-upload-alt text-4xl text-gray-400 mb-4"></i>
267
+ <h3 class="text-xl font-semibold text-gray-600 dark:text-gray-300">Arraste e solte sua imagem aqui</h3>
268
+ <p class="text-gray-500 dark:text-gray-400 mt-2">Ou clique para selecionar um arquivo</p>
269
+ <button id="upload-btn" class="pointer-events-auto mt-4 px-6 py-2 bg-primary-500 hover:bg-primary-600 text-white rounded-lg font-medium transition-colors">
270
+ <i class="fas fa-folder-open mr-2"></i>Selecionar Imagem
271
+ </button>
272
+ <input type="file" id="file-input" accept="image/*" class="hidden">
273
+ </div>
274
+ </div>
275
+ </div>
276
+ </div>
277
+
278
+ <!-- Properties Panel -->
279
+ <div id="properties-panel" class="w-full md:w-64 lg:w-80 bg-white dark:bg-gray-800 border-l border-gray-200 dark:border-gray-700 p-4 overflow-y-auto">
280
+ <h2 class="text-lg font-semibold mb-4 flex items-center justify-between">
281
+ <span>Propriedades</span>
282
+ <button id="close-properties" class="md:hidden text-gray-500 hover:text-gray-700 dark:hover:text-gray-300">
283
+ <i class="fas fa-times"></i>
284
+ </button>
285
+ </h2>
286
+
287
+ <!-- Adjustments Section -->
288
+ <div id="adjustments-section" class="mb-6">
289
+ <h3 class="text-sm font-medium mb-3 flex items-center">
290
+ <i class="fas fa-sliders-h mr-2 text-primary-500"></i>
291
+ Ajustes
292
+ </h3>
293
+ <div class="space-y-4">
294
+ <div>
295
+ <label class="flex justify-between items-center mb-1">
296
+ <span class="text-sm">Brilho</span>
297
+ <span id="brightness-value" class="text-xs bg-gray-100 dark:bg-gray-700 px-2 py-1 rounded">0%</span>
298
+ </label>
299
+ <input id="brightness-slider" type="range" min="-100" max="100" value="0" class="w-full h-2 bg-gray-200 dark:bg-gray-600 rounded-lg appearance-none cursor-pointer slider-thumb">
300
+ </div>
301
+ <div>
302
+ <label class="flex justify-between items-center mb-1">
303
+ <span class="text-sm">Contraste</span>
304
+ <span id="contrast-value" class="text-xs bg-gray-100 dark:bg-gray-700 px-2 py-1 rounded">0%</span>
305
+ </label>
306
+ <input id="contrast-slider" type="range" min="-100" max="100" value="0" class="w-full h-2 bg-gray-200 dark:bg-gray-600 rounded-lg appearance-none cursor-pointer slider-thumb">
307
+ </div>
308
+ <div>
309
+ <label class="flex justify-between items-center mb-1">
310
+ <span class="text-sm">Saturação</span>
311
+ <span id="saturation-value" class="text-xs bg-gray-100 dark:bg-gray-700 px-2 py-1 rounded">100%</span>
312
+ </label>
313
+ <input id="saturation-slider" type="range" min="0" max="200" value="100" class="w-full h-2 bg-gray-200 dark:bg-gray-600 rounded-lg appearance-none cursor-pointer slider-thumb">
314
+ </div>
315
+ <div>
316
+ <label class="flex justify-between items-center mb-1">
317
+ <span class="text-sm">Exposição</span>
318
+ <span id="exposure-value" class="text-xs bg-gray-100 dark:bg-gray-700 px-2 py-1 rounded">0%</span>
319
+ </label>
320
+ <input id="exposure-slider" type="range" min="-100" max="100" value="0" class="w-full h-2 bg-gray-200 dark:bg-gray-600 rounded-lg appearance-none cursor-pointer slider-thumb">
321
+ </div>
322
+ <div>
323
+ <label class="flex justify-between items-center mb-1">
324
+ <span class="text-sm">Nitidez</span>
325
+ <span id="sharpness-value" class="text-xs bg-gray-100 dark:bg-gray-700 px-2 py-1 rounded">0%</span>
326
+ </label>
327
+ <input id="sharpness-slider" type="range" min="0" max="100" value="0" class="w-full h-2 bg-gray-200 dark:bg-gray-600 rounded-lg appearance-none cursor-pointer slider-thumb">
328
+ </div>
329
+ <button id="reset-adjustments" class="w-full px-4 py-2 bg-gray-200 dark:bg-gray-700 hover:bg-gray-300 dark:hover:bg-gray-600 rounded-lg font-medium transition-colors">
330
+ Redefinir Ajustes
331
+ </button>
332
+ </div>
333
+ </div>
334
+
335
+ <!-- Filters Section -->
336
+ <div id="filters-section" class="mb-6">
337
+ <h3 class="text-sm font-medium mb-3 flex items-center">
338
+ <i class="fas fa-filter mr-2 text-primary-500"></i>
339
+ Filtros
340
+ </h3>
341
+ <div class="grid grid-cols-3 gap-2">
342
+ <button data-filter="none" class="filter-btn p-2 rounded border border-gray-200 dark:border-gray-700 hover:border-primary-500 dark:hover:border-primary-400">
343
+ <div class="filter-preview w-full h-12 bg-gray-100 dark:bg-gray-700 rounded mb-1" style="background-image: url('')"></div>
344
+ <span class="text-xs">Original</span>
345
+ </button>
346
+ <button data-filter="bw" class="filter-btn p-2 rounded border border-gray-200 dark:border-gray-700 hover:border-primary-500 dark:hover:border-primary-400">
347
+ <div class="filter-preview w-full h-12 bg-gray-300 rounded mb-1" style="filter: grayscale(100%)"></div>
348
+ <span class="text-xs">Preto e Branco</span>
349
+ </button>
350
+ <button data-filter="cool" class="filter-btn p-2 rounded border border-gray-200 dark:border-gray-700 hover:border-primary-500 dark:hover:border-primary-400">
351
+ <div class="filter-preview w-full h-12 bg-blue-100 rounded mb-1"></div>
352
+ <span class="text-xs">Frio</span>
353
+ </button>
354
+ <button data-filter="warm" class="filter-btn p-2 rounded border border-gray-200 dark:border-gray-700 hover:border-primary-500 dark:hover:border-primary-400">
355
+ <div class="filter-preview w-full h-12 bg-red-100 rounded mb-1"></div>
356
+ <span class="text-xs">Quente</span>
357
+ </button>
358
+ <button data-filter="vintage" class="filter-btn p-2 rounded border border-gray-200 dark:border-gray-700 hover:border-primary-500 dark:hover:border-primary-400">
359
+ <div class="filter-preview w-full h-12 bg-yellow-100 rounded mb-1"></div>
360
+ <span class="text-xs">Vintage</span>
361
+ </button>
362
+ <button data-filter="dramatic" class="filter-btn p-2 rounded border border-gray-200 dark:border-gray-700 hover:border-primary-500 dark:hover:border-primary-400">
363
+ <div class="filter-preview w-full h-12 bg-purple-100 rounded mb-1"></div>
364
+ <span class="text-xs">Dramático</span>
365
+ </button>
366
+ <button data-filter="invert" class="filter-btn p-2 rounded border border-gray-200 dark:border-gray-700 hover:border-primary-500 dark:hover:border-primary-400">
367
+ <div class="filter-preview w-full h-12 bg-gray-800 rounded mb-1" style="filter: invert(100%)"></div>
368
+ <span class="text-xs">Inverter</span>
369
+ </button>
370
+ <button data-filter="sepia" class="filter-btn p-2 rounded border border-gray-200 dark:border-gray-700 hover:border-primary-500 dark:hover:border-primary-400">
371
+ <div class="filter-preview w-full h-12 bg-amber-200 rounded mb-1" style="filter: sepia(100%)"></div>
372
+ <span class="text-xs">Sépia</span>
373
+ </button>
374
+ <button data-filter="blur" class="filter-btn p-2 rounded border border-gray-200 dark:border-gray-700 hover:border-primary-500 dark:hover:border-primary-400">
375
+ <div class="filter-preview w-full h-12 bg-blue-50 rounded mb-1" style="filter: blur(1px)"></div>
376
+ <span class="text-xs">Desfoque</span>
377
+ </button>
378
+ </div>
379
+ </div>
380
+
381
+ <!-- Color Tools Section -->
382
+ <div id="colors-section" class="mb-6 hidden">
383
+ <h3 class="text-sm font-medium mb-3 flex items-center">
384
+ <i class="fas fa-palette mr-2 text-primary-500"></i>
385
+ Cores
386
+ </h3>
387
+ <div class="space-y-4">
388
+ <div>
389
+ <label class="block text-sm font-medium mb-2">Seletor de Cor</label>
390
+ <input id="color-picker" type="color" value="#000000" class="w-full h-10 cursor-pointer">
391
+ </div>
392
+ <div>
393
+ <label class="flex justify-between items-center mb-1">
394
+ <span class="text-sm">Tonalidade</span>
395
+ <span id="hue-value" class="text-xs bg-gray-100 dark:bg-gray-700 px-2 py-1 rounded">0°</span>
396
+ </label>
397
+ <input id="hue-slider" type="range" min="0" max="360" value="0" class="w-full h-2 bg-gray-200 dark:bg-gray-600 rounded-lg appearance-none cursor-pointer slider-thumb">
398
+ </div>
399
+ <div>
400
+ <label class="block text-sm font-medium mb-2">Cor de Fundo</label>
401
+ <input id="bg-color-picker" type="color" value="#ffffff" class="w-full h-10 cursor-pointer">
402
+ </div>
403
+ </div>
404
+ </div>
405
+
406
+ <!-- Export Options -->
407
+ <div id="export-section">
408
+ <h3 class="text-sm font-medium mb-3 flex items-center">
409
+ <i class="fas fa-file-export mr-2 text-primary-500"></i>
410
+ Exportar
411
+ </h3>
412
+ <div class="space-y-3">
413
+ <div>
414
+ <label class="block text-sm font-medium mb-1">Formato</label>
415
+ <select id="export-format" class="w-full bg-gray-50 dark:bg-gray-700 border border-gray-200 dark:border-gray-600 rounded-lg px-3 py-2 text-sm">
416
+ <option value="png">PNG</option>
417
+ <option value="jpeg">JPG</option>
418
+ <option value="webp">WEBP</option>
419
+ </select>
420
+ </div>
421
+ <div>
422
+ <label class="block text-sm font-medium mb-1">Qualidade</label>
423
+ <input id="quality-slider" type="range" min="1" max="100" value="90" class="w-full h-2 bg-gray-200 dark:bg-gray-600 rounded-lg appearance-none cursor-pointer slider-thumb">
424
+ </div>
425
+ <button id="export-image-btn" class="w-full px-4 py-2 bg-primary-500 hover:bg-primary-600 text-white rounded-lg font-medium transition-colors flex items-center justify-center">
426
+ <i class="fas fa-download mr-2"></i>Exportar Imagem
427
+ </button>
428
+ </div>
429
+ </div>
430
+ </div>
431
+ </div>
432
+ </div>
433
+ </main>
434
+
435
+ <!-- Status Bar -->
436
+ <footer class="bg-white dark:bg-gray-800 border-t border-gray-200 dark:border-gray-700 px-4 py-2 text-sm flex items-center justify-between">
437
+ <div class="flex items-center space-x-4">
438
+ <span id="status-message" class="text-gray-500 dark:text-gray-400">Pronto</span>
439
+ <span id="zoom-level" class="text-gray-500 dark:text-gray-400 hidden md:inline">100%</span>
440
+ </div>
441
+ <div class="flex items-center space-x-4">
442
+ <span id="image-dimensions" class="text-gray-500 dark:text-gray-400">0 × 0 px</span>
443
+ <span id="image-size" class="text-gray-500 dark:text-gray-400 hidden md:inline">0 KB</span>
444
+ </div>
445
+ </footer>
446
+ </div>
447
+
448
+ <script>
449
+ // Theme Toggle
450
+ const themeToggle = document.getElementById('theme-toggle');
451
+ const html = document.documentElement;
452
+
453
+ themeToggle.addEventListener('click', () => {
454
+ html.classList.toggle('dark');
455
+ localStorage.setItem('theme', html.classList.contains('dark') ? 'dark' : 'light');
456
+ });
457
+
458
+ // Check for saved theme preference
459
+ if (localStorage.getItem('theme') === 'dark' || (!localStorage.getItem('theme') && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
460
+ html.classList.add('dark');
461
+ } else {
462
+ html.classList.remove('dark');
463
+ }
464
+
465
+ // Canvas and Image Handling
466
+ const canvas = document.getElementById('editor-canvas');
467
+ const ctx = canvas.getContext('2d');
468
+ const dropzone = document.getElementById('dropzone');
469
+ const fileInput = document.getElementById('file-input');
470
+ const uploadBtn = document.getElementById('upload-btn');
471
+ const dropMessage = document.getElementById('drop-message');
472
+
473
+ // Status bar elements
474
+ const statusMessage = document.getElementById('status-message');
475
+ const imageDimensions = document.getElementById('image-dimensions');
476
+ const imageSize = document.getElementById('image-size');
477
+ const zoomLevel = document.getElementById('zoom-level');
478
+
479
+ // Properties panel sections
480
+ const adjustmentsSection = document.getElementById('adjustments-section');
481
+ const filtersSection = document.getElementById('filters-section');
482
+ const colorsSection = document.getElementById('colors-section');
483
+
484
+ // Tool buttons
485
+ const adjustmentsBtn = document.getElementById('adjustments-btn');
486
+ const filtersBtn = document.getElementById('filters-btn');
487
+ const colorsBtn = document.getElementById('colors-btn');
488
+
489
+ // Image adjustment sliders
490
+ const brightnessSlider = document.getElementById('brightness-slider');
491
+ const contrastSlider = document.getElementById('contrast-slider');
492
+ const saturationSlider = document.getElementById('saturation-slider');
493
+ const exposureSlider = document.getElementById('exposure-slider');
494
+ const sharpnessSlider = document.getElementById('sharpness-slider');
495
+ const hueSlider = document.getElementById('hue-slider');
496
+
497
+ // Image adjustment values
498
+ const brightnessValue = document.getElementById('brightness-value');
499
+ const contrastValue = document.getElementById('contrast-value');
500
+ const saturationValue = document.getElementById('saturation-value');
501
+ const exposureValue = document.getElementById('exposure-value');
502
+ const sharpnessValue = document.getElementById('sharpness-value');
503
+ const hueValue = document.getElementById('hue-value');
504
+
505
+ // Color pickers
506
+ const colorPicker = document.getElementById('color-picker');
507
+ const bgColorPicker = document.getElementById('bg-color-picker');
508
+
509
+ // Export elements
510
+ const exportFormat = document.getElementById('export-format');
511
+ const qualitySlider = document.getElementById('quality-slider');
512
+ const exportImageBtn = document.getElementById('export-image-btn');
513
+
514
+ // State variables
515
+ let currentImage = null;
516
+ let originalImageData = null;
517
+ let historyStack = [];
518
+ let futureStack = [];
519
+ let currentTool = null;
520
+ let isDrawing = false;
521
+ let lastX = 0;
522
+ let lastY = 0;
523
+ let currentFilter = 'none';
524
+ let zoom = 1;
525
+
526
+ // Initialize properties panel sections
527
+ function showSection(section) {
528
+ adjustmentsSection.classList.add('hidden');
529
+ filtersSection.classList.add('hidden');
530
+ colorsSection.classList.add('hidden');
531
+
532
+ if (section === 'adjustments') {
533
+ adjustmentsSection.classList.remove('hidden');
534
+ adjustmentsBtn.classList.add('active');
535
+ filtersBtn.classList.remove('active');
536
+ colorsBtn.classList.remove('active');
537
+ } else if (section === 'filters') {
538
+ filtersSection.classList.remove('hidden');
539
+ adjustmentsBtn.classList.remove('active');
540
+ filtersBtn.classList.add('active');
541
+ colorsBtn.classList.remove('active');
542
+ } else if (section === 'colors') {
543
+ colorsSection.classList.remove('hidden');
544
+ adjustmentsBtn.classList.remove('active');
545
+ filtersBtn.classList.remove('active');
546
+ colorsBtn.classList.add('active');
547
+ }
548
+ }
549
+
550
+ // Handle drag and drop
551
+ ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
552
+ dropzone.addEventListener(eventName, preventDefaults, false);
553
+ });
554
+
555
+ function preventDefaults(e) {
556
+ e.preventDefault();
557
+ e.stopPropagation();
558
+ }
559
+
560
+ ['dragenter', 'dragover'].forEach(eventName => {
561
+ dropzone.addEventListener(eventName, highlight, false);
562
+ });
563
+
564
+ ['dragleave', 'drop'].forEach(eventName => {
565
+ dropzone.addEventListener(eventName, unhighlight, false);
566
+ });
567
+
568
+ function highlight() {
569
+ dropzone.classList.add('active');
570
+ }
571
+
572
+ function unhighlight() {
573
+ dropzone.classList.remove('active');
574
+ }
575
+
576
+ dropzone.addEventListener('drop', handleDrop, false);
577
+
578
+ function handleDrop(e) {
579
+ const dt = e.dataTransfer;
580
+ const files = dt.files;
581
+ handleFiles(files);
582
+ }
583
+
584
+ uploadBtn.addEventListener('click', () => {
585
+ fileInput.click();
586
+ });
587
+
588
+ fileInput.addEventListener('change', () => {
589
+ if (fileInput.files.length) {
590
+ handleFiles(fileInput.files);
591
+ }
592
+ });
593
+
594
+ function handleFiles(files) {
595
+ const file = files[0];
596
+ if (!file.type.match('image.*')) {
597
+ statusMessage.textContent = 'Por favor, selecione um arquivo de imagem válido';
598
+ return;
599
+ }
600
+
601
+ const reader = new FileReader();
602
+ reader.onload = function(e) {
603
+ const img = new Image();
604
+ img.onload = function() {
605
+ // Save original image data
606
+ currentImage = img;
607
+ canvas.width = img.width;
608
+ canvas.height = img.height;
609
+
610
+ // Reset adjustments and filters
611
+ resetAdjustments();
612
+ currentFilter = 'none';
613
+
614
+ // Draw image on canvas
615
+ ctx.drawImage(img, 0, 0);
616
+ saveToHistory();
617
+
618
+ // Hide drop message
619
+ dropMessage.classList.add('hidden');
620
+
621
+ // Update status bar
622
+ updateStatusBar(img.width, img.height, file.size);
623
+ statusMessage.textContent = 'Imagem carregada';
624
+ };
625
+ img.src = e.target.result;
626
+ };
627
+ reader.readAsDataURL(file);
628
+ }
629
+
630
+ function updateStatusBar(width, height, size) {
631
+ imageDimensions.textContent = `${width} × ${height} px`;
632
+
633
+ if (size) {
634
+ const sizeKB = Math.round(size / 1024);
635
+ imageSize.textContent = `${sizeKB} KB`;
636
+ }
637
+ }
638
+
639
+ // Save canvas state to history
640
+ function saveToHistory() {
641
+ const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
642
+ historyStack.push(imageData);
643
+ futureStack = []; // Clear redo stack
644
+ }
645
+
646
+ // Undo action
647
+ document.getElementById('undo-btn').addEventListener('click', () => {
648
+ if (historyStack.length > 1) { // Keep at least the original image
649
+ futureStack.push(historyStack.pop());
650
+ const prevState = historyStack[historyStack.length - 1];
651
+ ctx.putImageData(prevState, 0, 0);
652
+ statusMessage.textContent = 'Ação desfeita';
653
+ } else {
654
+ statusMessage.textContent = 'Nada para desfazer';
655
+ }
656
+ });
657
+
658
+ // Redo action
659
+ document.getElementById('redo-btn').addEventListener('click', () => {
660
+ if (futureStack.length > 0) {
661
+ const nextState = futureStack.pop();
662
+ historyStack.push(nextState);
663
+ ctx.putImageData(nextState, 0, 0);
664
+ statusMessage.textContent = 'Ação refeita';
665
+ } else {
666
+ statusMessage.textContent = 'Nada para refazer';
667
+ }
668
+ });
669
+
670
+ // Apply image adjustments
671
+ function applyAdjustments() {
672
+ if (!currentImage) return;
673
+
674
+ ctx.filter = getCurrentFilter();
675
+ ctx.drawImage(currentImage, 0, 0, canvas.width, canvas.height);
676
+ ctx.filter = 'none';
677
+
678
+ saveToHistory();
679
+ statusMessage.textContent = 'Ajustes aplicados';
680
+ }
681
+
682
+ function getCurrentFilter() {
683
+ const brightness = brightnessSlider.value;
684
+ const contrast = contrastSlider.value;
685
+ const saturation = saturationSlider.value / 100;
686
+ const exposure = exposureSlider.value;
687
+ const sharpness = sharpnessSlider.value;
688
+ const hue = hueSlider.value;
689
+
690
+ let filter = '';
691
+
692
+ if (brightness !== '0') filter += `brightness(${100 + parseInt(brightness)}%) `;
693
+ if (contrast !== '0') filter += `contrast(${100 + parseInt(contrast)}%) `;
694
+ if (saturation !== 1) filter += `saturate(${saturation}) `;
695
+ if (exposure !== '0') filter += `opacity(${1 + exposure/100}) `;
696
+ if (hue !== '0') filter += `hue-rotate(${hue}deg) `;
697
+
698
+ return filter.trim();
699
+ }
700
+
701
+ function resetAdjustments() {
702
+ brightnessSlider.value = 0;
703
+ contrastSlider.value = 0;
704
+ saturationSlider.value = 100;
705
+ exposureSlider.value = 0;
706
+ sharpnessSlider.value = 0;
707
+ hueSlider.value = 0;
708
+
709
+ brightnessValue.textContent = '0%';
710
+ contrastValue.textContent = '0%';
711
+ saturationValue.textContent = '100%';
712
+ exposureValue.textContent = '0%';
713
+ sharpnessValue.textContent = '0%';
714
+ hueValue.textContent = '0°';
715
+
716
+ if (currentImage) {
717
+ ctx.drawImage(currentImage, 0, 0, canvas.width, canvas.height);
718
+ saveToHistory();
719
+ statusMessage.textContent = 'Ajustes redefinidos';
720
+ }
721
+ }
722
+
723
+ // Update slider value displays
724
+ brightnessSlider.addEventListener('input', () => {
725
+ brightnessValue.textContent = `${brightnessSlider.value}%`;
726
+ applyAdjustments();
727
+ });
728
+
729
+ contrastSlider.addEventListener('input', () => {
730
+ contrastValue.textContent = `${contrastSlider.value}%`;
731
+ applyAdjustments();
732
+ });
733
+
734
+ saturationSlider.addEventListener('input', () => {
735
+ saturationValue.textContent = `${saturationSlider.value}%`;
736
+ applyAdjustments();
737
+ });
738
+
739
+ exposureSlider.addEventListener('input', () => {
740
+ exposureValue.textContent = `${exposureSlider.value}%`;
741
+ applyAdjustments();
742
+ });
743
+
744
+ sharpnessSlider.addEventListener('input', () => {
745
+ sharpnessValue.textContent = `${sharpnessSlider.value}%`;
746
+ applyAdjustments();
747
+ });
748
+
749
+ hueSlider.addEventListener('input', () => {
750
+ hueValue.textContent = `${hueSlider.value}°`;
751
+ applyAdjustments();
752
+ });
753
+
754
+ // Reset adjustments button
755
+ document.getElementById('reset-adjustments').addEventListener('click', resetAdjustments);
756
+
757
+ // Apply filters
758
+ document.querySelectorAll('.filter-btn').forEach(btn => {
759
+ btn.addEventListener('click', () => {
760
+ currentFilter = btn.getAttribute('data-filter');
761
+
762
+ // Remove active class from all filter buttons
763
+ document.querySelectorAll('.filter-btn').forEach(b => {
764
+ b.classList.remove('active');
765
+ });
766
+
767
+ // Add active class to clicked button
768
+ btn.classList.add('active');
769
+
770
+ // Apply the selected filter
771
+ applyFilter(currentFilter);
772
+ });
773
+ });
774
+
775
+ function applyFilter(filterName) {
776
+ if (!currentImage) return;
777
+
778
+ let filterValue = '';
779
+
780
+ switch(filterName) {
781
+ case 'bw':
782
+ filterValue = 'grayscale(100%)';
783
+ break;
784
+ case 'cool':
785
+ filterValue = 'sepia(50%) hue-rotate(180deg) saturate(2)';
786
+ break;
787
+ case 'warm':
788
+ filterValue = 'sepia(50%) hue-rotate(-20deg) saturate(2)';
789
+ break;
790
+ case 'vintage':
791
+ filterValue = 'sepia(100%) contrast(0.8) brightness(0.9)';
792
+ break;
793
+ case 'dramatic':
794
+ filterValue = 'contrast(1.4) brightness(0.8) saturate(1.5)';
795
+ break;
796
+ case 'invert':
797
+ filterValue = 'invert(100%)';
798
+ break;
799
+ case 'sepia':
800
+ filterValue = 'sepia(100%)';
801
+ break;
802
+ case 'blur':
803
+ filterValue = 'blur(1px)';
804
+ break;
805
+ default:
806
+ filterValue = 'none';
807
+ }
808
+
809
+ ctx.filter = filterValue;
810
+ ctx.drawImage(currentImage, 0, 0, canvas.width, canvas.height);
811
+ ctx.filter = 'none';
812
+
813
+ saveToHistory();
814
+ statusMessage.textContent = `Filtro ${filterName === 'none' ? 'removido' : 'aplicado'}`;
815
+ }
816
+
817
+ // Tool buttons functionality
818
+ document.getElementById('crop-btn').addEventListener('click', () => {
819
+ currentTool = 'crop';
820
+ activateTool('crop-btn');
821
+ statusMessage.textContent = 'Modo corte: selecione uma área na imagem';
822
+ });
823
+
824
+ document.getElementById('resize-btn').addEventListener('click', () => {
825
+ const width = prompt('Nova largura (em pixels):', canvas.width);
826
+ if (width && !isNaN(width)) {
827
+ const height = prompt('Nova altura (em pixels):', canvas.height);
828
+ if (height && !isNaN(height)) {
829
+ resizeCanvas(parseInt(width), parseInt(height));
830
+ statusMessage.textContent = `Imagem redimensionada para ${width} × ${height} px`;
831
+ }
832
+ }
833
+ currentTool = null;
834
+ deactivateTools();
835
+ });
836
+
837
+ document.getElementById('rotate-btn').addEventListener('click', () => {
838
+ rotateImage(90);
839
+ statusMessage.textContent = 'Imagem girada 90°';
840
+ });
841
+
842
+ document.getElementById('brush-btn').addEventListener('click', () => {
843
+ currentTool = 'brush';
844
+ activateTool('brush-btn');
845
+ statusMessage.textContent = 'Modo pincel: clique e arraste para desenhar';
846
+ });
847
+
848
+ document.getElementById('eraser-btn').addEventListener('click', () => {
849
+ currentTool = 'eraser';
850
+ activateTool('eraser-btn');
851
+ statusMessage.textContent = 'Modo borracha: clique e arraste para apagar';
852
+ });
853
+
854
+ document.getElementById('text-btn').addEventListener('click', () => {
855
+ currentTool = 'text';
856
+ activateTool('text-btn');
857
+ const text = prompt('Digite o texto:');
858
+ if (text) {
859
+ statusMessage.textContent = 'Clique na imagem para posicionar o texto';
860
+ canvas.addEventListener('click', addTextOnce);
861
+ } else {
862
+ currentTool = null;
863
+ deactivateTools();
864
+ }
865
+ });
866
+
867
+ function addTextOnce(e) {
868
+ if (currentTool === 'text') {
869
+ const text = prompt('Digite o texto:', '');
870
+ if (text) {
871
+ const size = prompt('Tamanho da fonte (em pixels):', '24');
872
+ const x = e.offsetX;
873
+ const y = e.offsetY;
874
+
875
+ ctx.font = `${size}px Arial`;
876
+ ctx.fillStyle = colorPicker.value;
877
+ ctx.fillText(text, x, y);
878
+
879
+ saveToHistory();
880
+ statusMessage.textContent = 'Texto adicionado';
881
+ }
882
+
883
+ canvas.removeEventListener('click', addTextOnce);
884
+ currentTool = null;
885
+ deactivateTools();
886
+ }
887
+ }
888
+
889
+ // Activate/deactivate tools
890
+ function activateTool(toolId) {
891
+ deactivateTools();
892
+ document.getElementById(toolId).classList.add('active');
893
+
894
+ if (['brush', 'eraser'].includes(currentTool)) {
895
+ canvas.style.cursor = 'crosshair';
896
+ setupDrawing();
897
+ } else {
898
+ canvas.style.cursor = 'default';
899
+ }
900
+ }
901
+
902
+ function deactivateTools() {
903
+ document.querySelectorAll('.tool-btn').forEach(btn => {
904
+ btn.classList.remove('active');
905
+ });
906
+ canvas.style.cursor = 'default';
907
+
908
+ // Remove drawing event listeners
909
+ canvas.removeEventListener('mousedown', startDrawing);
910
+ canvas.removeEventListener('mousemove', draw);
911
+ canvas.removeEventListener('mouseup', stopDrawing);
912
+ canvas.removeEventListener('mouseout', stopDrawing);
913
+ }
914
+
915
+ function setupDrawing() {
916
+ canvas.addEventListener('mousedown', startDrawing);
917
+ canvas.addEventListener('mousemove', draw);
918
+ canvas.addEventListener('mouseup', stopDrawing);
919
+ canvas.addEventListener('mouseout', stopDrawing);
920
+ }
921
+
922
+ function startDrawing(e) {
923
+ if (['brush', 'eraser'].includes(currentTool)) {
924
+ isDrawing = true;
925
+ [lastX, lastY] = [e.offsetX, e.offsetY];
926
+ }
927
+ }
928
+
929
+ function draw(e) {
930
+ if (!isDrawing) return;
931
+
932
+ ctx.beginPath();
933
+ ctx.moveTo(lastX, lastY);
934
+ ctx.lineTo(e.offsetX, e.offsetY);
935
+
936
+ if (currentTool === 'brush') {
937
+ ctx.strokeStyle = colorPicker.value;
938
+ ctx.lineWidth = 5;
939
+ ctx.lineCap = 'round';
940
+ ctx.lineJoin = 'round';
941
+ ctx.stroke();
942
+ } else if (currentTool === 'eraser') {
943
+ ctx.strokeStyle = bgColorPicker.value;
944
+ ctx.lineWidth = 20;
945
+ ctx.lineCap = 'round';
946
+ ctx.lineJoin = 'round';
947
+ ctx.stroke();
948
+ }
949
+
950
+ [lastX, lastY] = [e.offsetX, e.offsetY];
951
+ }
952
+
953
+ function stopDrawing() {
954
+ isDrawing = false;
955
+ if (['brush', 'eraser'].includes(currentTool)) {
956
+ saveToHistory();
957
+ }
958
+ }
959
+
960
+ // Image manipulation functions
961
+ function resizeCanvas(newWidth, newHeight) {
962
+ const tempCanvas = document.createElement('canvas');
963
+ const tempCtx = tempCanvas.getContext('2d');
964
+
965
+ tempCanvas.width = newWidth;
966
+ tempCanvas.height = newHeight;
967
+
968
+ // Draw current canvas content to temp canvas
969
+ tempCtx.drawImage(canvas, 0, 0, newWidth, newHeight);
970
+
971
+ // Resize main canvas
972
+ canvas.width = newWidth;
973
+ canvas.height = newHeight;
974
+
975
+ // Draw resized image back to main canvas
976
+ ctx.drawImage(tempCanvas, 0, 0);
977
+
978
+ // Update current image
979
+ currentImage.width = newWidth;
980
+ currentImage.height = newHeight;
981
+
982
+ updateStatusBar(newWidth, newHeight);
983
+ saveToHistory();
984
+ }
985
+
986
+ function rotateImage(degrees) {
987
+ const radians = degrees * Math.PI / 180;
988
+ const sin = Math.abs(Math.sin(radians));
989
+ const cos = Math.abs(Math.cos(radians));
990
+
991
+ const newWidth = Math.floor(canvas.width * cos + canvas.height * sin);
992
+ const newHeight = Math.floor(canvas.width * sin + canvas.height * cos);
993
+
994
+ ctx.save();
995
+
996
+ // Move to center of new canvas
997
+ ctx.translate(newWidth / 2, newHeight / 2);
998
+
999
+ // Rotate
1000
+ ctx.rotate(radians);
1001
+
1002
+ // Draw image centered
1003
+ ctx.drawImage(canvas, -canvas.width / 2, -canvas.height / 2);
1004
+
1005
+ ctx.restore();
1006
+
1007
+ // Resize canvas
1008
+ canvas.width = newWidth;
1009
+ canvas.height = newHeight;
1010
+
1011
+ // Redraw rotated image
1012
+ ctx.save();
1013
+ ctx.translate(newWidth / 2, newHeight / 2);
1014
+ ctx.rotate(radians);
1015
+ ctx.drawImage(currentImage, -currentImage.width / 2, -currentImage.height / 2);
1016
+ ctx.restore();
1017
+
1018
+ updateStatusBar(newWidth, newHeight);
1019
+ saveToHistory();
1020
+ }
1021
+
1022
+ // Zoom functionality
1023
+ function setZoom(newZoom) {
1024
+ const container = document.getElementById('canvas-container');
1025
+ zoom = Math.max(0.1, Math.min(5, newZoom)); // Limit zoom between 10% and 500%
1026
+
1027
+ container.style.transform = `scale(${zoom})`;
1028
+ container.style.transformOrigin = 'center center';
1029
+ zoomLevel.textContent = `${Math.round(zoom * 100)}%`;
1030
+ }
1031
+
1032
+ // Export image
1033
+ exportImageBtn.addEventListener('click', () => {
1034
+ if (!currentImage) {
1035
+ statusMessage.textContent = 'Nenhuma imagem para exportar';
1036
+ return;
1037
+ }
1038
+
1039
+ const format = exportFormat.value;
1040
+ const quality = qualitySlider.value / 100;
1041
+
1042
+ let mimeType;
1043
+ switch(format) {
1044
+ case 'png': mimeType = 'image/png'; break;
1045
+ case 'jpeg': mimeType = 'image/jpeg'; break;
1046
+ case 'webp': mimeType = 'image/webp'; break;
1047
+ default: mimeType = 'image/png';
1048
+ }
1049
+
1050
+ const dataUrl = canvas.toDataURL(mimeType, quality);
1051
+ const link = document.createElement('a');
1052
+ link.download = `pixelflow-edit.${format}`;
1053
+ link.href = dataUrl;
1054
+ link.click();
1055
+
1056
+ statusMessage.textContent = 'Imagem exportada';
1057
+ });
1058
+
1059
+ // Panel navigation
1060
+ adjustmentsBtn.addEventListener('click', () => showSection('adjustments'));
1061
+ filtersBtn.addEventListener('click', () => showSection('filters'));
1062
+ colorsBtn.addEventListener('click', () => showSection('colors'));
1063
+
1064
+ // Close properties panel on mobile
1065
+ document.getElementById('close-properties').addEventListener('click', () => {
1066
+ document.getElementById('properties-panel').classList.add('hidden');
1067
+ });
1068
+
1069
+ // Initialize with adjustments panel open
1070
+ showSection('adjustments');
1071
+
1072
+ // Set initial zoom
1073
+ setZoom(1);
1074
+
1075
+ // Show help dialog
1076
+ document.getElementById('help-btn').addEventListener('click', () => {
1077
+ alert('PixelFlow Editor\n\n1. Arraste e solte uma imagem ou clique em "Selecionar Imagem"\n2. Use as ferramentas para editar sua imagem\n3. Exporte sua imagem quando terminar\n\nTodos os controles estão funcionais neste editor!');
1078
+ });
1079
+ </script>
1080
+ <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=devisor/editor-imagem-pixel" style="color: #fff;text-decoration: underline;" target="_blank" >🧬 Remix</a></p></body>
1081
+ </html>