C50BARZ commited on
Commit
ba607be
·
verified ·
1 Parent(s): a4acbdc

Add 2 files

Browse files
Files changed (2) hide show
  1. README.md +7 -5
  2. index.html +1414 -19
README.md CHANGED
@@ -1,10 +1,12 @@
1
  ---
2
- title: Clean Paste 10
3
- emoji: 🐠
4
- colorFrom: gray
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: clean-paste-10
3
+ emoji: 🐳
4
+ colorFrom: pink
5
+ colorTo: purple
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,1414 @@
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>Clean Paste - Remove and Format Text</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <script>
9
+ tailwind.config = {
10
+ darkMode: 'class',
11
+ theme: {
12
+ extend: {
13
+ animation: {
14
+ 'fade-in': 'fadeIn 0.3s ease-in-out',
15
+ 'fade-out': 'fadeOut 0.3s ease-in-out',
16
+ 'pulse-slow': 'pulse 3s infinite',
17
+ },
18
+ keyframes: {
19
+ fadeIn: {
20
+ '0%': { opacity: '0' },
21
+ '100%': { opacity: '1' },
22
+ },
23
+ fadeOut: {
24
+ '0%': { opacity: '1' },
25
+ '100%': { opacity: '0' },
26
+ }
27
+ }
28
+ }
29
+ }
30
+ }
31
+ </script>
32
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
33
+ <style>
34
+ .text-area-container {
35
+ position: relative;
36
+ transition: all 0.3s ease;
37
+ }
38
+ .text-area-container:focus-within {
39
+ box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.5);
40
+ }
41
+ .paste-btn {
42
+ transition: all 0.2s ease;
43
+ }
44
+ .paste-btn:hover {
45
+ transform: translateY(-1px);
46
+ }
47
+ .copy-btn {
48
+ transition: all 0.2s ease;
49
+ }
50
+ .copy-btn:hover {
51
+ transform: translateY(-1px);
52
+ }
53
+ .format-btn {
54
+ transition: all 0.2s ease;
55
+ }
56
+ .format-btn:hover {
57
+ transform: translateY(-1px);
58
+ }
59
+ .character-count {
60
+ font-variant-numeric: tabular-nums;
61
+ }
62
+ .tooltip {
63
+ position: absolute;
64
+ top: -40px;
65
+ left: 50%;
66
+ transform: translateX(-50%);
67
+ background-color: #333;
68
+ color: white;
69
+ padding: 5px 10px;
70
+ border-radius: 4px;
71
+ font-size: 12px;
72
+ opacity: 0;
73
+ transition: opacity 0.3s;
74
+ pointer-events: none;
75
+ white-space: nowrap;
76
+ }
77
+ .tooltip:after {
78
+ content: "";
79
+ position: absolute;
80
+ top: 100%;
81
+ left: 50%;
82
+ margin-left: -5px;
83
+ border-width: 5px;
84
+ border-style: solid;
85
+ border-color: #333 transparent transparent transparent;
86
+ }
87
+ .show-tooltip {
88
+ opacity: 1;
89
+ }
90
+ .active-format {
91
+ background-color: #3b82f6 !important;
92
+ color: white !important;
93
+ }
94
+ .dark .text-area-container {
95
+ background-color: #1e293b;
96
+ border-color: #334155;
97
+ }
98
+ .dark .text-area-container textarea {
99
+ background-color: #1e293b;
100
+ border-color: #334155;
101
+ color: #f8fafc;
102
+ }
103
+ .dark .text-area-container textarea::placeholder {
104
+ color: #64748b;
105
+ }
106
+ .dark .format-btn {
107
+ background-color: #334155;
108
+ color: #e2e8f0;
109
+ }
110
+ .dark .format-btn:hover {
111
+ background-color: #475569;
112
+ }
113
+ .dark .bg-gray-50 {
114
+ background-color: #1e293b;
115
+ }
116
+ .dark .bg-white {
117
+ background-color: #0f172a;
118
+ }
119
+ .dark .text-gray-600 {
120
+ color: #94a3b8;
121
+ }
122
+ .dark .text-gray-500 {
123
+ color: #94a3b8;
124
+ }
125
+ .dark .text-gray-700 {
126
+ color: #e2e8f0;
127
+ }
128
+ .dark .border-gray-200 {
129
+ border-color: #334155;
130
+ }
131
+ .code-btn {
132
+ background-color: #f59e0b;
133
+ color: white;
134
+ }
135
+ .code-btn:hover {
136
+ background-color: #d97706;
137
+ }
138
+ .dark .code-btn {
139
+ background-color: #92400e;
140
+ }
141
+ .dark .code-btn:hover {
142
+ background-color: #7c2d12;
143
+ }
144
+ .analysis-panel {
145
+ max-height: 0;
146
+ overflow: hidden;
147
+ transition: max-height 0.3s ease-out;
148
+ }
149
+ .analysis-panel.open {
150
+ max-height: 300px;
151
+ }
152
+ .word-frequency-item {
153
+ transition: all 0.2s ease;
154
+ }
155
+ .word-frequency-item:hover {
156
+ transform: translateX(3px);
157
+ }
158
+ .find-replace-panel {
159
+ max-height: 0;
160
+ overflow: hidden;
161
+ transition: max-height 0.3s ease-out;
162
+ }
163
+ .find-replace-panel.open {
164
+ max-height: 200px;
165
+ }
166
+ .highlight {
167
+ background-color: rgba(255, 255, 0, 0.4);
168
+ }
169
+ .current-highlight {
170
+ background-color: rgba(255, 165, 0, 0.6);
171
+ animation: pulse 1.5s infinite;
172
+ }
173
+ @keyframes pulse {
174
+ 0% { background-color: rgba(255, 165, 0, 0.6); }
175
+ 50% { background-color: rgba(255, 165, 0, 0.3); }
176
+ 100% { background-color: rgba(255, 165, 0, 0.6); }
177
+ }
178
+ .speech-controls {
179
+ max-height: 0;
180
+ overflow: hidden;
181
+ transition: max-height 0.3s ease-out;
182
+ }
183
+ .speech-controls.open {
184
+ max-height: 120px;
185
+ }
186
+ .voice-active {
187
+ animation: pulse-slow 2s infinite;
188
+ }
189
+ .progress-bar {
190
+ height: 4px;
191
+ background-color: #e5e7eb;
192
+ border-radius: 2px;
193
+ overflow: hidden;
194
+ }
195
+ .progress-bar-fill {
196
+ height: 100%;
197
+ background-color: #3b82f6;
198
+ transition: width 0.1s linear;
199
+ }
200
+ </style>
201
+ </head>
202
+ <body class="bg-gray-100 dark:bg-gray-900 min-h-screen flex flex-col items-center justify-center p-4 transition-colors duration-200">
203
+ <div class="w-full max-w-3xl bg-white dark:bg-slate-800 rounded-xl shadow-lg overflow-hidden transition-colors duration-200">
204
+ <div class="bg-blue-600 p-4">
205
+ <div class="flex items-center justify-between">
206
+ <div class="flex items-center space-x-2">
207
+ <i class="fas fa-broom text-white text-2xl"></i>
208
+ <h1 class="text-white text-2xl font-bold">Clean Paste</h1>
209
+ </div>
210
+ <div class="flex items-center space-x-4">
211
+ <span class="text-blue-100 text-sm hidden md:block">Paste → Clean → Format → Copy</span>
212
+ <button id="theme-toggle" class="text-blue-100 hover:text-white focus:outline-none">
213
+ <i class="fas fa-moon" id="theme-icon"></i>
214
+ </button>
215
+ </div>
216
+ </div>
217
+ <p class="text-blue-100 mt-1 text-sm">Remove formatting and apply new styles to your text</p>
218
+ </div>
219
+
220
+ <div class="p-6">
221
+ <div class="text-area-container bg-gray-50 dark:bg-slate-700 rounded-lg border border-gray-200 dark:border-slate-600 p-4">
222
+ <div class="flex justify-between items-center mb-2">
223
+ <label for="clean-text" class="text-gray-600 dark:text-gray-300 font-medium">Paste your formatted text here:</label>
224
+ <div class="flex items-center space-x-2">
225
+ <span class="text-xs text-gray-500 dark:text-gray-400 character-count">0 characters</span>
226
+ <button id="paste-btn" class="paste-btn bg-blue-100 hover:bg-blue-200 dark:bg-blue-900 dark:hover:bg-blue-800 text-blue-700 dark:text-blue-200 px-3 py-1 rounded-md text-sm flex items-center">
227
+ <i class="fas fa-paste mr-1"></i> Paste
228
+ </button>
229
+ </div>
230
+ </div>
231
+ <textarea
232
+ id="clean-text"
233
+ class="w-full h-64 p-3 bg-white dark:bg-slate-700 border border-gray-300 dark:border-slate-600 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent resize-none dark:text-white"
234
+ placeholder="Paste your formatted text here (or click the Paste button above)..."
235
+ spellcheck="false"
236
+ ></textarea>
237
+ </div>
238
+
239
+ <!-- Find & Replace Panel -->
240
+ <div id="find-replace-panel" class="find-replace-panel bg-gray-50 dark:bg-slate-700 rounded-lg mt-4 p-4 border border-gray-200 dark:border-slate-600">
241
+ <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
242
+ <div>
243
+ <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Find:</label>
244
+ <div class="flex">
245
+ <input type="text" id="find-input" class="flex-1 p-2 border border-gray-300 dark:border-slate-600 rounded-l-md dark:bg-slate-600 dark:text-white focus:outline-none focus:ring-2 focus:ring-blue-500">
246
+ <button id="find-prev-btn" class="bg-gray-200 dark:bg-slate-600 px-3 border-t border-b border-gray-300 dark:border-slate-600">
247
+ <i class="fas fa-chevron-up"></i>
248
+ </button>
249
+ <button id="find-next-btn" class="bg-gray-200 dark:bg-slate-600 px-3 border border-gray-300 dark:border-slate-600 rounded-r-md">
250
+ <i class="fas fa-chevron-down"></i>
251
+ </button>
252
+ </div>
253
+ </div>
254
+ <div>
255
+ <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Replace with:</label>
256
+ <div class="flex">
257
+ <input type="text" id="replace-input" class="flex-1 p-2 border border-gray-300 dark:border-slate-600 rounded-l-md dark:bg-slate-600 dark:text-white focus:outline-none focus:ring-2 focus:ring-blue-500">
258
+ <button id="replace-btn" class="bg-blue-500 text-white px-3 border border-blue-500 rounded-r-md hover:bg-blue-600">
259
+ Replace
260
+ </button>
261
+ </div>
262
+ </div>
263
+ </div>
264
+ <div class="flex justify-between items-center mt-3">
265
+ <div class="flex items-center space-x-2">
266
+ <input type="checkbox" id="case-sensitive" class="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded dark:bg-slate-600 dark:border-slate-500">
267
+ <label for="case-sensitive" class="text-sm text-gray-700 dark:text-gray-300">Case sensitive</label>
268
+ </div>
269
+ <div class="text-sm text-gray-500 dark:text-gray-400">
270
+ <span id="find-results">0 matches</span>
271
+ </div>
272
+ </div>
273
+ </div>
274
+
275
+ <!-- Text to Speech Panel -->
276
+ <div id="speech-panel" class="speech-controls bg-gray-50 dark:bg-slate-700 rounded-lg mt-4 p-4 border border-gray-200 dark:border-slate-600">
277
+ <div class="flex flex-col md:flex-row md:items-center md:justify-between gap-4">
278
+ <div class="flex-1">
279
+ <div class="flex items-center space-x-3 mb-2">
280
+ <label class="text-sm font-medium text-gray-700 dark:text-gray-300">Voice:</label>
281
+ <select id="voice-select" class="flex-1 p-2 border border-gray-300 dark:border-slate-600 rounded-md dark:bg-slate-600 dark:text-white focus:outline-none focus:ring-2 focus:ring-blue-500 text-sm">
282
+ <option value="">Loading voices...</option>
283
+ </select>
284
+ </div>
285
+ <div class="grid grid-cols-2 gap-3">
286
+ <div>
287
+ <label class="text-sm font-medium text-gray-700 dark:text-gray-300 block mb-1">Rate:</label>
288
+ <input type="range" id="rate-control" min="0.5" max="2" step="0.1" value="1" class="w-full">
289
+ <div class="text-xs text-gray-500 dark:text-gray-400 text-center" id="rate-value">1.0</div>
290
+ </div>
291
+ <div>
292
+ <label class="text-sm font-medium text-gray-700 dark:text-gray-300 block mb-1">Pitch:</label>
293
+ <input type="range" id="pitch-control" min="0.5" max="2" step="0.1" value="1" class="w-full">
294
+ <div class="text-xs text-gray-500 dark:text-gray-400 text-center" id="pitch-value">1.0</div>
295
+ </div>
296
+ </div>
297
+ </div>
298
+ <div class="flex flex-col items-center justify-center space-y-2">
299
+ <div class="flex space-x-3">
300
+ <button id="speak-btn" class="bg-green-500 hover:bg-green-600 text-white px-4 py-2 rounded-full flex items-center">
301
+ <i class="fas fa-play mr-2"></i> Speak
302
+ </button>
303
+ <button id="pause-btn" class="bg-yellow-500 hover:bg-yellow-600 text-white px-4 py-2 rounded-full flex items-center">
304
+ <i class="fas fa-pause mr-2"></i> Pause
305
+ </button>
306
+ <button id="stop-btn" class="bg-red-500 hover:bg-red-600 text-white px-4 py-2 rounded-full flex items-center">
307
+ <i class="fas fa-stop mr-2"></i> Stop
308
+ </button>
309
+ </div>
310
+ <div class="progress-bar w-full max-w-xs">
311
+ <div id="progress-bar-fill" class="progress-bar-fill" style="width: 0%"></div>
312
+ </div>
313
+ </div>
314
+ </div>
315
+ </div>
316
+
317
+ <!-- Text Analysis Panel -->
318
+ <div id="analysis-panel" class="analysis-panel bg-gray-50 dark:bg-slate-700 rounded-lg mt-4 p-4 border border-gray-200 dark:border-slate-600 overflow-y-auto">
319
+ <h3 class="font-medium text-gray-700 dark:text-gray-300 mb-3">Text Analysis</h3>
320
+ <div class="grid grid-cols-2 md:grid-cols-4 gap-4 mb-4">
321
+ <div class="bg-white dark:bg-slate-600 p-3 rounded-lg shadow">
322
+ <div class="text-sm text-gray-500 dark:text-gray-400">Characters</div>
323
+ <div class="text-xl font-bold text-gray-800 dark:text-white" id="analysis-chars">0</div>
324
+ </div>
325
+ <div class="bg-white dark:bg-slate-600 p-3 rounded-lg shadow">
326
+ <div class="text-sm text-gray-500 dark:text-gray-400">Words</div>
327
+ <div class="text-xl font-bold text-gray-800 dark:text-white" id="analysis-words">0</div>
328
+ </div>
329
+ <div class="bg-white dark:bg-slate-600 p-3 rounded-lg shadow">
330
+ <div class="text-sm text-gray-500 dark:text-gray-400">Sentences</div>
331
+ <div class="text-xl font-bold text-gray-800 dark:text-white" id="analysis-sentences">0</div>
332
+ </div>
333
+ <div class="bg-white dark:bg-slate-600 p-3 rounded-lg shadow">
334
+ <div class="text-sm text-gray-500 dark:text-gray-400">Reading Time</div>
335
+ <div class="text-xl font-bold text-gray-800 dark:text-white" id="analysis-reading-time">0 min</div>
336
+ </div>
337
+ </div>
338
+ <div>
339
+ <h4 class="font-medium text-gray-700 dark:text-gray-300 mb-2">Word Frequency</h4>
340
+ <div class="max-h-32 overflow-y-auto" id="word-frequency-list">
341
+ <div class="text-center text-gray-500 dark:text-gray-400 py-2">No words to analyze</div>
342
+ </div>
343
+ </div>
344
+ </div>
345
+
346
+ <div class="mt-4">
347
+ <div class="flex flex-wrap gap-2 mb-4">
348
+ <button id="lowercase-btn" class="format-btn bg-gray-200 hover:bg-gray-300 dark:bg-slate-600 dark:hover:bg-slate-500 text-gray-700 dark:text-gray-200 px-3 py-1 rounded-md text-sm flex items-center">
349
+ <i class="fas fa-text-height mr-1"></i> lowercase
350
+ </button>
351
+ <button id="uppercase-btn" class="format-btn bg-gray-200 hover:bg-gray-300 dark:bg-slate-600 dark:hover:bg-slate-500 text-gray-700 dark:text-gray-200 px-3 py-1 rounded-md text-sm flex items-center">
352
+ <i class="fas fa-text-height mr-1 transform rotate-180"></i> UPPERCASE
353
+ </button>
354
+ <button id="bold-btn" class="format-btn bg-gray-200 hover:bg-gray-300 dark:bg-slate-600 dark:hover:bg-slate-500 text-gray-700 dark:text-gray-200 px-3 py-1 rounded-md text-sm flex items-center">
355
+ <i class="fas fa-bold mr-1"></i> Bold
356
+ </button>
357
+ <button id="italic-btn" class="format-btn bg-gray-200 hover:bg-gray-300 dark:bg-slate-600 dark:hover:bg-slate-500 text-gray-700 dark:text-gray-200 px-3 py-1 rounded-md text-sm flex items-center">
358
+ <i class="fas fa-italic mr-1"></i> Italic
359
+ </button>
360
+ <button id="underline-btn" class="format-btn bg-gray-200 hover:bg-gray-300 dark:bg-slate-600 dark:hover:bg-slate-500 text-gray-700 dark:text-gray-200 px-3 py-1 rounded-md text-sm flex items-center">
361
+ <i class="fas fa-underline mr-1"></i> Underline
362
+ </button>
363
+ <button id="capitalize-btn" class="format-btn bg-gray-200 hover:bg-gray-300 dark:bg-slate-600 dark:hover:bg-slate-500 text-gray-700 dark:text-gray-200 px-3 py-1 rounded-md text-sm flex items-center">
364
+ <i class="fas fa-paragraph mr-1"></i> Capitalize
365
+ </button>
366
+ <button id="reverse-btn" class="format-btn bg-gray-200 hover:bg-gray-300 dark:bg-slate-600 dark:hover:bg-slate-500 text-gray-700 dark:text-gray-200 px-3 py-1 rounded-md text-sm flex items-center">
367
+ <i class="fas fa-exchange-alt mr-1"></i> Reverse
368
+ </button>
369
+ <button id="encode-btn" class="format-btn code-btn px-3 py-1 rounded-md text-sm flex items-center">
370
+ <i class="fas fa-lock mr-1"></i> Encode
371
+ </button>
372
+ <button id="decode-btn" class="format-btn code-btn px-3 py-1 rounded-md text-sm flex items-center">
373
+ <i class="fas fa-lock-open mr-1"></i> Decode
374
+ </button>
375
+ <button id="remove-format-btn" class="format-btn bg-red-100 hover:bg-red-200 dark:bg-red-900 dark:hover:bg-red-800 text-red-700 dark:text-red-200 px-3 py-1 rounded-md text-sm flex items-center">
376
+ <i class="fas fa-eraser mr-1"></i> Remove Format
377
+ </button>
378
+ <button id="find-replace-btn" class="format-btn bg-purple-100 hover:bg-purple-200 dark:bg-purple-900 dark:hover:bg-purple-800 text-purple-700 dark:text-purple-200 px-3 py-1 rounded-md text-sm flex items-center">
379
+ <i class="fas fa-search mr-1"></i> Find & Replace
380
+ </button>
381
+ <button id="analyze-btn" class="format-btn bg-green-100 hover:bg-green-200 dark:bg-green-900 dark:hover:bg-green-800 text-green-700 dark:text-green-200 px-3 py-1 rounded-md text-sm flex items-center">
382
+ <i class="fas fa-chart-bar mr-1"></i> Analyze Text
383
+ </button>
384
+ <button id="speak-toggle-btn" class="format-btn bg-indigo-100 hover:bg-indigo-200 dark:bg-indigo-900 dark:hover:bg-indigo-800 text-indigo-700 dark:text-indigo-200 px-3 py-1 rounded-md text-sm flex items-center">
385
+ <i class="fas fa-volume-up mr-1"></i> Text to Speech
386
+ </button>
387
+ </div>
388
+
389
+ <div class="flex justify-between items-center">
390
+ <div class="flex items-center space-x-2">
391
+ <button id="clear-btn" class="bg-gray-200 hover:bg-gray-300 dark:bg-slate-600 dark:hover:bg-slate-500 text-gray-700 dark:text-gray-200 px-4 py-2 rounded-md flex items-center">
392
+ <i class="fas fa-trash-alt mr-2"></i> Clear
393
+ </button>
394
+ <button id="copy-btn" class="copy-btn bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-md flex items-center relative">
395
+ <i class="fas fa-copy mr-2"></i> Copy Text
396
+ <span class="tooltip">Copied to clipboard!</span>
397
+ </button>
398
+ </div>
399
+ <div class="text-xs text-gray-500 dark:text-gray-400">
400
+ <span id="word-count">0 words</span>
401
+ </div>
402
+ </div>
403
+ </div>
404
+ </div>
405
+
406
+ <div class="bg-gray-50 dark:bg-slate-700 p-4 border-t border-gray-200 dark:border-slate-600">
407
+ <div class="flex flex-wrap items-center justify-center gap-4 text-sm text-gray-600 dark:text-gray-300">
408
+ <div class="flex items-center">
409
+ <i class="fas fa-info-circle mr-1"></i>
410
+ <span>Formats removed: bold, italic, colors, fonts, etc.</span>
411
+ </div>
412
+ <div class="flex items-center">
413
+ <i class="fas fa-keyboard mr-1"></i>
414
+ <span>Shortcut: Ctrl+V to paste, Ctrl+C to copy</span>
415
+ </div>
416
+ </div>
417
+ </div>
418
+ </div>
419
+
420
+ <div class="mt-8 text-center text-gray-500 dark:text-gray-400 text-sm">
421
+ <p>Clean Paste - Remove formatting and apply new styles to your text</p>
422
+ </div>
423
+
424
+ <audio id="click-sound" preload="auto">
425
+ <source src="https://electronicenergysource.com/entirelibofsounds/Salvaged%20Files/1001%20Sound%20Effects/Video%20Game%20Sounds/Arcade%20Beep%2001.wav" type="audio/wav">
426
+ </audio>
427
+
428
+ <script>
429
+ document.addEventListener('DOMContentLoaded', function() {
430
+ const cleanTextArea = document.getElementById('clean-text');
431
+ const pasteBtn = document.getElementById('paste-btn');
432
+ const copyBtn = document.getElementById('copy-btn');
433
+ const clearBtn = document.getElementById('clear-btn');
434
+ const charCount = document.querySelector('.character-count');
435
+ const wordCount = document.getElementById('word-count');
436
+ const tooltip = document.querySelector('.tooltip');
437
+ const clickSound = document.getElementById('click-sound');
438
+ const themeToggle = document.getElementById('theme-toggle');
439
+ const themeIcon = document.getElementById('theme-icon');
440
+
441
+ // Format buttons
442
+ const lowercaseBtn = document.getElementById('lowercase-btn');
443
+ const uppercaseBtn = document.getElementById('uppercase-btn');
444
+ const boldBtn = document.getElementById('bold-btn');
445
+ const italicBtn = document.getElementById('italic-btn');
446
+ const underlineBtn = document.getElementById('underline-btn');
447
+ const capitalizeBtn = document.getElementById('capitalize-btn');
448
+ const reverseBtn = document.getElementById('reverse-btn');
449
+ const encodeBtn = document.getElementById('encode-btn');
450
+ const decodeBtn = document.getElementById('decode-btn');
451
+ const removeFormatBtn = document.getElementById('remove-format-btn');
452
+
453
+ // New feature buttons
454
+ const findReplaceBtn = document.getElementById('find-replace-btn');
455
+ const analyzeBtn = document.getElementById('analyze-btn');
456
+ const speakToggleBtn = document.getElementById('speak-toggle-btn');
457
+ const findReplacePanel = document.getElementById('find-replace-panel');
458
+ const analysisPanel = document.getElementById('analysis-panel');
459
+ const speechPanel = document.getElementById('speech-panel');
460
+
461
+ // Find & Replace elements
462
+ const findInput = document.getElementById('find-input');
463
+ const replaceInput = document.getElementById('replace-input');
464
+ const findNextBtn = document.getElementById('find-next-btn');
465
+ const findPrevBtn = document.getElementById('find-prev-btn');
466
+ const replaceBtn = document.getElementById('replace-btn');
467
+ const caseSensitiveCheckbox = document.getElementById('case-sensitive');
468
+ const findResults = document.getElementById('find-results');
469
+
470
+ // Analysis elements
471
+ const analysisChars = document.getElementById('analysis-chars');
472
+ const analysisWords = document.getElementById('analysis-words');
473
+ const analysisSentences = document.getElementById('analysis-sentences');
474
+ const analysisReadingTime = document.getElementById('analysis-reading-time');
475
+ const wordFrequencyList = document.getElementById('word-frequency-list');
476
+
477
+ // Speech synthesis elements
478
+ const voiceSelect = document.getElementById('voice-select');
479
+ const rateControl = document.getElementById('rate-control');
480
+ const pitchControl = document.getElementById('pitch-control');
481
+ const rateValue = document.getElementById('rate-value');
482
+ const pitchValue = document.getElementById('pitch-value');
483
+ const speakBtn = document.getElementById('speak-btn');
484
+ const pauseBtn = document.getElementById('pause-btn');
485
+ const stopBtn = document.getElementById('stop-btn');
486
+ const progressBarFill = document.getElementById('progress-bar-fill');
487
+
488
+ // Variables for find & replace
489
+ let currentFindIndex = -1;
490
+ let findMatches = [];
491
+
492
+ // Variables for speech synthesis
493
+ let speechSynthesis = window.speechSynthesis;
494
+ let speechUtterance = null;
495
+ let voices = [];
496
+ let isSpeaking = false;
497
+ let isPaused = false;
498
+ let speechProgressInterval;
499
+
500
+ // Play click sound
501
+ function playClickSound() {
502
+ clickSound.currentTime = 0;
503
+ clickSound.play().catch(e => console.log("Audio play failed:", e));
504
+ }
505
+
506
+ // Theme toggle
507
+ function toggleTheme() {
508
+ if (document.documentElement.classList.contains('dark')) {
509
+ document.documentElement.classList.remove('dark');
510
+ localStorage.setItem('theme', 'light');
511
+ themeIcon.classList.remove('fa-sun');
512
+ themeIcon.classList.add('fa-moon');
513
+ } else {
514
+ document.documentElement.classList.add('dark');
515
+ localStorage.setItem('theme', 'dark');
516
+ themeIcon.classList.remove('fa-moon');
517
+ themeIcon.classList.add('fa-sun');
518
+ }
519
+ playClickSound();
520
+ }
521
+
522
+ // Check for saved theme preference
523
+ if (localStorage.getItem('theme') === 'dark' ||
524
+ (!localStorage.getItem('theme') && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
525
+ document.documentElement.classList.add('dark');
526
+ themeIcon.classList.remove('fa-moon');
527
+ themeIcon.classList.add('fa-sun');
528
+ }
529
+
530
+ themeToggle.addEventListener('click', toggleTheme);
531
+
532
+ // Update character and word count
533
+ function updateCounts() {
534
+ const text = cleanTextArea.value;
535
+ charCount.textContent = `${text.length} characters`;
536
+
537
+ const words = text.trim() === '' ? 0 : text.trim().split(/\s+/).length;
538
+ wordCount.textContent = `${words} words`;
539
+ }
540
+
541
+ // Handle paste from button
542
+ pasteBtn.addEventListener('click', async function() {
543
+ playClickSound();
544
+ try {
545
+ const text = await navigator.clipboard.readText();
546
+ cleanTextArea.value = text;
547
+ updateCounts();
548
+
549
+ // Show success feedback
550
+ pasteBtn.innerHTML = '<i class="fas fa-check mr-1"></i> Pasted!';
551
+ pasteBtn.classList.remove('bg-blue-100', 'text-blue-700', 'dark:bg-blue-900', 'dark:text-blue-200');
552
+ pasteBtn.classList.add('bg-green-100', 'text-green-700', 'dark:bg-green-900', 'dark:text-green-200');
553
+
554
+ setTimeout(() => {
555
+ pasteBtn.innerHTML = '<i class="fas fa-paste mr-1"></i> Paste';
556
+ pasteBtn.classList.remove('bg-green-100', 'text-green-700', 'dark:bg-green-900', 'dark:text-green-200');
557
+ pasteBtn.classList.add('bg-blue-100', 'text-blue-700', 'dark:bg-blue-900', 'dark:text-blue-200');
558
+ }, 1500);
559
+ } catch (err) {
560
+ alert('Failed to read clipboard. Please paste manually into the text area.');
561
+ console.error('Failed to read clipboard contents:', err);
562
+ }
563
+ });
564
+
565
+ // Handle copy button
566
+ copyBtn.addEventListener('click', function() {
567
+ playClickSound();
568
+ if (cleanTextArea.value.trim() === '') {
569
+ alert('Nothing to copy! Please paste some text first.');
570
+ return;
571
+ }
572
+
573
+ navigator.clipboard.writeText(cleanTextArea.value)
574
+ .then(() => {
575
+ // Show tooltip
576
+ tooltip.classList.add('show-tooltip');
577
+ setTimeout(() => {
578
+ tooltip.classList.remove('show-tooltip');
579
+ }, 2000);
580
+ })
581
+ .catch(err => {
582
+ console.error('Failed to copy text: ', err);
583
+ alert('Failed to copy text. Please try again.');
584
+ });
585
+ });
586
+
587
+ // Handle clear button
588
+ clearBtn.addEventListener('click', function() {
589
+ playClickSound();
590
+ cleanTextArea.value = '';
591
+ updateCounts();
592
+ clearHighlights();
593
+ findMatches = [];
594
+ currentFindIndex = -1;
595
+ findResults.textContent = '0 matches';
596
+
597
+ // Stop any ongoing speech
598
+ stopSpeech();
599
+ });
600
+
601
+ // Formatting functions
602
+ function applyFormat(formatFn) {
603
+ if (cleanTextArea.value.trim() === '') {
604
+ alert('No text to format! Please paste some text first.');
605
+ return;
606
+ }
607
+
608
+ const startPos = cleanTextArea.selectionStart;
609
+ const endPos = cleanTextArea.selectionEnd;
610
+
611
+ if (startPos === endPos) {
612
+ // No selection - format entire text
613
+ cleanTextArea.value = formatFn(cleanTextArea.value);
614
+ } else {
615
+ // Format only selected text
616
+ const selectedText = cleanTextArea.value.substring(startPos, endPos);
617
+ const formattedText = formatFn(selectedText);
618
+ cleanTextArea.value = cleanTextArea.value.substring(0, startPos) +
619
+ formattedText +
620
+ cleanTextArea.value.substring(endPos);
621
+ }
622
+
623
+ updateCounts();
624
+ }
625
+
626
+ // Base64 encode function
627
+ function encodeBase64(text) {
628
+ return btoa(unescape(encodeURIComponent(text)));
629
+ }
630
+
631
+ // Base64 decode function
632
+ function decodeBase64(text) {
633
+ try {
634
+ return decodeURIComponent(escape(atob(text)));
635
+ } catch (e) {
636
+ alert("Invalid Base64 encoded string!");
637
+ return text;
638
+ }
639
+ }
640
+
641
+ // Format button handlers
642
+ lowercaseBtn.addEventListener('click', function() {
643
+ playClickSound();
644
+ applyFormat(text => text.toLowerCase());
645
+ highlightActiveButton(lowercaseBtn);
646
+ });
647
+
648
+ uppercaseBtn.addEventListener('click', function() {
649
+ playClickSound();
650
+ applyFormat(text => text.toUpperCase());
651
+ highlightActiveButton(uppercaseBtn);
652
+ });
653
+
654
+ boldBtn.addEventListener('click', function() {
655
+ playClickSound();
656
+ applyFormat(text => {
657
+ const startPos = cleanTextArea.selectionStart;
658
+ const endPos = cleanTextArea.selectionEnd;
659
+
660
+ if (startPos === endPos) {
661
+ return text.replace(/([^\n])([^\n]+)?/g, function(match, p1, p2) {
662
+ return p1 + (p2 ? '**' + p2 + '**' : '');
663
+ });
664
+ } else {
665
+ return '**' + text + '**';
666
+ }
667
+ });
668
+ highlightActiveButton(boldBtn);
669
+ });
670
+
671
+ italicBtn.addEventListener('click', function() {
672
+ playClickSound();
673
+ applyFormat(text => {
674
+ const startPos = cleanTextArea.selectionStart;
675
+ const endPos = cleanTextArea.selectionEnd;
676
+
677
+ if (startPos === endPos) {
678
+ return text.replace(/([^\n])([^\n]+)?/g, function(match, p1, p2) {
679
+ return p1 + (p2 ? '_' + p2 + '_' : '');
680
+ });
681
+ } else {
682
+ return '_' + text + '_';
683
+ }
684
+ });
685
+ highlightActiveButton(italicBtn);
686
+ });
687
+
688
+ underlineBtn.addEventListener('click', function() {
689
+ playClickSound();
690
+ applyFormat(text => {
691
+ const startPos = cleanTextArea.selectionStart;
692
+ const endPos = cleanTextArea.selectionEnd;
693
+
694
+ if (startPos === endPos) {
695
+ return text.replace(/([^\n])([^\n]+)?/g, function(match, p1, p2) {
696
+ return p1 + (p2 ? '<u>' + p2 + '</u>' : '');
697
+ });
698
+ } else {
699
+ return '<u>' + text + '</u>';
700
+ }
701
+ });
702
+ highlightActiveButton(underlineBtn);
703
+ });
704
+
705
+ capitalizeBtn.addEventListener('click', function() {
706
+ playClickSound();
707
+ applyFormat(text => {
708
+ return text.toLowerCase().replace(/(^|\s)\S/g, function(firstLetter) {
709
+ return firstLetter.toUpperCase();
710
+ });
711
+ });
712
+ highlightActiveButton(capitalizeBtn);
713
+ });
714
+
715
+ reverseBtn.addEventListener('click', function() {
716
+ playClickSound();
717
+ applyFormat(text => {
718
+ return text.split('').reverse().join('');
719
+ });
720
+ highlightActiveButton(reverseBtn);
721
+ });
722
+
723
+ encodeBtn.addEventListener('click', function() {
724
+ playClickSound();
725
+ applyFormat(text => {
726
+ return encodeBase64(text);
727
+ });
728
+ highlightActiveButton(encodeBtn);
729
+ });
730
+
731
+ decodeBtn.addEventListener('click', function() {
732
+ playClickSound();
733
+ applyFormat(text => {
734
+ return decodeBase64(text);
735
+ });
736
+ highlightActiveButton(decodeBtn);
737
+ });
738
+
739
+ removeFormatBtn.addEventListener('click', function() {
740
+ playClickSound();
741
+ applyFormat(text => {
742
+ // Remove markdown formatting
743
+ let cleaned = text.replace(/(\*\*|__)(.*?)\1/g, '$2'); // bold
744
+ cleaned = cleaned.replace(/(\*|_)(.*?)\1/g, '$2'); // italic
745
+ cleaned = cleaned.replace(/<u>(.*?)<\/u>/g, '$1'); // underline
746
+ cleaned = cleaned.replace(/<[^>]+>/g, ''); // any HTML tags
747
+ return cleaned;
748
+ });
749
+ highlightActiveButton(removeFormatBtn);
750
+ });
751
+
752
+ // Highlight active format button
753
+ function highlightActiveButton(button) {
754
+ // Remove active class from all format buttons
755
+ document.querySelectorAll('.format-btn').forEach(btn => {
756
+ if (!btn.classList.contains('code-btn') &&
757
+ !btn.classList.contains('bg-purple-100') &&
758
+ !btn.classList.contains('bg-green-100') &&
759
+ !btn.classList.contains('bg-indigo-100')) {
760
+ btn.classList.remove('active-format');
761
+ btn.classList.add('bg-gray-200', 'text-gray-700', 'dark:bg-slate-600', 'dark:text-gray-200');
762
+ btn.classList.remove('bg-blue-600', 'text-white');
763
+ }
764
+ });
765
+
766
+ // Add active class to clicked button
767
+ if (button.classList.contains('code-btn')) {
768
+ button.classList.add('bg-yellow-600', 'dark:bg-yellow-700');
769
+ } else if (button.classList.contains('bg-purple-100')) {
770
+ button.classList.add('bg-purple-600', 'text-white');
771
+ } else if (button.classList.contains('bg-green-100')) {
772
+ button.classList.add('bg-green-600', 'text-white');
773
+ } else if (button.classList.contains('bg-indigo-100')) {
774
+ button.classList.add('bg-indigo-600', 'text-white');
775
+ } else {
776
+ button.classList.add('active-format');
777
+ button.classList.remove('bg-gray-200', 'text-gray-700', 'dark:bg-slate-600', 'dark:text-gray-200');
778
+ button.classList.add('bg-blue-600', 'text-white');
779
+ }
780
+
781
+ // Remove highlight after 1.5 seconds
782
+ setTimeout(() => {
783
+ if (button.classList.contains('code-btn')) {
784
+ button.classList.remove('bg-yellow-600', 'dark:bg-yellow-700');
785
+ } else if (button.classList.contains('bg-purple-100')) {
786
+ button.classList.remove('bg-purple-600', 'text-white');
787
+ } else if (button.classList.contains('bg-green-100')) {
788
+ button.classList.remove('bg-green-600', 'text-white');
789
+ } else if (button.classList.contains('bg-indigo-100')) {
790
+ button.classList.remove('bg-indigo-600', 'text-white');
791
+ } else {
792
+ button.classList.remove('active-format');
793
+ button.classList.remove('bg-blue-600', 'text-white');
794
+ button.classList.add('bg-gray-200', 'text-gray-700', 'dark:bg-slate-600', 'dark:text-gray-200');
795
+ }
796
+ }, 1500);
797
+ }
798
+
799
+ // Toggle find & replace panel
800
+ findReplaceBtn.addEventListener('click', function() {
801
+ playClickSound();
802
+ analysisPanel.classList.remove('open');
803
+ speechPanel.classList.remove('open');
804
+ findReplacePanel.classList.toggle('open');
805
+
806
+ if (findReplacePanel.classList.contains('open')) {
807
+ highlightActiveButton(findReplaceBtn);
808
+ findInput.focus();
809
+ }
810
+ });
811
+
812
+ // Toggle analysis panel
813
+ analyzeBtn.addEventListener('click', function() {
814
+ playClickSound();
815
+ findReplacePanel.classList.remove('open');
816
+ speechPanel.classList.remove('open');
817
+ analysisPanel.classList.toggle('open');
818
+
819
+ if (analysisPanel.classList.contains('open')) {
820
+ highlightActiveButton(analyzeBtn);
821
+ analyzeText();
822
+ }
823
+ });
824
+
825
+ // Toggle speech panel
826
+ speakToggleBtn.addEventListener('click', function() {
827
+ playClickSound();
828
+ findReplacePanel.classList.remove('open');
829
+ analysisPanel.classList.remove('open');
830
+ speechPanel.classList.toggle('open');
831
+
832
+ if (speechPanel.classList.contains('open')) {
833
+ highlightActiveButton(speakToggleBtn);
834
+ loadVoices();
835
+ }
836
+ });
837
+
838
+ // Analyze text function
839
+ function analyzeText() {
840
+ const text = cleanTextArea.value;
841
+
842
+ // Basic stats
843
+ analysisChars.textContent = text.length;
844
+
845
+ const words = text.trim() === '' ? 0 : text.trim().split(/\s+/).length;
846
+ analysisWords.textContent = words;
847
+
848
+ // Count sentences (very basic implementation)
849
+ const sentences = text.split(/[.!?]+/).filter(s => s.trim().length > 0).length;
850
+ analysisSentences.textContent = sentences;
851
+
852
+ // Calculate reading time (average reading speed: 200 words per minute)
853
+ const readingTime = Math.ceil(words / 200);
854
+ analysisReadingTime.textContent = readingTime <= 1 ? '1 min' : `${readingTime} mins`;
855
+
856
+ // Word frequency analysis
857
+ if (text.trim() === '') {
858
+ wordFrequencyList.innerHTML = '<div class="text-center text-gray-500 dark:text-gray-400 py-2">No words to analyze</div>';
859
+ return;
860
+ }
861
+
862
+ // Process words
863
+ const wordsArray = text.toLowerCase()
864
+ .replace(/[^\w\s]/g, '') // Remove punctuation
865
+ .split(/\s+/)
866
+ .filter(word => word.length > 0);
867
+
868
+ // Count word frequency
869
+ const wordFrequency = {};
870
+ wordsArray.forEach(word => {
871
+ wordFrequency[word] = (wordFrequency[word] || 0) + 1;
872
+ });
873
+
874
+ // Sort by frequency
875
+ const sortedWords = Object.keys(wordFrequency).sort((a, b) => wordFrequency[b] - wordFrequency[a]);
876
+
877
+ // Display top 20 words
878
+ wordFrequencyList.innerHTML = '';
879
+ sortedWords.slice(0, 20).forEach(word => {
880
+ const frequency = wordFrequency[word];
881
+ const percentage = Math.round((frequency / wordsArray.length) * 100);
882
+
883
+ const item = document.createElement('div');
884
+ item.className = 'word-frequency-item flex justify-between items-center mb-1 p-2 bg-white dark:bg-slate-600 rounded';
885
+
886
+ const wordSpan = document.createElement('span');
887
+ wordSpan.className = 'font-medium dark:text-white';
888
+ wordSpan.textContent = word;
889
+
890
+ const freqSpan = document.createElement('span');
891
+ freqSpan.className = 'text-sm text-gray-500 dark:text-gray-300';
892
+ freqSpan.textContent = `${frequency} (${percentage}%)`;
893
+
894
+ item.appendChild(wordSpan);
895
+ item.appendChild(freqSpan);
896
+ wordFrequencyList.appendChild(item);
897
+ });
898
+ }
899
+
900
+ // Find text function
901
+ function findText(direction = 'next') {
902
+ const searchText = findInput.value;
903
+ if (!searchText) {
904
+ alert('Please enter text to find');
905
+ return;
906
+ }
907
+
908
+ const text = cleanTextArea.value;
909
+ const caseSensitive = caseSensitiveCheckbox.checked;
910
+ const flags = caseSensitive ? 'g' : 'gi';
911
+
912
+ // Clear previous highlights
913
+ clearHighlights();
914
+
915
+ // Find all matches
916
+ const regex = new RegExp(escapeRegExp(searchText), flags);
917
+ findMatches = [];
918
+ let match;
919
+
920
+ while ((match = regex.exec(text)) !== null) {
921
+ findMatches.push({
922
+ start: match.index,
923
+ end: match.index + match[0].length
924
+ });
925
+ }
926
+
927
+ findResults.textContent = `${findMatches.length} matches`;
928
+
929
+ if (findMatches.length === 0) {
930
+ alert('No matches found');
931
+ return;
932
+ }
933
+
934
+ // Navigate to next/previous match
935
+ if (direction === 'next') {
936
+ currentFindIndex = (currentFindIndex + 1) % findMatches.length;
937
+ } else {
938
+ currentFindIndex = (currentFindIndex - 1 + findMatches.length) % findMatches.length;
939
+ }
940
+
941
+ // Highlight all matches and scroll to current one
942
+ highlightMatches();
943
+
944
+ // Scroll to current match
945
+ const currentMatch = findMatches[currentFindIndex];
946
+ scrollToMatch(currentMatch.start, currentMatch.end);
947
+ }
948
+
949
+ // Helper function to escape regex special characters
950
+ function escapeRegExp(string) {
951
+ return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
952
+ }
953
+
954
+ // Highlight all matches
955
+ function highlightMatches() {
956
+ const text = cleanTextArea.value;
957
+ let highlightedText = '';
958
+ let lastIndex = 0;
959
+
960
+ findMatches.forEach((match, index) => {
961
+ // Add text before match
962
+ highlightedText += text.substring(lastIndex, match.start);
963
+
964
+ // Add highlighted match
965
+ const matchClass = index === currentFindIndex ? 'current-highlight': 'highlight';
966
+ highlightedText += `<span class="${matchClass}">${text.substring(match.start, match.end)}</span>`;
967
+
968
+ lastIndex = match.end;
969
+ });
970
+
971
+ // Add remaining text
972
+ highlightedText += text.substring(lastIndex);
973
+
974
+ // Update textarea with highlighted HTML (using a hidden div)
975
+ const hiddenDiv = document.createElement('div');
976
+ hiddenDiv.style.display = 'none';
977
+ hiddenDiv.innerHTML = highlightedText;
978
+ document.body.appendChild(hiddenDiv);
979
+
980
+ // Get plain text with markers for highlights
981
+ let plainText = '';
982
+ const walker = document.createTreeWalker(hiddenDiv, NodeFilter.SHOW_TEXT, null, false);
983
+ let node;
984
+
985
+ while (node = walker.nextNode()) {
986
+ plainText += node.nodeValue;
987
+ }
988
+
989
+ // Remove the hidden div
990
+ document.body.removeChild(hiddenDiv);
991
+
992
+ // Update textarea with plain text
993
+ cleanTextArea.value = plainText;
994
+ }
995
+
996
+ // Clear all highlights
997
+ function clearHighlights() {
998
+ if (findMatches.length > 0) {
999
+ const text = cleanTextArea.value;
1000
+ let plainText = '';
1001
+ let lastIndex = 0;
1002
+
1003
+ findMatches.forEach(match => {
1004
+ plainText += text.substring(lastIndex, match.start);
1005
+ plainText += text.substring(match.start, match.end);
1006
+ lastIndex = match.end;
1007
+ });
1008
+
1009
+ plainText += text.substring(lastIndex);
1010
+ cleanTextArea.value = plainText;
1011
+
1012
+ findMatches = [];
1013
+ currentFindIndex = -1;
1014
+ findResults.textContent = '0 matches';
1015
+ }
1016
+ }
1017
+
1018
+ // Scroll to match position
1019
+ function scrollToMatch(start, end) {
1020
+ // Calculate line height (approximate)
1021
+ const lineHeight = 20; // px
1022
+
1023
+ // Calculate number of lines to scroll
1024
+ const textBefore = cleanTextArea.value.substring(0, start);
1025
+ const linesBefore = textBefore.split('\n').length - 1;
1026
+
1027
+ // Scroll to position
1028
+ cleanTextArea.scrollTop = linesBefore * lineHeight;
1029
+
1030
+ // Set selection
1031
+ cleanTextArea.focus();
1032
+ cleanTextArea.setSelectionRange(start, end);
1033
+ }
1034
+
1035
+ // Replace text function
1036
+ function replaceText() {
1037
+ if (findMatches.length === 0) {
1038
+ findText();
1039
+ return;
1040
+ }
1041
+
1042
+ const replaceText = replaceInput.value;
1043
+ const currentMatch = findMatches[currentFindIndex];
1044
+
1045
+ // Replace current match
1046
+ const text = cleanTextArea.value;
1047
+ const newText = text.substring(0, currentMatch.start) + replaceText + text.substring(currentMatch.end);
1048
+ cleanTextArea.value = newText;
1049
+
1050
+ // Adjust positions of remaining matches
1051
+ const lengthDiff = replaceText.length - (currentMatch.end - currentMatch.start);
1052
+
1053
+ findMatches.forEach((match, index) => {
1054
+ if (match.start > currentMatch.start) {
1055
+ match.start += lengthDiff;
1056
+ match.end += lengthDiff;
1057
+ }
1058
+ });
1059
+
1060
+ // Remove current match from array
1061
+ findMatches.splice(currentFindIndex, 1);
1062
+
1063
+ if (findMatches.length === 0) {
1064
+ findResults.textContent = '0 matches';
1065
+ currentFindIndex = -1;
1066
+ return;
1067
+ }
1068
+
1069
+ // Update current index and highlight
1070
+ currentFindIndex = Math.min(currentFindIndex, findMatches.length - 1);
1071
+ findResults.textContent = `${findMatches.length} matches`;
1072
+ highlightMatches();
1073
+ }
1074
+
1075
+ // Speech Synthesis Functions
1076
+ function loadVoices() {
1077
+ // Chrome loads voices asynchronously
1078
+ voices = speechSynthesis.getVoices();
1079
+ voiceSelect.innerHTML = '';
1080
+
1081
+ if (voices.length === 0) {
1082
+ voiceSelect.innerHTML = '<option value="">Loading voices...</option>';
1083
+ // Try again in 1 second if voices aren't loaded yet
1084
+ setTimeout(loadVoices, 1000);
1085
+ return;
1086
+ }
1087
+
1088
+ // Filter voices to only include ones with localService (usually system voices)
1089
+ const systemVoices = voices.filter(voice => voice.localService);
1090
+
1091
+ systemVoices.forEach((voice, index) => {
1092
+ const option = document.createElement('option');
1093
+ option.value = index;
1094
+ option.textContent = `${voice.name} (${voice.lang})`;
1095
+ if (voice.default) {
1096
+ option.textContent += ' [Default]';
1097
+ }
1098
+ voiceSelect.appendChild(option);
1099
+ });
1100
+
1101
+ // Try to select a reasonable default voice
1102
+ const defaultVoice = systemVoices.find(voice =>
1103
+ voice.lang.startsWith('en-') && voice.default
1104
+ ) || systemVoices.find(voice => voice.default) || systemVoices[0];
1105
+
1106
+ if (defaultVoice) {
1107
+ const defaultIndex = systemVoices.indexOf(defaultVoice);
1108
+ voiceSelect.value = defaultIndex;
1109
+ }
1110
+ }
1111
+
1112
+ function speakText() {
1113
+ if (isSpeaking) {
1114
+ pauseSpeech();
1115
+ return;
1116
+ }
1117
+
1118
+ if (isPaused) {
1119
+ resumeSpeech();
1120
+ return;
1121
+ }
1122
+
1123
+ const text = cleanTextArea.value;
1124
+ if (!text.trim()) {
1125
+ alert('No text to speak! Please paste some text first.');
1126
+ return;
1127
+ }
1128
+
1129
+ // Stop any ongoing speech
1130
+ stopSpeech();
1131
+
1132
+ // Create a new utterance
1133
+ speechUtterance = new SpeechSynthesisUtterance(text);
1134
+
1135
+ // Set voice
1136
+ const selectedVoiceIndex = parseInt(voiceSelect.value);
1137
+ if (!isNaN(selectedVoiceIndex) && voices[selectedVoiceIndex]) {
1138
+ speechUtterance.voice = voices[selectedVoiceIndex];
1139
+ }
1140
+
1141
+ // Set rate and pitch
1142
+ speechUtterance.rate = parseFloat(rateControl.value);
1143
+ speechUtterance.pitch = parseFloat(pitchControl.value);
1144
+
1145
+ // Update UI
1146
+ isSpeaking = true;
1147
+ speakBtn.innerHTML = '<i class="fas fa-pause mr-2"></i> Pause';
1148
+ speakBtn.classList.remove('bg-green-500', 'hover:bg-green-600');
1149
+ speakBtn.classList.add('bg-yellow-500', 'hover:bg-yellow-600');
1150
+ speakToggleBtn.classList.add('voice-active');
1151
+
1152
+ // Reset progress bar
1153
+ progressBarFill.style.width = '0%';
1154
+
1155
+ // Start progress tracking
1156
+ const textLength = text.length;
1157
+ let currentPosition = 0;
1158
+
1159
+ speechProgressInterval = setInterval(() => {
1160
+ if (speechSynthesis.speaking && !isPaused) {
1161
+ // Estimate position based on time elapsed
1162
+ const elapsed = (speechUtterance.elapsedTime || 0) * 1000; // ms
1163
+ const totalDuration = (textLength / (speechUtterance.rate * 8)) * 1000; // rough estimate
1164
+ const progress = Math.min(100, (elapsed / totalDuration) * 100);
1165
+ progressBarFill.style.width = `${progress}%`;
1166
+ }
1167
+ }, 100);
1168
+
1169
+ // Event handlers
1170
+ speechUtterance.onend = function() {
1171
+ stopSpeech();
1172
+ };
1173
+
1174
+ speechUtterance.onerror = function(event) {
1175
+ console.error('SpeechSynthesis error:', event);
1176
+ stopSpeech();
1177
+ alert('Error occurred during speech synthesis: ' + event.error);
1178
+ };
1179
+
1180
+ speechUtterance.onboundary = function(event) {
1181
+ if (event.name === 'word') {
1182
+ currentPosition = event.charIndex;
1183
+ }
1184
+ };
1185
+
1186
+ // Speak the text
1187
+ speechSynthesis.speak(speechUtterance);
1188
+ }
1189
+
1190
+ function pauseSpeech() {
1191
+ if (isSpeaking && !isPaused) {
1192
+ speechSynthesis.pause();
1193
+ isSpeaking = false;
1194
+ isPaused = true;
1195
+ speakBtn.innerHTML = '<i class="fas fa-play mr-2"></i> Resume';
1196
+ speakBtn.classList.remove('bg-yellow-500', 'hover:bg-yellow-600');
1197
+ speakBtn.classList.add('bg-green-500', 'hover:bg-green-600');
1198
+ }
1199
+ }
1200
+
1201
+ function resumeSpeech() {
1202
+ if (isPaused) {
1203
+ speechSynthesis.resume();
1204
+ isSpeaking = true;
1205
+ isPaused = false;
1206
+ speakBtn.innerHTML = '<i class="fas fa-pause mr-2"></i> Pause';
1207
+ speakBtn.classList.remove('bg-green-500', 'hover:bg-green-600');
1208
+ speakBtn.classList.add('bg-yellow-500', 'hover:bg-yellow-600');
1209
+ }
1210
+ }
1211
+
1212
+ function stopSpeech() {
1213
+ speechSynthesis.cancel();
1214
+ isSpeaking = false;
1215
+ isPaused = false;
1216
+
1217
+ // Clear interval
1218
+ if (speechProgressInterval) {
1219
+ clearInterval(speechProgressInterval);
1220
+ speechProgressInterval = null;
1221
+ }
1222
+
1223
+ // Reset UI
1224
+ speakBtn.innerHTML = '<i class="fas fa-play mr-2"></i> Speak';
1225
+ speakBtn.classList.remove('bg-yellow-500', 'hover:bg-yellow-600');
1226
+ speakBtn.classList.add('bg-green-500', 'hover:bg-green-600');
1227
+ speakToggleBtn.classList.remove('voice-active');
1228
+ progressBarFill.style.width = '0%';
1229
+ }
1230
+
1231
+ // Initialize speech synthesis
1232
+ if ('speechSynthesis' in window) {
1233
+ // Chrome loads voices asynchronously
1234
+ speechSynthesis.onvoiceschanged = loadVoices;
1235
+
1236
+ // Load voices immediately if they're already available
1237
+ if (speechSynthesis.getVoices().length > 0) {
1238
+ loadVoices();
1239
+ }
1240
+ } else {
1241
+ // Disable speech features if not supported
1242
+ speakToggleBtn.disabled = true;
1243
+ speakToggleBtn.title = 'Text-to-speech not supported in your browser';
1244
+ }
1245
+
1246
+ // Find & Replace event listeners
1247
+ findNextBtn.addEventListener('click', function() {
1248
+ playClickSound();
1249
+ findText('next');
1250
+ });
1251
+
1252
+ findPrevBtn.addEventListener('click', function() {
1253
+ playClickSound();
1254
+ findText('prev');
1255
+ });
1256
+
1257
+ replaceBtn.addEventListener('click', function() {
1258
+ playClickSound();
1259
+ replaceText();
1260
+ });
1261
+
1262
+ findInput.addEventListener('keydown', function(e) {
1263
+ if (e.key === 'Enter') {
1264
+ playClickSound();
1265
+ findText('next');
1266
+ }
1267
+ });
1268
+
1269
+ // Speech synthesis event listeners
1270
+ speakBtn.addEventListener('click', function() {
1271
+ playClickSound();
1272
+ speakText();
1273
+ });
1274
+
1275
+ pauseBtn.addEventListener('click', function() {
1276
+ playClickSound();
1277
+ pauseSpeech();
1278
+ });
1279
+
1280
+ stopBtn.addEventListener('click', function() {
1281
+ playClickSound();
1282
+ stopSpeech();
1283
+ });
1284
+
1285
+ rateControl.addEventListener('input', function() {
1286
+ rateValue.textContent = this.value;
1287
+ if (speechUtterance) {
1288
+ speechUtterance.rate = parseFloat(this.value);
1289
+ }
1290
+ });
1291
+
1292
+ pitchControl.addEventListener('input', function() {
1293
+ pitchValue.textContent = this.value;
1294
+ if (speechUtterance) {
1295
+ speechUtterance.pitch = parseFloat(this.value);
1296
+ }
1297
+ });
1298
+
1299
+ // Update counts when typing
1300
+ cleanTextArea.addEventListener('input', function() {
1301
+ updateCounts();
1302
+
1303
+ // Clear find highlights if text changes
1304
+ if (findMatches.length > 0) {
1305
+ clearHighlights();
1306
+ }
1307
+
1308
+ // Stop speech if text changes while speaking
1309
+ if (isSpeaking || isPaused) {
1310
+ stopSpeech();
1311
+ }
1312
+ });
1313
+
1314
+ // Handle direct paste into textarea
1315
+ cleanTextArea.addEventListener('paste', function(e) {
1316
+ playClickSound();
1317
+ // Let the paste happen first
1318
+ setTimeout(() => {
1319
+ updateCounts();
1320
+
1321
+ // Show feedback
1322
+ const originalPlaceholder = cleanTextArea.placeholder;
1323
+ cleanTextArea.placeholder = "✓ Text pasted!";
1324
+
1325
+ setTimeout(() => {
1326
+ cleanTextArea.placeholder = originalPlaceholder;
1327
+ }, 1500);
1328
+ }, 10);
1329
+ });
1330
+
1331
+ // Keyboard shortcuts
1332
+ document.addEventListener('keydown', function(e) {
1333
+ if ((e.ctrlKey || e.metaKey) && e.key === 'v' && document.activeElement !== cleanTextArea) {
1334
+ e.preventDefault();
1335
+ pasteBtn.click();
1336
+ }
1337
+
1338
+ if ((e.ctrlKey || e.metaKey) && e.key === 'c' && document.activeElement === cleanTextArea) {
1339
+ e.preventDefault();
1340
+ copyBtn.click();
1341
+ }
1342
+
1343
+ // Formatting shortcuts
1344
+ if ((e.ctrlKey || e.metaKey) && document.activeElement === cleanTextArea) {
1345
+ switch(e.key.toLowerCase()) {
1346
+ case 'b':
1347
+ e.preventDefault();
1348
+ boldBtn.click();
1349
+ break;
1350
+ case 'i':
1351
+ e.preventDefault();
1352
+ italicBtn.click();
1353
+ break;
1354
+ case 'u':
1355
+ e.preventDefault();
1356
+ underlineBtn.click();
1357
+ break;
1358
+ case 'l':
1359
+ e.preventDefault();
1360
+ lowercaseBtn.click();
1361
+ break;
1362
+ case 'u':
1363
+ if (e.shiftKey) {
1364
+ e.preventDefault();
1365
+
1366
+ uppercaseBtn.click();
1367
+ }
1368
+ break;
1369
+ case 'r':
1370
+ e.preventDefault();
1371
+ reverseBtn.click();
1372
+ break;
1373
+ case 'e':
1374
+ e.preventDefault();
1375
+ encodeBtn.click();
1376
+ break;
1377
+ case 'd':
1378
+ e.preventDefault();
1379
+ decodeBtn.click();
1380
+ break;
1381
+ case 'f':
1382
+ e.preventDefault();
1383
+ findReplaceBtn.click();
1384
+ break;
1385
+ case 'h':
1386
+ e.preventDefault();
1387
+ analyzeBtn.click();
1388
+ break;
1389
+ case 's':
1390
+ e.preventDefault();
1391
+ speakToggleBtn.click();
1392
+ break;
1393
+ }
1394
+ }
1395
+
1396
+ // Find next/previous with F3/Shift+F3
1397
+ if (e.key === 'F3') {
1398
+ e.preventDefault();
1399
+ if (findReplacePanel.classList.contains('open')) {
1400
+ if (e.shiftKey) {
1401
+ findPrevBtn.click();
1402
+ } else {
1403
+ findNextBtn.click();
1404
+ }
1405
+ }
1406
+ }
1407
+ });
1408
+
1409
+ // Initialize counts
1410
+ updateCounts();
1411
+ });
1412
+ </script>
1413
+ <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=C50BARZ/clean-paste-10" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
1414
+ </html>