ð html
public
Untitled Paste
Guest
34m ago
7 views
Code Paste
| 1 | <!DOCTYPE html> |
| 2 | <html lang="th"> |
| 3 | <head> |
| 4 | <meta charset="UTF-8"> |
| 5 | <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| 6 | <title>AI Image Detector & Editor (Canva Style)</title> |
| 7 | <script src="https://cdn.tailwindcss.com"></script> |
| 8 | <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet"> |
| 9 | <!-- āļāļģāđāļāđāļē Google Fonts āļ āļēāļĐāļēāđāļāļĒāđāļĨāļ°āļāļąāļāļāļĪāļĐāļāļĩāđāļŦāļĨāļēāļāļŦāļĨāļēāļĒāļāļķāđāļ --> |
| 10 | <link href="https://fonts.googleapis.com/css2?family=Kanit:wght@300;400;500;700&family=Prompt:wght@300;400;500;700&family=Sarabun:wght@300;400;500;700&family=Chakra+Petch:wght@300;400;500;700&family=Mali:wght@300;400;500;700&family=Itim&family=Roboto:wght@300;400;500;700&family=Montserrat:wght@300;400;500;700&display=swap" rel="stylesheet"> |
| 11 | <!-- āļāļģāđāļāđāļē Fabric.js āļŠāļģāļŦāļĢāļąāļāļĢāļ°āļāļ Layer āđāļĨāļ°āļāļēāļĢāļāļąāļāļāļēāļĢ Object āđāļāļ Canva --> |
| 12 | <script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/5.3.1/fabric.min.js"></script> |
| 13 | <style> |
| 14 | ::-webkit-scrollbar { width: 8px; } |
| 15 | ::-webkit-scrollbar-track { background: #f1f1f1; } |
| 16 | ::-webkit-scrollbar-thumb { background: #cbd5e1; border-radius: 4px; } |
| 17 | ::-webkit-scrollbar-thumb:hover { background: #94a3b8; } |
| 18 | |
| 19 | /* āļāļąāļāļāļēāļĢ Container āļāļāļ Canvas āļāļĩāđāļāļđāļāļŠāļĢāđāļēāļāđāļāļĒ Fabric.js */ |
| 20 | .canvas-container { |
| 21 | margin: 0 auto; |
| 22 | box-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1); |
| 23 | } |
| 24 | </style> |
| 25 | </head> |
| 26 | <body class="bg-slate-50 text-slate-800 font-sans min-h-screen"> |
| 27 | |
| 28 | <div class="max-w-7xl mx-auto px-4 py-8"> |
| 29 | |
| 30 | <!-- Header --> |
| 31 | <header class="text-center mb-10"> |
| 32 | <h1 class="text-3xl md:text-4xl font-bold text-indigo-700 mb-2"> |
| 33 | <i class="fa-solid fa-layer-group mr-2"></i> AI Image Detector & Editor |
| 34 | </h1> |
| 35 | <p class="text-slate-500">āļāļĢāļ§āļāļŠāļāļāļ āļēāļ AI āđāļĨāļ°āļĢāļ°āļāļāđāļāđāđāļāļ āļēāļāđāļāļāđāļĨāđāļĒāļāļĢāđ (Canva Style)</p> |
| 36 | </header> |
| 37 | |
| 38 | <!-- Main Content Grid --> |
| 39 | <div class="grid grid-cols-1 lg:grid-cols-12 gap-6"> |
| 40 | |
| 41 | <!-- Left Column: Controls --> |
| 42 | <div class="lg:col-span-4 xl:col-span-3 space-y-6"> |
| 43 | |
| 44 | <!-- Upload Section --> |
| 45 | <div class="bg-white p-5 rounded-2xl shadow-sm border border-slate-200"> |
| 46 | <h2 class="text-lg font-semibold mb-4 text-slate-700">1. āļ āļēāļāļŦāļĨāļąāļ (Background)</h2> |
| 47 | <label for="imageUpload" class="flex flex-col items-center justify-center w-full h-28 border-2 border-slate-300 border-dashed rounded-xl cursor-pointer bg-slate-50 hover:bg-slate-100 transition"> |
| 48 | <div class="flex flex-col items-center justify-center pt-5 pb-6"> |
| 49 | <i class="fa-solid fa-cloud-arrow-up text-2xl text-indigo-500 mb-2"></i> |
| 50 | <p class="text-sm text-slate-500"><span class="font-semibold">āļāļąāļāđāļŦāļĨāļāļ āļēāļāļŦāļĨāļąāļ</span></p> |
| 51 | </div> |
| 52 | <input id="imageUpload" type="file" class="hidden" accept="image/*" /> |
| 53 | </label> |
| 54 | </div> |
| 55 | |
| 56 | <!-- Detection Status --> |
| 57 | <div id="detectionPanel" class="bg-white p-5 rounded-2xl shadow-sm border border-slate-200 hidden"> |
| 58 | <h2 class="text-lg font-semibold mb-4 text-slate-700">2. āļāļĨāļāļēāļĢāļ§āļīāđāļāļĢāļēāļ°āļŦāđāļ āļēāļ</h2> |
| 59 | |
| 60 | <div id="detectionLoading" class="flex flex-col items-center py-4"> |
| 61 | <i class="fa-solid fa-circle-notch fa-spin text-4xl text-indigo-500 mb-3"></i> |
| 62 | <p class="text-sm text-slate-600 font-medium" id="loadingText">āļāļģāļĨāļąāļāļāļĢāļ§āļāļŠāļāļ Metadata...</p> |
| 63 | <div class="w-full bg-slate-200 rounded-full h-2.5 mt-4"> |
| 64 | <div id="loadingBar" class="bg-indigo-600 h-2.5 rounded-full" style="width: 0%"></div> |
| 65 | </div> |
| 66 | </div> |
| 67 | |
| 68 | <div id="detectionResult" class="hidden text-center"> |
| 69 | <div id="resultIcon" class="text-5xl mb-3"></div> |
| 70 | <h3 id="resultTitle" class="text-lg font-bold mb-1"></h3> |
| 71 | <p id="resultDesc" class="text-xs text-slate-500 mb-4"></p> |
| 72 | |
| 73 | <div class="bg-slate-50 rounded-lg p-3 text-left text-xs text-slate-600 space-y-2 mb-4"> |
| 74 | <div class="flex justify-between"><span>āđāļāļāļēāļŠāđāļāđāļāļ āļēāļ AI:</span> <span id="scoreAi" class="font-semibold"></span></div> |
| 75 | <div class="flex justify-between"><span>Noise Pattern:</span> <span id="scoreNoise" class="font-semibold"></span></div> |
| 76 | </div> |
| 77 | |
| 78 | <button id="startEditBtn" class="w-full bg-indigo-600 hover:bg-indigo-700 text-white font-medium py-2 px-4 rounded-lg transition"> |
| 79 | <i class="fa-solid fa-wand-magic-sparkles mr-2"></i>āđāļāļīāļāđāļāļĢāļ·āđāļāļāļĄāļ·āļāđāļāđāđāļ (Layers) |
| 80 | </button> |
| 81 | </div> |
| 82 | </div> |
| 83 | |
| 84 | <!-- Editor Tools (Layers, Filters, Draw) --> |
| 85 | <div id="editorPanel" class="bg-white p-5 rounded-2xl shadow-sm border border-slate-200 hidden max-h-[800px] overflow-y-auto"> |
| 86 | <h2 class="text-lg font-semibold mb-4 text-slate-700">3. āđāļāļĢāļ·āđāļāļāļĄāļ·āļāđāļāđāđāļ (Canva Style)</h2> |
| 87 | |
| 88 | <!-- Add Elements / Layers --> |
| 89 | <div class="mb-5"> |
| 90 | <label class="block text-sm font-medium text-slate-700 mb-2"><i class="fa-solid fa-shapes mr-1"></i> āđāļāļīāđāļĄāļāļāļāđāļāļĢāļ°āļāļāļ</label> |
| 91 | <div class="grid grid-cols-2 gap-2"> |
| 92 | <button id="addTextBtn" class="bg-slate-100 hover:bg-slate-200 text-slate-700 py-2 rounded text-sm transition border border-slate-200"> |
| 93 | <i class="fa-solid fa-font"></i> āļāđāļāļāļ§āļēāļĄ |
| 94 | </button> |
| 95 | <label class="bg-slate-100 hover:bg-slate-200 text-slate-700 py-2 rounded text-sm transition border border-slate-200 text-center cursor-pointer"> |
| 96 | <i class="fa-regular fa-image"></i> āđāļāļīāđāļĄāļĢāļđāļāļ āļēāļ |
| 97 | <input type="file" id="addLayerImage" class="hidden" accept="image/*"> |
| 98 | </label> |
| 99 | <button id="addRectBtn" class="bg-slate-100 hover:bg-slate-200 text-slate-700 py-2 rounded text-sm transition border border-slate-200"> |
| 100 | <i class="fa-solid fa-square"></i> āļŠāļĩāđāđāļŦāļĨāļĩāđāļĒāļĄ |
| 101 | </button> |
| 102 | <button id="addCircleBtn" class="bg-slate-100 hover:bg-slate-200 text-slate-700 py-2 rounded text-sm transition border border-slate-200"> |
| 103 | <i class="fa-solid fa-circle"></i> āļ§āļāļāļĨāļĄ |
| 104 | </button> |
| 105 | </div> |
| 106 | |
| 107 | <!-- āđāļāļĢāļ·āđāļāļāļĄāļ·āļāļĨāļāđāļĨāļ°āđāļāđāđāļāļāđāļāļāļ§āļēāļĄāļāļāļ āļēāļ --> |
| 108 | <div class="mt-3"> |
| 109 | <button id="magicTextBtn" class="w-full bg-gradient-to-r from-purple-500 to-indigo-500 hover:from-purple-600 hover:to-indigo-600 text-white py-2 rounded-lg text-sm font-medium transition shadow-sm"> |
| 110 | <i class="fa-solid fa-wand-magic-sparkles mr-1"></i> āđāļāđāđāļāļāđāļāļāļ§āļēāļĄāđāļāļīāļĄāļāļāļ āļēāļ (Magic Edit) |
| 111 | </button> |
| 112 | <p class="text-[10px] text-slate-500 mt-1 text-center">āļāļĨāļīāļāļāļļāđāļĄ āđāļĨāđāļ§āļĨāļēāļāļāļĨāļļāļĄāļāđāļāļāļ§āļēāļĄāđāļāļīāļĄ āđāļāļ·āđāļāļĨāļāđāļĨāļ°āļāļīāļĄāļāđāđāļŦāļĄāđ</p> |
| 113 | </div> |
| 114 | </div> |
| 115 | |
| 116 | <!-- Manage Selected Object --> |
| 117 | <div id="objectControls" class="mb-5 p-3 bg-indigo-50 border border-indigo-100 rounded-lg opacity-50 pointer-events-none transition-opacity"> |
| 118 | <div class="flex justify-between items-center mb-2"> |
| 119 | <label class="block text-sm font-medium text-indigo-900">āļāļąāļāļāļēāļĢāļ§āļąāļāļāļļāļāļĩāđāđāļĨāļ·āļāļ</label> |
| 120 | <input type="color" id="objColorPicker" class="w-6 h-6 rounded cursor-pointer border-0 p-0 hidden" title="āđāļāļĨāļĩāđāļĒāļāļŠāļĩāļ§āļąāļāļāļļ/āļāđāļāļāļ§āļēāļĄ"> |
| 121 | </div> |
| 122 | <div class="flex space-x-2"> |
| 123 | <button id="bringForwardBtn" class="flex-1 bg-white hover:bg-indigo-100 text-indigo-700 py-1.5 rounded text-xs transition border border-indigo-200" title="āļāļģāļĄāļēāđāļ§āđāļāđāļēāļāļŦāļāđāļē"> |
| 124 | <i class="fa-solid fa-layer-group"></i> āļāļķāđāļāļāļ |
| 125 | </button> |
| 126 | <button id="sendBackwardBtn" class="flex-1 bg-white hover:bg-indigo-100 text-indigo-700 py-1.5 rounded text-xs transition border border-indigo-200" title="āļŠāđāļāđāļāļāđāļēāļāļŦāļĨāļąāļ"> |
| 127 | <i class="fa-solid fa-layer-group fa-flip-vertical"></i> āļĨāļāļĨāđāļēāļ |
| 128 | </button> |
| 129 | <button id="deleteObjBtn" class="flex-1 bg-white hover:bg-red-100 text-red-600 py-1.5 rounded text-xs transition border border-red-200"> |
| 130 | <i class="fa-solid fa-trash"></i> āļĨāļ |
| 131 | </button> |
| 132 | </div> |
| 133 | |
| 134 | <!-- Text Specific Controls --> |
| 135 | <div id="textControls" class="hidden mt-3 pt-3 border-t border-indigo-200"> |
| 136 | <label class="block text-xs font-medium text-indigo-900 mb-2"><i class="fa-solid fa-font"></i> āļĢāļđāļāđāļāļāļāđāļāļāļ§āļēāļĄ</label> |
| 137 | <div class="grid grid-cols-4 gap-2 items-end"> |
| 138 | <div class="col-span-2"> |
| 139 | <label class="text-[10px] text-indigo-700">āđāļāļāļāļąāļāļĐāļĢ</label> |
| 140 | <select id="fontFamilySelect" class="w-full text-xs p-1.5 border border-indigo-200 rounded bg-white"> |
| 141 | <optgroup label="āļĒāļāļāļāļīāļĒāļĄ"> |
| 142 | <option value="sans-serif">Sans-serif</option> |
| 143 | <option value="serif">Serif</option> |
| 144 | <option value="Arial">Arial</option> |
| 145 | </optgroup> |
| 146 | <optgroup label="āļ āļēāļĐāļēāđāļāļĒ"> |
| 147 | <option value="Kanit">Kanit</option> |
| 148 | <option value="Prompt">Prompt</option> |
| 149 | <option value="Sarabun">Sarabun</option> |
| 150 | <option value="Chakra Petch">Chakra Petch</option> |
| 151 | <option value="Mali">Mali</option> |
| 152 | <option value="Itim">Itim</option> |
| 153 | </optgroup> |
| 154 | <optgroup label="āļ āļēāļĐāļēāļāļąāļāļāļĪāļĐ"> |
| 155 | <option value="Roboto">Roboto</option> |
| 156 | <option value="Montserrat">Montserrat</option> |
| 157 | </optgroup> |
| 158 | </select> |
| 159 | </div> |
| 160 | <div> |
| 161 | <label class="text-[10px] text-indigo-700">āļāļāļēāļ</label> |
| 162 | <input type="number" id="fontSizeInput" min="10" max="200" value="40" class="w-full text-xs p-1.5 border border-indigo-200 rounded bg-white"> |
| 163 | </div> |
| 164 | <div class="flex flex-col justify-end"> |
| 165 | <label class="text-[10px] text-indigo-700">āļŠāđāļāļĨāđ/āļŠāļĩ</label> |
| 166 | <div class="flex space-x-1"> |
| 167 | <button id="textBoldBtn" class="w-7 h-[30px] bg-white border border-indigo-200 rounded flex items-center justify-center text-indigo-700 hover:bg-indigo-50 transition" title="āđāļāļīāļ/āļāļīāļāļāļąāļ§āļŦāļāļē"> |
| 168 | <i class="fa-solid fa-bold text-xs"></i> |
| 169 | </button> |
| 170 | <input type="color" id="textColorPicker" value="#333333" class="w-7 h-[30px] p-0 border border-indigo-200 rounded bg-white cursor-pointer" title="āļŠāļĩāļāđāļāļāļ§āļēāļĄ"> |
| 171 | </div> |
| 172 | </div> |
| 173 | </div> |
| 174 | </div> |
| 175 | </div> |
| 176 | |
| 177 | <hr class="border-slate-100 my-4"> |
| 178 | |
| 179 | <!-- Filters (Applies to selected image or background) --> |
| 180 | <div class="space-y-3 mb-5"> |
| 181 | <label class="block text-sm font-medium text-slate-700 mb-1"><i class="fa-solid fa-sliders mr-1"></i> āļāļĢāļąāļāđāļāđāļāļŠāļĩ/āđāļŠāļ (āļ āļēāļāļāļĩāđāđāļĨāļ·āļāļ)</label> |
| 182 | <div> |
| 183 | <label class="flex justify-between text-xs text-slate-500 mb-1"> |
| 184 | <span>āļāļ§āļēāļĄāļŠāļ§āđāļēāļ</span> <span id="valBright">0</span> |
| 185 | </label> |
| 186 | <input type="range" id="filterBright" min="-100" max="100" value="0" class="w-full h-1.5 bg-slate-200 rounded-lg appearance-none cursor-pointer"> |
| 187 | </div> |
| 188 | <div> |
| 189 | <label class="flex justify-between text-xs text-slate-500 mb-1"> |
| 190 | <span>āļāļ§āļēāļĄāđāļāļĢāļĩāļĒāļāļāđāļēāļ</span> <span id="valContrast">0</span> |
| 191 | </label> |
| 192 | <input type="range" id="filterContrast" min="-100" max="100" value="0" class="w-full h-1.5 bg-slate-200 rounded-lg appearance-none cursor-pointer"> |
| 193 | </div> |
| 194 | <div> |
| 195 | <label class="flex justify-between text-xs text-slate-500 mb-1"> |
| 196 | <span>āļāļ§āļēāļĄāļāļīāđāļĄāļāļąāļ§āļŠāļĩ</span> <span id="valSaturate">0</span> |
| 197 | </label> |
| 198 | <input type="range" id="filterSaturate" min="-100" max="100" value="0" class="w-full h-1.5 bg-slate-200 rounded-lg appearance-none cursor-pointer"> |
| 199 | </div> |
| 200 | <button id="resetFiltersBtn" class="text-xs text-indigo-600 hover:text-indigo-800">āļĢāļĩāđāļāđāļāļāļīāļĨāđāļāļāļĢāđ</button> |
| 201 | </div> |
| 202 | |
| 203 | <hr class="border-slate-100 my-4"> |
| 204 | |
| 205 | <!-- Drawing --> |
| 206 | <div class="mb-5"> |
| 207 | <label class="block text-sm font-medium text-slate-700 mb-2"><i class="fa-solid fa-pen mr-1"></i> āļ§āļēāļāđāļāļĩāļĒāļāļāļīāļŠāļĢāļ°</label> |
| 208 | <div class="flex items-center space-x-3 mb-3"> |
| 209 | <input type="color" id="brushColor" value="#ff0000" class="w-8 h-8 rounded cursor-pointer border-0 p-0"> |
| 210 | <input type="range" id="brushSize" min="1" max="50" value="5" class="flex-1 h-1.5 bg-slate-200 rounded-lg appearance-none cursor-pointer"> |
| 211 | </div> |
| 212 | <button id="toggleDrawBtn" class="w-full bg-slate-100 hover:bg-slate-200 text-slate-700 py-2 rounded-lg text-sm font-medium transition border border-slate-300"> |
| 213 | <i class="fa-solid fa-pen-nib mr-1"></i> āđāļāļīāļāđāļŦāļĄāļāļ§āļēāļāđāļāļĩāļĒāļ |
| 214 | </button> |
| 215 | </div> |
| 216 | |
| 217 | <!-- Actions --> |
| 218 | <div class="flex flex-col space-y-2 mt-6"> |
| 219 | <button id="downloadBtn" class="w-full bg-emerald-600 hover:bg-emerald-700 text-white font-medium py-2.5 px-4 rounded-lg transition flex justify-center items-center shadow-sm"> |
| 220 | <i class="fa-solid fa-download mr-2"></i> āļāļēāļ§āļāđāđāļŦāļĨāļāļāļĨāļāļēāļ |
| 221 | </button> |
| 222 | </div> |
| 223 | </div> |
| 224 | </div> |
| 225 | |
| 226 | <!-- Right Column: Canvas Display (Fabric.js) --> |
| 227 | <div class="lg:col-span-8 xl:col-span-9 flex flex-col"> |
| 228 | <!-- Toolbar hint --> |
| 229 | <div class="bg-indigo-50 text-indigo-800 text-sm py-2 px-4 rounded-t-xl border border-indigo-100 flex justify-between"> |
| 230 | <span><i class="fa-solid fa-circle-info mr-1"></i> āļāļąāļāđāļāļīāđāļĨāļāļĨāļīāļāļāļĩāđāļāđāļāļāļ§āļēāļĄāđāļāļ·āđāļāđāļāđāđāļ / āļĨāļēāļāđāļāļ·āđāļāļĒāđāļēāļĒāļāļģāđāļŦāļāđāļ</span> |
| 231 | <span id="canvasDimInfo" class="font-mono text-xs text-indigo-500"></span> |
| 232 | </div> |
| 233 | |
| 234 | <div id="canvasWrapper" class="bg-slate-200 p-2 rounded-b-xl shadow-inner border border-slate-300 min-h-[500px] flex items-center justify-center overflow-auto flex-1 relative bg-[url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMCIgaGVpZ2h0PSIyMCI+CjxyZWN0IHdpZHRoPSIyMCIgaGVpZ2h0PSIyMCIgZmlsbD0iI2ZmZmZmZiIvPgo8cmVjdCB3aWR0aD0iMTAiIGhlaWdodD0iMTAiIGZpbGw9IiNmM2Y0ZjYiLz4KPHJlY3QgeD0iMTAiIHk9IjEwIiB3aWR0aD0iMTAiIGhlaWdodD0iMTAiIGZpbGw9IiNmM2Y0ZjYiLz4KPC9zdmc+')]"> |
| 235 | |
| 236 | <!-- Placeholder text before upload --> |
| 237 | <div id="canvasPlaceholder" class="text-slate-400 text-center pointer-events-none absolute z-10"> |
| 238 | <i class="fa-regular fa-images text-6xl mb-3 opacity-50"></i> |
| 239 | <p>āļāļąāļāđāļŦāļĨāļāļ āļēāļāļŦāļĨāļąāļāđāļāļ·āđāļāđāļĢāļīāđāļĄāļŠāļĢāđāļēāļāļāļīāđāļāļāļēāļ</p> |
| 240 | </div> |
| 241 | |
| 242 | <!-- Fabric Canvas Canvas Element --> |
| 243 | <canvas id="mainCanvas"></canvas> |
| 244 | </div> |
| 245 | </div> |
| 246 | |
| 247 | </div> |
| 248 | </div> |
| 249 | |
| 250 | <script> |
| 251 | // --- Fabric.js Initialization --- |
| 252 | const canvas = new fabric.Canvas('mainCanvas', { |
| 253 | isDrawingMode: false, |
| 254 | preserveObjectStacking: true // Keep objects in order when selected |
| 255 | }); |
| 256 | |
| 257 | let baseImage = null; // The background image uploaded first |
| 258 | |
| 259 | // --- DOM Elements --- |
| 260 | const imageUpload = document.getElementById('imageUpload'); |
| 261 | const detectionPanel = document.getElementById('detectionPanel'); |
| 262 | const editorPanel = document.getElementById('editorPanel'); |
| 263 | const canvasPlaceholder = document.getElementById('canvasPlaceholder'); |
| 264 | const canvasDimInfo = document.getElementById('canvasDimInfo'); |
| 265 | |
| 266 | // Layers & Objects controls |
| 267 | const addTextBtn = document.getElementById('addTextBtn'); |
| 268 | const addLayerImage = document.getElementById('addLayerImage'); |
| 269 | const addRectBtn = document.getElementById('addRectBtn'); |
| 270 | const addCircleBtn = document.getElementById('addCircleBtn'); |
| 271 | const magicTextBtn = document.getElementById('magicTextBtn'); |
| 272 | const objectControls = document.getElementById('objectControls'); |
| 273 | const objColorPicker = document.getElementById('objColorPicker'); |
| 274 | const bringForwardBtn = document.getElementById('bringForwardBtn'); |
| 275 | const sendBackwardBtn = document.getElementById('sendBackwardBtn'); |
| 276 | const deleteObjBtn = document.getElementById('deleteObjBtn'); |
| 277 | const textControls = document.getElementById('textControls'); |
| 278 | const fontFamilySelect = document.getElementById('fontFamilySelect'); |
| 279 | const fontSizeInput = document.getElementById('fontSizeInput'); |
| 280 | const textColorPicker = document.getElementById('textColorPicker'); |
| 281 | const textBoldBtn = document.getElementById('textBoldBtn'); |
| 282 | |
| 283 | // Filter Controls |
| 284 | const filterBright = document.getElementById('filterBright'); |
| 285 | const filterContrast = document.getElementById('filterContrast'); |
| 286 | const filterSaturate = document.getElementById('filterSaturate'); |
| 287 | const resetFiltersBtn = document.getElementById('resetFiltersBtn'); |
| 288 | |
| 289 | // Drawing Controls |
| 290 | const toggleDrawBtn = document.getElementById('toggleDrawBtn'); |
| 291 | const brushColor = document.getElementById('brushColor'); |
| 292 | const brushSize = document.getElementById('brushSize'); |
| 293 | |
| 294 | // Action |
| 295 | const downloadBtn = document.getElementById('downloadBtn'); |
| 296 | |
| 297 | // Magic Text Variables |
| 298 | let isMagicTextMode = false; |
| 299 | let magicRect = null; |
| 300 | let origX, origY; |
| 301 | |
| 302 | // --- 1. Load Main Image --- |
| 303 | imageUpload.addEventListener('change', function(e) { |
| 304 | const file = e.target.files[0]; |
| 305 | if (!file) return; |
| 306 | |
| 307 | const reader = new FileReader(); |
| 308 | reader.onload = function(event) { |
| 309 | // Remove placeholder |
| 310 | canvasPlaceholder.classList.add('hidden'); |
| 311 | |
| 312 | fabric.Image.fromURL(event.target.result, function(img) { |
| 313 | // Set canvas dimensions |
| 314 | // Limit max width to prevent extremely large canvases in browser, scale proportionally |
| 315 | const MAX_WIDTH = 1000; |
| 316 | let scale = 1; |
| 317 | if (img.width > MAX_WIDTH) { |
| 318 | scale = MAX_WIDTH / img.width; |
| 319 | } |
| 320 | |
| 321 | const finalWidth = img.width * scale; |
| 322 | const finalHeight = img.height * scale; |
| 323 | |
| 324 | canvas.setWidth(finalWidth); |
| 325 | canvas.setHeight(finalHeight); |
| 326 | |
| 327 | // Scale image |
| 328 | img.scale(scale); |
| 329 | |
| 330 | // Set as background image so it acts as the base canvas |
| 331 | canvas.setBackgroundImage(img, canvas.renderAll.bind(canvas)); |
| 332 | baseImage = img; // Store reference for filters |
| 333 | |
| 334 | canvasDimInfo.innerText = `${Math.round(finalWidth)} x ${Math.round(finalHeight)} px`; |
| 335 | |
| 336 | // Trigger fake detection |
| 337 | startDetectionSimulation(); |
| 338 | }); |
| 339 | } |
| 340 | reader.readAsDataURL(file); |
| 341 | }); |
| 342 | |
| 343 | // --- 2. Element / Layer Additions (Canva style) --- |
| 344 | addTextBtn.addEventListener('click', () => { |
| 345 | const text = new fabric.IText('āļāđāļāļāļ§āļēāļĄāđāļŦāļĄāđ', { |
| 346 | left: canvas.width / 2, |
| 347 | top: canvas.height / 2, |
| 348 | originX: 'center', |
| 349 | originY: 'center', |
| 350 | fontFamily: 'sans-serif', |
| 351 | fill: '#333333', |
| 352 | fontSize: 40, |
| 353 | fontWeight: 'bold', |
| 354 | transparentCorners: false, |
| 355 | cornerColor: 'blue', |
| 356 | cornerSize: 10 |
| 357 | }); |
| 358 | canvas.add(text); |
| 359 | canvas.setActiveObject(text); |
| 360 | }); |
| 361 | |
| 362 | addRectBtn.addEventListener('click', () => { |
| 363 | const rect = new fabric.Rect({ |
| 364 | left: canvas.width / 2, |
| 365 | top: canvas.height / 2, |
| 366 | originX: 'center', |
| 367 | originY: 'center', |
| 368 | fill: '#4f46e5', |
| 369 | width: 100, |
| 370 | height: 100, |
| 371 | rx: 10, |
| 372 | ry: 10, |
| 373 | transparentCorners: false, |
| 374 | cornerColor: 'blue', |
| 375 | cornerSize: 10 |
| 376 | }); |
| 377 | canvas.add(rect); |
| 378 | canvas.setActiveObject(rect); |
| 379 | }); |
| 380 | |
| 381 | addCircleBtn.addEventListener('click', () => { |
| 382 | const circle = new fabric.Circle({ |
| 383 | left: canvas.width / 2, |
| 384 | top: canvas.height / 2, |
| 385 | originX: 'center', |
| 386 | originY: 'center', |
| 387 | fill: '#10b981', |
| 388 | radius: 50, |
| 389 | transparentCorners: false, |
| 390 | cornerColor: 'blue', |
| 391 | cornerSize: 10 |
| 392 | }); |
| 393 | canvas.add(circle); |
| 394 | canvas.setActiveObject(circle); |
| 395 | }); |
| 396 | |
| 397 | addLayerImage.addEventListener('change', function(e) { |
| 398 | const file = e.target.files[0]; |
| 399 | if (!file) return; |
| 400 | const reader = new FileReader(); |
| 401 | reader.onload = function(event) { |
| 402 | fabric.Image.fromURL(event.target.result, function(img) { |
| 403 | // scale down if it's too big for the canvas |
| 404 | if(img.width > canvas.width / 2) { |
| 405 | img.scaleToWidth(canvas.width / 2); |
| 406 | } |
| 407 | img.set({ |
| 408 | left: canvas.width / 2, |
| 409 | top: canvas.height / 2, |
| 410 | originX: 'center', |
| 411 | originY: 'center', |
| 412 | transparentCorners: false, |
| 413 | cornerColor: 'blue', |
| 414 | cornerSize: 10 |
| 415 | }); |
| 416 | canvas.add(img); |
| 417 | canvas.setActiveObject(img); |
| 418 | }); |
| 419 | } |
| 420 | reader.readAsDataURL(file); |
| 421 | this.value = ''; // Reset input |
| 422 | }); |
| 423 | |
| 424 | // --- 3. Manage Object State (Enable/Disable tools) --- |
| 425 | function updateObjectControls() { |
| 426 | const activeObj = canvas.getActiveObject(); |
| 427 | if (activeObj) { |
| 428 | // Enable layer tools |
| 429 | objectControls.classList.remove('opacity-50', 'pointer-events-none'); |
| 430 | |
| 431 | // Show/hide text controls |
| 432 | if (activeObj.type === 'i-text' || activeObj.type === 'text') { |
| 433 | textControls.classList.remove('hidden'); |
| 434 | fontFamilySelect.value = activeObj.fontFamily || 'sans-serif'; |
| 435 | fontSizeInput.value = activeObj.fontSize || 40; |
| 436 | |
| 437 | // Update bold button visual state |
| 438 | if (activeObj.fontWeight === 'bold') { |
| 439 | textBoldBtn.classList.replace('bg-white', 'bg-indigo-100'); |
| 440 | textBoldBtn.classList.replace('border-indigo-200', 'border-indigo-400'); |
| 441 | } else { |
| 442 | textBoldBtn.classList.replace('bg-indigo-100', 'bg-white'); |
| 443 | textBoldBtn.classList.replace('border-indigo-400', 'border-indigo-200'); |
| 444 | } |
| 445 | |
| 446 | // Update text color picker |
| 447 | if (activeObj.fill && typeof activeObj.fill === 'string') { |
| 448 | if (activeObj.fill.startsWith('#')) { |
| 449 | textColorPicker.value = activeObj.fill.substring(0, 7); |
| 450 | } else if (activeObj.fill.startsWith('rgb')) { |
| 451 | const rgb = activeObj.fill.match(/\d+/g); |
| 452 | if(rgb && rgb.length >= 3) { |
| 453 | const hex = "#" + (1 << 24 | rgb[0] << 16 | rgb[1] << 8 | rgb[2]).toString(16).slice(1); |
| 454 | textColorPicker.value = hex; |
| 455 | } |
| 456 | } |
| 457 | } |
| 458 | } else { |
| 459 | textControls.classList.add('hidden'); |
| 460 | } |
| 461 | |
| 462 | // Update filter sliders if an image is selected |
| 463 | if (activeObj.type === 'image') { |
| 464 | updateFilterSlidersFromObject(activeObj); |
| 465 | objColorPicker.classList.add('hidden'); |
| 466 | } else { |
| 467 | // Show color picker for text and shapes |
| 468 | objColorPicker.classList.remove('hidden'); |
| 469 | if (activeObj.fill && typeof activeObj.fill === 'string') { |
| 470 | if (activeObj.fill.startsWith('#')) { |
| 471 | objColorPicker.value = activeObj.fill.substring(0, 7); |
| 472 | } else if (activeObj.fill.startsWith('rgb')) { |
| 473 | const rgb = activeObj.fill.match(/\d+/g); |
| 474 | if(rgb && rgb.length >= 3) { |
| 475 | const hex = "#" + (1 << 24 | rgb[0] << 16 | rgb[1] << 8 | rgb[2]).toString(16).slice(1); |
| 476 | objColorPicker.value = hex; |
| 477 | } |
| 478 | } |
| 479 | } |
| 480 | } |
| 481 | } else { |
| 482 | // Disable layer tools |
| 483 | objectControls.classList.add('opacity-50', 'pointer-events-none'); |
| 484 | objColorPicker.classList.add('hidden'); |
| 485 | textControls.classList.add('hidden'); |
| 486 | |
| 487 | // If nothing selected, show filters of the background image |
| 488 | if (baseImage) { |
| 489 | updateFilterSlidersFromObject(baseImage); |
| 490 | } |
| 491 | } |
| 492 | } |
| 493 | |
| 494 | canvas.on('selection:created', updateObjectControls); |
| 495 | canvas.on('selection:updated', updateObjectControls); |
| 496 | canvas.on('selection:cleared', updateObjectControls); |
| 497 | |
| 498 | objColorPicker.addEventListener('input', (e) => { |
| 499 | const activeObj = canvas.getActiveObject(); |
| 500 | if (activeObj) { |
| 501 | activeObj.set('fill', e.target.value); |
| 502 | canvas.renderAll(); |
| 503 | } |
| 504 | }); |
| 505 | |
| 506 | deleteObjBtn.addEventListener('click', () => { |
| 507 | const activeObjects = canvas.getActiveObjects(); |
| 508 | if (activeObjects.length) { |
| 509 | activeObjects.forEach(obj => canvas.remove(obj)); |
| 510 | canvas.discardActiveObject(); |
| 511 | } |
| 512 | }); |
| 513 | |
| 514 | fontFamilySelect.addEventListener('change', (e) => { |
| 515 | const activeObj = canvas.getActiveObject(); |
| 516 | if (activeObj && (activeObj.type === 'i-text' || activeObj.type === 'text')) { |
| 517 | activeObj.set('fontFamily', e.target.value); |
| 518 | canvas.renderAll(); |
| 519 | } |
| 520 | }); |
| 521 | |
| 522 | fontSizeInput.addEventListener('input', (e) => { |
| 523 | const activeObj = canvas.getActiveObject(); |
| 524 | if (activeObj && (activeObj.type === 'i-text' || activeObj.type === 'text')) { |
| 525 | const size = parseInt(e.target.value, 10); |
| 526 | if (!isNaN(size) && size > 0) { |
| 527 | activeObj.set('fontSize', size); |
| 528 | canvas.renderAll(); |
| 529 | } |
| 530 | } |
| 531 | }); |
| 532 | |
| 533 | textColorPicker.addEventListener('input', (e) => { |
| 534 | const activeObj = canvas.getActiveObject(); |
| 535 | if (activeObj && (activeObj.type === 'i-text' || activeObj.type === 'text')) { |
| 536 | activeObj.set('fill', e.target.value); |
| 537 | canvas.renderAll(); |
| 538 | // āļāļąāļāđāļāļāļāļĨāđāļāļāļŠāļĩāļŦāļĨāļąāļāđāļŦāđāļāļĢāļāļāļąāļāļāđāļ§āļĒ |
| 539 | objColorPicker.value = e.target.value; |
| 540 | } |
| 541 | }); |
| 542 | |
| 543 | textBoldBtn.addEventListener('click', () => { |
| 544 | const activeObj = canvas.getActiveObject(); |
| 545 | if (activeObj && (activeObj.type === 'i-text' || activeObj.type === 'text')) { |
| 546 | const isBold = activeObj.fontWeight === 'bold'; |
| 547 | activeObj.set('fontWeight', isBold ? 'normal' : 'bold'); |
| 548 | canvas.renderAll(); |
| 549 | updateObjectControls(); // āļāļąāļāđāļāļāļŠāļāļēāļāļ°āļāļļāđāļĄāļāļąāļ§āļŦāļāļēāļāļąāļāļāļĩ |
| 550 | } |
| 551 | }); |
| 552 | |
| 553 | bringForwardBtn.addEventListener('click', () => { |
| 554 | const activeObj = canvas.getActiveObject(); |
| 555 | if (activeObj) canvas.bringForward(activeObj); |
| 556 | }); |
| 557 | |
| 558 | sendBackwardBtn.addEventListener('click', () => { |
| 559 | const activeObj = canvas.getActiveObject(); |
| 560 | if (activeObj) canvas.sendBackwards(activeObj); |
| 561 | }); |
| 562 | |
| 563 | // --- 3.5 Magic Text Editor (Masking Old Text & Replace) --- |
| 564 | magicTextBtn.addEventListener('click', () => { |
| 565 | isMagicTextMode = true; |
| 566 | canvas.isDrawingMode = false; |
| 567 | canvas.defaultCursor = 'crosshair'; |
| 568 | canvas.discardActiveObject(); |
| 569 | canvas.renderAll(); |
| 570 | |
| 571 | // Highlight button to show it's active |
| 572 | magicTextBtn.classList.replace('from-purple-500', 'from-pink-500'); |
| 573 | magicTextBtn.classList.replace('to-indigo-500', 'to-rose-500'); |
| 574 | magicTextBtn.innerHTML = '<i class="fa-solid fa-check mr-1"></i> āļĨāļēāļāļāļĨāļļāļĄāļāđāļāļāļ§āļēāļĄāļāļāļ āļēāļāđāļĨāļĒ...'; |
| 575 | }); |
| 576 | |
| 577 | canvas.on('mouse:down', function(o) { |
| 578 | if (!isMagicTextMode) return; |
| 579 | var pointer = canvas.getPointer(o.e); |
| 580 | origX = pointer.x; |
| 581 | origY = pointer.y; |
| 582 | |
| 583 | magicRect = new fabric.Rect({ |
| 584 | left: origX, |
| 585 | top: origY, |
| 586 | width: 0, |
| 587 | height: 0, |
| 588 | fill: 'rgba(236, 72, 153, 0.3)', // Pinkish selection box |
| 589 | stroke: '#ec4899', |
| 590 | strokeWidth: 2, |
| 591 | selectable: false |
| 592 | }); |
| 593 | canvas.add(magicRect); |
| 594 | }); |
| 595 | |
| 596 | canvas.on('mouse:move', function(o) { |
| 597 | if (!isMagicTextMode || !magicRect) return; |
| 598 | var pointer = canvas.getPointer(o.e); |
| 599 | |
| 600 | if (origX > pointer.x) { |
| 601 | magicRect.set({ left: Math.abs(pointer.x) }); |
| 602 | } |
| 603 | if (origY > pointer.y) { |
| 604 | magicRect.set({ top: Math.abs(pointer.y) }); |
| 605 | } |
| 606 | |
| 607 | magicRect.set({ width: Math.abs(origX - pointer.x) }); |
| 608 | magicRect.set({ height: Math.abs(origY - pointer.y) }); |
| 609 | canvas.renderAll(); |
| 610 | }); |
| 611 | |
| 612 | canvas.on('mouse:up', function(o) { |
| 613 | if (!isMagicTextMode || !magicRect) return; |
| 614 | |
| 615 | // Ensure selection is big enough |
| 616 | if (magicRect.width < 10 || magicRect.height < 10) { |
| 617 | canvas.remove(magicRect); |
| 618 | resetMagicMode(); |
| 619 | return; |
| 620 | } |
| 621 | |
| 622 | // 1. Hide selection box temporarily to read actual background pixels |
| 623 | magicRect.set('visible', false); |
| 624 | canvas.renderAll(); |
| 625 | |
| 626 | // 2. Read background color from the Top-Left of the selection |
| 627 | const ctx = canvas.getContext('2d'); |
| 628 | const multiplier = canvas.getRetinaScaling(); |
| 629 | const pixel = ctx.getImageData(magicRect.left * multiplier, magicRect.top * multiplier, 1, 1).data; |
| 630 | const bgColor = `rgb(${pixel[0]}, ${pixel[1]}, ${pixel[2]})`; |
| 631 | |
| 632 | // 3. Make the box visible again, filled with background color to "Erase" old text |
| 633 | magicRect.set({ |
| 634 | visible: true, |
| 635 | fill: bgColor, |
| 636 | strokeWidth: 0, |
| 637 | selectable: true // allow user to resize mask later if needed |
| 638 | }); |
| 639 | |
| 640 | // 4. Create new editable text over the erased area |
| 641 | const text = new fabric.IText('āļāđāļāļāļ§āļēāļĄāđāļŦāļĄāđ', { |
| 642 | left: magicRect.left + (magicRect.width * 0.05), |
| 643 | top: magicRect.top + (magicRect.height * 0.1), |
| 644 | fontFamily: 'sans-serif', |
| 645 | fill: '#333333', // Default text color |
| 646 | fontSize: Math.max(16, magicRect.height * 0.6), |
| 647 | fontWeight: 'bold', |
| 648 | transparentCorners: false, |
| 649 | cornerColor: 'blue', |
| 650 | cornerSize: 10 |
| 651 | }); |
| 652 | canvas.add(text); |
| 653 | |
| 654 | // 5. Select the new text and open edit mode automatically |
| 655 | canvas.setActiveObject(text); |
| 656 | text.enterEditing(); |
| 657 | text.selectAll(); |
| 658 | |
| 659 | resetMagicMode(); |
| 660 | }); |
| 661 | |
| 662 | function resetMagicMode() { |
| 663 | isMagicTextMode = false; |
| 664 | magicRect = null; |
| 665 | canvas.defaultCursor = 'default'; |
| 666 | magicTextBtn.classList.replace('from-pink-500', 'from-purple-500'); |
| 667 | magicTextBtn.classList.replace('to-rose-500', 'to-indigo-500'); |
| 668 | magicTextBtn.innerHTML = '<i class="fa-solid fa-wand-magic-sparkles mr-1"></i> āđāļāđāđāļāļāđāļāļāļ§āļēāļĄāđāļāļīāļĄāļāļāļ āļēāļ (Magic Edit)'; |
| 669 | canvas.renderAll(); |
| 670 | } |
| 671 | |
| 672 | // --- 4. Filters (Fabric Image Filters) --- |
| 673 | // Range slider value is -100 to 100. Fabric expects -1 to 1. |
| 674 | function applyImageFilters() { |
| 675 | // Apply to active image object, or background baseImage if none selected |
| 676 | const target = canvas.getActiveObject() && canvas.getActiveObject().type === 'image' |
| 677 | ? canvas.getActiveObject() |
| 678 | : baseImage; |
| 679 | |
| 680 | if (!target) return; |
| 681 | |
| 682 | document.getElementById('valBright').innerText = filterBright.value; |
| 683 | document.getElementById('valContrast').innerText = filterContrast.value; |
| 684 | document.getElementById('valSaturate').innerText = filterSaturate.value; |
| 685 | |
| 686 | // Fabric 5 filter API |
| 687 | target.filters[0] = new fabric.Image.filters.Brightness({ brightness: parseFloat(filterBright.value) / 100 }); |
| 688 | target.filters[1] = new fabric.Image.filters.Contrast({ contrast: parseFloat(filterContrast.value) / 100 }); |
| 689 | target.filters[2] = new fabric.Image.filters.Saturation({ saturation: parseFloat(filterSaturate.value) / 100 }); |
| 690 | |
| 691 | target.applyFilters(); |
| 692 | canvas.renderAll(); |
| 693 | } |
| 694 | |
| 695 | function updateFilterSlidersFromObject(obj) { |
| 696 | if (!obj.filters || obj.filters.length === 0) { |
| 697 | filterBright.value = 0; filterContrast.value = 0; filterSaturate.value = 0; |
| 698 | } else { |
| 699 | filterBright.value = (obj.filters[0] && obj.filters[0].brightness) ? Math.round(obj.filters[0].brightness * 100) : 0; |
| 700 | filterContrast.value = (obj.filters[1] && obj.filters[1].contrast) ? Math.round(obj.filters[1].contrast * 100) : 0; |
| 701 | filterSaturate.value = (obj.filters[2] && obj.filters[2].saturation) ? Math.round(obj.filters[2].saturation * 100) : 0; |
| 702 | } |
| 703 | document.getElementById('valBright').innerText = filterBright.value; |
| 704 | document.getElementById('valContrast').innerText = filterContrast.value; |
| 705 | document.getElementById('valSaturate').innerText = filterSaturate.value; |
| 706 | } |
| 707 | |
| 708 | filterBright.addEventListener('input', applyImageFilters); |
| 709 | filterContrast.addEventListener('input', applyImageFilters); |
| 710 | filterSaturate.addEventListener('input', applyImageFilters); |
| 711 | |
| 712 | resetFiltersBtn.addEventListener('click', () => { |
| 713 | filterBright.value = 0; filterContrast.value = 0; filterSaturate.value = 0; |
| 714 | applyImageFilters(); |
| 715 | }); |
| 716 | |
| 717 | // --- 5. Free Drawing --- |
| 718 | // Setup brush |
| 719 | canvas.freeDrawingBrush = new fabric.PencilBrush(canvas); |
| 720 | canvas.freeDrawingBrush.color = brushColor.value; |
| 721 | canvas.freeDrawingBrush.width = parseInt(brushSize.value, 10) || 5; |
| 722 | |
| 723 | brushColor.addEventListener('change', (e) => { |
| 724 | canvas.freeDrawingBrush.color = e.target.value; |
| 725 | }); |
| 726 | |
| 727 | brushSize.addEventListener('input', (e) => { |
| 728 | canvas.freeDrawingBrush.width = parseInt(e.target.value, 10); |
| 729 | }); |
| 730 | |
| 731 | toggleDrawBtn.addEventListener('click', () => { |
| 732 | canvas.isDrawingMode = !canvas.isDrawingMode; |
| 733 | if (canvas.isDrawingMode) { |
| 734 | toggleDrawBtn.classList.replace('bg-slate-100', 'bg-indigo-600'); |
| 735 | toggleDrawBtn.classList.replace('text-slate-700', 'text-white'); |
| 736 | canvas.discardActiveObject(); |
| 737 | canvas.requestRenderAll(); |
| 738 | } else { |
| 739 | toggleDrawBtn.classList.replace('bg-indigo-600', 'bg-slate-100'); |
| 740 | toggleDrawBtn.classList.replace('text-white', 'text-slate-700'); |
| 741 | } |
| 742 | }); |
| 743 | |
| 744 | // --- 6. Download --- |
| 745 | downloadBtn.addEventListener('click', () => { |
| 746 | // Unselect everything before export so bounding boxes aren't visible |
| 747 | canvas.discardActiveObject(); |
| 748 | canvas.renderAll(); |
| 749 | |
| 750 | const link = document.createElement('a'); |
| 751 | link.download = 'ai-canvas-edited.png'; |
| 752 | link.href = canvas.toDataURL({ format: 'png', quality: 1 }); |
| 753 | link.click(); |
| 754 | }); |
| 755 | |
| 756 | // --- 7. Detection Simulation --- |
| 757 | const startEditBtn = document.getElementById('startEditBtn'); |
| 758 | startEditBtn.addEventListener('click', () => { |
| 759 | detectionPanel.classList.add('hidden'); |
| 760 | editorPanel.classList.remove('hidden'); |
| 761 | }); |
| 762 | |
| 763 | function startDetectionSimulation() { |
| 764 | editorPanel.classList.add('hidden'); |
| 765 | detectionPanel.classList.remove('hidden'); |
| 766 | document.getElementById('detectionLoading').classList.remove('hidden'); |
| 767 | document.getElementById('detectionResult').classList.add('hidden'); |
| 768 | |
| 769 | const loadingBar = document.getElementById('loadingBar'); |
| 770 | const loadingText = document.getElementById('loadingText'); |
| 771 | loadingBar.style.width = '0%'; |
| 772 | |
| 773 | setTimeout(() => { loadingBar.style.width = '30%'; loadingText.innerText = 'āļāļĢāļ§āļāļŠāļāļ Noise Pattern...'; }, 800); |
| 774 | setTimeout(() => { loadingBar.style.width = '60%'; loadingText.innerText = 'āļ§āļīāđāļāļĢāļēāļ°āļŦāđāļāļāļāđāļāļāđāļĨāđāļĒāļāļĢāđ (Edge Analysis)...'; }, 1600); |
| 775 | setTimeout(() => { loadingBar.style.width = '90%'; loadingText.innerText = 'āļāļĢāļ°āļĄāļ§āļĨāļāļĨāļāļĨāļĨāļąāļāļāđ...'; }, 2400); |
| 776 | |
| 777 | setTimeout(() => { |
| 778 | showDetectionResult(); |
| 779 | }, 3000); |
| 780 | } |
| 781 | |
| 782 | function showDetectionResult() { |
| 783 | document.getElementById('detectionLoading').classList.add('hidden'); |
| 784 | document.getElementById('detectionResult').classList.remove('hidden'); |
| 785 | |
| 786 | const isLikelyAI = Math.random() > 0.4; |
| 787 | const resultIcon = document.getElementById('resultIcon'); |
| 788 | const resultTitle = document.getElementById('resultTitle'); |
| 789 | const resultDesc = document.getElementById('resultDesc'); |
| 790 | const scoreAi = document.getElementById('scoreAi'); |
| 791 | const scoreNoise = document.getElementById('scoreNoise'); |
| 792 | |
| 793 | if (isLikelyAI) { |
| 794 | resultIcon.innerHTML = '<i class="fa-solid fa-robot text-orange-500"></i>'; |
| 795 | resultTitle.innerText = 'āđāļāļāļēāļŠāļŠāļđāļāļāļĩāđāđāļāđāļāļ āļēāļāļŠāļĢāđāļēāļāļāļēāļ AI'; |
| 796 | resultTitle.className = 'text-lg font-bold mb-1 text-orange-600'; |
| 797 | resultDesc.innerText = 'āļāļāļĢāđāļāļāļĢāļāļĒāļāļēāļĢāļāļĢāļ°āļāļēāļĒāļāļīāļāđāļāļĨāļāļīāļāļāļāļāļī (Generative Artifacts)'; |
| 798 | scoreAi.innerText = (85 + Math.floor(Math.random() * 14)) + '%'; |
| 799 | scoreAi.className = 'font-semibold text-orange-600'; |
| 800 | scoreNoise.innerText = 'āļŠāļąāļāđāļāļĢāļēāļ°āļŦāđ (Synthetic)'; |
| 801 | } else { |
| 802 | resultIcon.innerHTML = '<i class="fa-solid fa-camera text-emerald-500"></i>'; |
| 803 | resultTitle.innerText = 'āļāđāļēāļāļ°āđāļāđāļāļ āļēāļāļāđāļēāļĒāļāļĢāļīāļ'; |
| 804 | resultTitle.className = 'text-lg font-bold mb-1 text-emerald-600'; |
| 805 | resultDesc.innerText = 'āđāļĄāđāļāļāļĢāđāļāļāļĢāļāļĒāļāļēāļĢāļŠāļąāļāđāļāļĢāļēāļ°āļŦāđāļāļāļ AI āđāļāļĢāļ°āļāļąāļāļāļĩāđāļāļīāļāļŠāļąāļāđāļāļ'; |
| 806 | scoreAi.innerText = (2 + Math.floor(Math.random() * 20)) + '%'; |
| 807 | scoreAi.className = 'font-semibold text-emerald-600'; |
| 808 | scoreNoise.innerText = 'āļāļāļāļī (Natural)'; |
| 809 | } |
| 810 | } |
| 811 | </script> |
| 812 | </body> |
| 813 | </html> |
Copied to clipboard!
Report This Paste
\n \n \n \n \n\n \n
\n\n \n\n\n AI Image Detector & Editor\n
\nāļāļĢāļ§āļāļŠāļāļāļ āļēāļ AI āđāļĨāļ°āļĢāļ°āļāļāđāļāđāđāļāļ āļēāļāđāļāļāđāļĨāđāļĒāļāļĢāđ (Canva Style)
\n\n \n \n
\n \n \n \n
\n\n \n \n
\n\n \n 1. āļ āļēāļāļŦāļĨāļąāļ (Background)
\n \n\n
\n\n \n 2. āļāļĨāļāļēāļĢāļ§āļīāđāļāļĢāļēāļ°āļŦāđāļ āļēāļ
\n \n\n \n
\n\n āļāļģāļĨāļąāļāļāļĢāļ§āļāļŠāļāļ Metadata...
\n\n \n
\n \n \n \n \n \n
\n \n
\n \n \n āđāļāļāļēāļŠāđāļāđāļāļ āļēāļ AI:
\n Noise Pattern:
\n \n
\n\n \n
\n\n \n
\n 3. āđāļāļĢāļ·āđāļāļāļĄāļ·āļāđāļāđāđāļ (Canva Style)
\n \n \n\n \n
\n\n \n \n \n \n \n \n
\n \n \n \n \n
\n āļāļĨāļīāļāļāļļāđāļĄ āđāļĨāđāļ§āļĨāļēāļāļāļĨāļļāļĄāļāđāļāļāļ§āļēāļĄāđāļāļīāļĄ āđāļāļ·āđāļāļĨāļāđāļĨāļ°āļāļīāļĄāļāđāđāļŦāļĄāđ
\n\n
\n\n \n \n \n
\n \n \n \n \n
\n \n \n \n \n
\n \n
\n \n \n \n
\n \n \n \n
\n \n \n
\n \n \n \n
\n \n\n \n
\n \n
\n\n \n \n \n
\n \n \n \n
\n \n \n \n
\n \n \n\n \n
\n \n
\n\n \n \n \n \n
\n \n \n \n
\n \n \n
\n \n \n āļāļąāļāđāļāļīāđāļĨāļāļĨāļīāļāļāļĩāđāļāđāļāļāļ§āļēāļĄāđāļāļ·āđāļāđāļāđāđāļ / āļĨāļēāļāđāļāļ·āđāļāļĒāđāļēāļĒāļāļģāđāļŦāļāđāļ\n \n
\n \n \n \n \n
\n \n \n
\n\n \n \n āļāļąāļāđāļŦāļĨāļāļ āļēāļāļŦāļĨāļąāļāđāļāļ·āđāļāđāļĢāļīāđāļĄāļŠāļĢāđāļēāļāļāļīāđāļāļāļēāļ
\n