lexicalspace commited on
Commit
ddb1ac3
·
verified ·
1 Parent(s): 39655da

Upload app (4).py

Browse files
Files changed (1) hide show
  1. app (4).py +918 -0
app (4).py ADDED
@@ -0,0 +1,918 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import requests
3
+ from PIL import Image, ImageDraw, ImageFont
4
+ import io
5
+ import time
6
+ import json
7
+ import base64
8
+ import uuid
9
+ import hashlib
10
+ import urllib.parse
11
+ import random
12
+ import datetime
13
+ import re
14
+ from collections import Counter
15
+ from bs4 import BeautifulSoup
16
+
17
+
18
+
19
+ # --- BATCH 1: MEDIA & FILE FUNCTIONS ---
20
+
21
+ def tool_youtube_downloader():
22
+ st.header("🎥 YouTube Media Extractor")
23
+ url = st.text_input("Paste YouTube URL", placeholder="https://youtube.com/...")
24
+ format_type = st.radio("Format", ["Video (MP4)", "Audio Only (MP3)"], horizontal=True)
25
+
26
+ if url and st.button("🚀 Process Media"):
27
+ with st.spinner("Contacting server..."):
28
+ try:
29
+ headers = {"Accept": "application/json", "Content-Type": "application/json"}
30
+ payload = {
31
+ "url": url,
32
+ "vQuality": "1080",
33
+ "isAudioOnly": True if "Audio" in format_type else False
34
+ }
35
+ # Using Cobalt API
36
+ response = requests.post("https://api.cobalt.tools/api/json", headers=headers, json=payload)
37
+ data = response.json()
38
+
39
+ if "url" in data:
40
+ st.success("✅ Ready!")
41
+ st.link_button(f"⬇️ Download {format_type}", data["url"])
42
+ if "Audio" not in format_type:
43
+ st.video(data["url"])
44
+ else:
45
+ st.audio(data["url"])
46
+ else:
47
+ st.error(f"Error: {data.get('text', 'Unknown error')}")
48
+ except Exception as e:
49
+ st.error(f"Connection failed: {str(e)}")
50
+
51
+ def tool_smart_converter():
52
+ with st.spinner("Starting File Engine..."):
53
+
54
+ time.sleep(0.3)
55
+
56
+
57
+ import pandas as pd
58
+ st.header("🔄 Smart File Converter")
59
+ st.info("Supports: Images (PNG/JPG/WEBP) and Data (CSV/JSON/Excel)")
60
+
61
+ uploaded_file = st.file_uploader("Upload File", type=['png', 'jpg', 'jpeg', 'webp', 'csv', 'json', 'xlsx'],key="smart_conv_upload")
62
+
63
+ if uploaded_file:
64
+ file_type = uploaded_file.name.split('.')[-1].lower()
65
+
66
+ # LOGIC: IMAGE CONVERSION
67
+ if file_type in ['png', 'jpg', 'jpeg', 'webp']:
68
+ image = Image.open(uploaded_file)
69
+ st.image(image, caption="Preview", width=300)
70
+ target_format = st.selectbox("Convert to:", ["PNG", "JPEG", "WEBP", "PDF"])
71
+
72
+ if st.button("Convert Image"):
73
+ buf = io.BytesIO()
74
+ # RGB required for JPEG/PDF
75
+ if image.mode in ("RGBA", "P") and target_format in ["JPEG", "PDF"]:
76
+ image = image.convert("RGB")
77
+
78
+ image.save(buf, format=target_format)
79
+ st.download_button(f"Download {target_format}", data=buf.getvalue(), file_name=f"converted.{target_format.lower()}")
80
+
81
+ # LOGIC: DATA CONVERSION
82
+ elif file_type in ['csv', 'json', 'xlsx']:
83
+ df = None
84
+ try:
85
+ if file_type == 'csv': df = pd.read_csv(uploaded_file)
86
+ elif file_type == 'json': df = pd.read_json(uploaded_file)
87
+ elif file_type == 'xlsx': df = pd.read_excel(uploaded_file)
88
+
89
+ st.write("Data Preview:", df.head())
90
+ target_data = st.selectbox("Convert to:", ["CSV", "JSON", "Excel"])
91
+
92
+ if st.button("Convert Data"):
93
+ buf = io.BytesIO()
94
+ if target_data == "CSV":
95
+ df.to_csv(buf, index=False)
96
+ ext = "csv"
97
+ elif target_data == "JSON":
98
+ df.to_json(buf, orient='records')
99
+ ext = "json"
100
+ elif target_data == "Excel":
101
+ df.to_excel(buf, index=False)
102
+ ext = "xlsx"
103
+
104
+ st.download_button(f"Download {target_data}", data=buf.getvalue(), file_name=f"converted.{ext}")
105
+ except Exception as e:
106
+ st.error(f"Error reading file: {e}")
107
+
108
+ def tool_image_compressor():
109
+ st.header("📉 Image Compressor")
110
+ # ADD key="compressor"
111
+ uploaded_file = st.file_uploader("Upload Image", type=['png', 'jpg', 'jpeg'], key="compressor")
112
+
113
+
114
+
115
+ if uploaded_file:
116
+ image = Image.open(uploaded_file)
117
+ st.write(f"Original Size: {uploaded_file.size / 1024:.2f} KB")
118
+ quality = st.slider("Quality (Lower = Smaller file)", 10, 95, 60)
119
+
120
+ if st.button("Compress"):
121
+ buf = io.BytesIO()
122
+ if image.mode in ("RGBA", "P"): image = image.convert("RGB")
123
+ image.save(buf, format="JPEG", quality=quality, optimize=True)
124
+
125
+ size_kb = len(buf.getvalue()) / 1024
126
+ st.success(f"Compressed Size: {size_kb:.2f} KB")
127
+ st.download_button("Download Compressed Image", data=buf.getvalue(), file_name="compressed.jpg")
128
+
129
+ def tool_image_resizer():
130
+ st.header("📐 Image Resizer")
131
+ # ADD key="resizer"
132
+ uploaded_file = st.file_uploader("Upload Image", type=['png', 'jpg', 'jpeg', 'webp'], key="resizer")
133
+
134
+
135
+
136
+ if uploaded_file:
137
+ image = Image.open(uploaded_file)
138
+ st.write(f"Current Dimensions: {image.size}")
139
+
140
+ col1, col2 = st.columns(2)
141
+ w = col1.number_input("Width", value=image.width)
142
+ h = col2.number_input("Height", value=image.height)
143
+
144
+ if st.button("Resize"):
145
+ new_img = image.resize((int(w), int(h)))
146
+ buf = io.BytesIO()
147
+ new_img.save(buf, format=image.format)
148
+ st.image(new_img, caption="Resized Preview")
149
+ st.download_button("Download Resized Image", data=buf.getvalue(), file_name=f"resized.{image.format.lower()}")
150
+
151
+ def tool_thumbnail_generator():
152
+ st.header("🚀 Ultimate Thumbnail Studio")
153
+
154
+ # --- DEPENDENCIES ---
155
+ from PIL import Image, ImageDraw, ImageFont, ImageFilter, ImageEnhance, ImageOps, ImageColor
156
+ import io
157
+
158
+ # --- LAYOUT FIX FOR PC/MOBILE ---
159
+ # We use a container for the controls to keep them tidy on PC
160
+ # and "use_column_width" to fix the crash.
161
+
162
+ # Top row: All Controls (Split into 2 columns for better PC spacing)
163
+ with st.expander("🎨 Thumbnail Settings & Controls", expanded=True):
164
+ col_c1, col_c2 = st.columns(2)
165
+
166
+ # --- LEFT CONTROL COLUMN ---
167
+ with col_c1:
168
+ st.subheader("1. Background & Filters")
169
+ bg_mode = st.radio("Background Type", ["Upload Image", "Solid Color", "Gradient"], horizontal=True)
170
+
171
+ bg_image = None
172
+ if bg_mode == "Upload Image":
173
+ bg_file = st.file_uploader("Upload BG", type=['png', 'jpg', 'jpeg', 'webp'], key="max_bg")
174
+ if bg_file: bg_image = Image.open(bg_file).convert("RGBA")
175
+ elif bg_mode == "Solid Color":
176
+ hex_bg = st.color_picker("Pick Color", "#1E1E1E")
177
+ st.session_state.thumb_bg_color = hex_bg
178
+ else: # Gradient
179
+ c1_col, c2_col = st.columns(2)
180
+ grad_c1 = c1_col.color_picker("Start", "#12c2e9")
181
+ grad_c2 = c2_col.color_picker("End", "#c471ed")
182
+ grad_dir = st.selectbox("Direction", ["Horizontal", "Vertical"])
183
+
184
+ st.markdown("---")
185
+ st.write("**Filters**")
186
+ f1, f2 = st.columns(2)
187
+ blur_amt = f1.slider("Blur", 0, 20, 0)
188
+ brightness = f2.slider("Brightness", 0.5, 1.5, 1.0)
189
+
190
+ # --- RIGHT CONTROL COLUMN ---
191
+ with col_c2:
192
+ st.subheader("2. Text & Branding")
193
+ main_text = st.text_area("Main Title", "THE ULTIMATE\nGUIDE TO PYTHON", height=100)
194
+
195
+ t1, t2 = st.columns(2)
196
+ font_size = t1.slider("Size", 20, 200, 80)
197
+ font_color = t2.color_picker("Text Color", "#FFFFFF")
198
+
199
+ st.write("**Styling**")
200
+ s1, s2 = st.columns(2)
201
+ stroke_width = s1.slider("Outline", 0, 10, 2)
202
+ stroke_color = s2.color_picker("Outline Color", "#000000")
203
+
204
+ st.markdown("---")
205
+ logo_file = st.file_uploader("Upload Logo (PNG)", type=['png', 'webp'], key="max_logo")
206
+ if logo_file:
207
+ l1, l2 = st.columns(2)
208
+ logo_size = l1.slider("Logo Size", 50, 300, 150)
209
+ logo_pos = l2.selectbox("Position", ["Top-Right", "Top-Left", "Bottom-Right", "Bottom-Left"])
210
+ else:
211
+ logo_size = 150
212
+ logo_pos = "Top-Right"
213
+
214
+ # --- PREVIEW SECTION (Full Width for PC clarity) ---
215
+ st.subheader("👁️ Preview & Download")
216
+
217
+ # GENERATION LOGIC
218
+ CANVAS_W, CANVAS_H = 1280, 720
219
+ canvas = Image.new("RGBA", (CANVAS_W, CANVAS_H), (0,0,0,0))
220
+
221
+ # 1. Background
222
+ if bg_mode == "Upload Image" and bg_image:
223
+ bg_ratio = bg_image.width / bg_image.height
224
+ target_ratio = CANVAS_W / CANVAS_H
225
+ if bg_ratio > target_ratio:
226
+ new_h = CANVAS_H
227
+ new_w = int(new_h * bg_ratio)
228
+ else:
229
+ new_w = CANVAS_W
230
+ new_h = int(new_w / bg_ratio)
231
+ bg_image = bg_image.resize((new_w, new_h), Image.Resampling.LANCZOS)
232
+ left = (new_w - CANVAS_W)/2
233
+ top = (new_h - CANVAS_H)/2
234
+ bg_image = bg_image.crop((left, top, left+CANVAS_W, top+CANVAS_H))
235
+ canvas.paste(bg_image, (0,0))
236
+ elif bg_mode == "Gradient":
237
+ base = Image.new('RGB', (CANVAS_W, CANVAS_H), grad_c1)
238
+ top_img = Image.new('RGB', (CANVAS_W, CANVAS_H), grad_c2)
239
+ mask = Image.new("L", (CANVAS_W, CANVAS_H))
240
+ mask_data = []
241
+ for y in range(CANVAS_H):
242
+ for x in range(CANVAS_W):
243
+ if grad_dir == "Vertical": mask_data.append(int(255 * (y / CANVAS_H)))
244
+ else: mask_data.append(int(255 * (x / CANVAS_W)))
245
+ mask.putdata(mask_data)
246
+ canvas = Image.composite(top_img, base, mask).convert("RGBA")
247
+ else:
248
+ if 'thumb_bg_color' not in st.session_state: st.session_state.thumb_bg_color = "#1E1E1E"
249
+ canvas = Image.new("RGBA", (CANVAS_W, CANVAS_H), st.session_state.thumb_bg_color)
250
+
251
+ # 2. Filters
252
+ if blur_amt > 0: canvas = canvas.filter(ImageFilter.GaussianBlur(blur_amt))
253
+ if brightness != 1.0:
254
+ enhancer = ImageEnhance.Brightness(canvas)
255
+ canvas = enhancer.enhance(brightness)
256
+
257
+ # 3. Text
258
+ txt_layer = Image.new("RGBA", canvas.size, (255,255,255,0))
259
+ draw = ImageDraw.Draw(txt_layer)
260
+
261
+ try:
262
+ font = ImageFont.truetype("arialbd.ttf", font_size)
263
+ except:
264
+ try:
265
+ font = ImageFont.truetype("arial.ttf", font_size)
266
+ except:
267
+ font = ImageFont.load_default()
268
+
269
+ # Center Text Calculation
270
+ # Using basic textsize for compatibility if textbbox fails in older Pillow
271
+ try:
272
+ bbox = draw.multiline_textbbox((0,0), main_text, font=font, align="center")
273
+ text_w = bbox[2] - bbox[0]
274
+ text_h = bbox[3] - bbox[1]
275
+ except:
276
+ # Fallback for very old Pillow versions
277
+ text_w, text_h = draw.textsize(main_text, font=font)
278
+
279
+ cx, cy = CANVAS_W // 2, CANVAS_H // 2
280
+ text_x = cx - (text_w // 2)
281
+ text_y = cy - (text_h // 2)
282
+
283
+ # Shadow
284
+ draw.multiline_text((text_x + 8, text_y + 8), main_text, font=font, align="center", fill="black")
285
+ # Main Text
286
+ draw.multiline_text((text_x, text_y), main_text, font=font, align="center", fill=font_color, stroke_width=stroke_width, stroke_fill=stroke_color)
287
+
288
+ # 4. Logo
289
+ if logo_file:
290
+ logo = Image.open(logo_file).convert("RGBA")
291
+ logo.thumbnail((logo_size, logo_size), Image.Resampling.LANCZOS)
292
+ pad = 30
293
+ if "Top" in logo_pos: ly = pad
294
+ elif "Bottom" in logo_pos: ly = CANVAS_H - logo.height - pad
295
+ if "Left" in logo_pos: lx = pad
296
+ elif "Right" in logo_pos: lx = CANVAS_W - logo.width - pad
297
+ txt_layer.paste(logo, (lx, ly), logo)
298
+
299
+ # Final Composite
300
+ final_comp = Image.alpha_composite(canvas, txt_layer)
301
+
302
+ # --- DISPLAY & DOWNLOAD ---
303
+ # Centered layout for PC look
304
+ col_show, col_down = st.columns([3, 1])
305
+
306
+ with col_show:
307
+ # FIX: Changed use_container_width to use_column_width
308
+ st.image(final_comp, caption="Result", use_column_width=True)
309
+
310
+ with col_down:
311
+ st.write("### Ready?")
312
+ buf = io.BytesIO()
313
+ final_comp.convert("RGB").save(buf, format="PNG")
314
+ st.download_button("💾 Download PNG", data=buf.getvalue(), file_name="thumbnail.png", mime="image/png")
315
+ # --- MAIN ROUTER (Paste this at the VERY END of app.py) ---
316
+ # This checks the URL params and decides which function to run
317
+ if __name__ == "__main__":
318
+ st.set_page_config(page_title="Lexical Space Tools", layout="centered")
319
+
320
+ # Get the 'mode' from the URL (e.g. ?mode=youtube)
321
+ params = st.query_params
322
+ mode = params.get("mode", "home")
323
+
324
+
325
+
326
+
327
+
328
+ # --- BATCH 2: WEBMASTER & SEO FUNCTIONS ---
329
+
330
+ def tool_meta_tag_generator():
331
+ st.header("🏷️ Meta Tag Generator")
332
+ title = st.text_input("Site Title", "My Awesome Blog")
333
+ desc = st.text_area("Description", "A blog about technology and coding.")
334
+ keywords = st.text_input("Keywords (comma separated)", "tech, coding, python")
335
+ author = st.text_input("Author", "Lexical Space")
336
+
337
+ if st.button("Generate Tags"):
338
+ code = f"""
339
+ <title>{title}</title>
340
+ <meta name="description" content="{desc}">
341
+ <meta name="keywords" content="{keywords}">
342
+ <meta name="author" content="{author}">
343
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
344
+ <meta property="og:type" content="website">
345
+ <meta property="og:title" content="{title}">
346
+ <meta property="og:description" content="{desc}">
347
+ """
348
+ st.code(code, language="html")
349
+
350
+ def tool_slug_generator():
351
+ st.header("🐌 URL Slug Generator")
352
+ text = st.text_input("Enter Post Title", "How to Install Python on Windows 10!")
353
+
354
+ if text:
355
+ # Lowercase, strip whitespace, replace spaces with dashes, remove non-alphanumeric
356
+ slug = text.lower().strip()
357
+ slug = re.sub(r'[^a-z0-9\s-]', '', slug)
358
+ slug = re.sub(r'[\s-]+', '-', slug)
359
+ st.success(f"Slug: {slug}")
360
+ st.code(slug, language="text")
361
+
362
+ def tool_robots_generator():
363
+ st.header("🤖 Robots.txt Generator")
364
+ st.write("Control which crawlers can access your site.")
365
+
366
+ all_agents = st.checkbox("Apply to all robots (*)", value=True)
367
+ disallow_admin = st.checkbox("Disallow /admin", value=True)
368
+ disallow_private = st.checkbox("Disallow /private", value=False)
369
+ sitemap_url = st.text_input("Sitemap URL (Optional)", "https://yoursite.com/sitemap.xml")
370
+
371
+ if st.button("Generate Robots.txt"):
372
+ agent = "*" if all_agents else "Googlebot"
373
+ txt = f"User-agent: {agent}\n"
374
+ if disallow_admin: txt += "Disallow: /admin/\n"
375
+ if disallow_private: txt += "Disallow: /private/\n"
376
+ if sitemap_url: txt += f"\nSitemap: {sitemap_url}"
377
+
378
+ st.text_area("Result", txt, height=150)
379
+
380
+ def tool_sitemap_builder():
381
+ st.header("🗺️ XML Sitemap Builder")
382
+ urls = st.text_area("Paste URLs (one per line)", "https://site.com\nhttps://site.com/about")
383
+
384
+ if st.button("Build XML"):
385
+ xml = '<?xml version="1.0" encoding="UTF-8"?>\n'
386
+ xml += '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">\n'
387
+
388
+ for url in urls.split('\n'):
389
+ if url.strip():
390
+ xml += f' <url>\n <loc>{url.strip()}</loc>\n <changefreq>monthly</changefreq>\n </url>\n'
391
+
392
+ xml += '</urlset>'
393
+ st.text_area("Sitemap.xml", xml, height=200)
394
+
395
+ def tool_keyword_density():
396
+ with st.spinner("Loading Analytics..."):
397
+ import pandas as pd
398
+ from collections import Counter
399
+
400
+
401
+ st.header("📊 Keyword Density Checker")
402
+ text = st.text_area("Paste Article Text", height=200,key="density_text")
403
+
404
+ if st.button("Analyze"):
405
+ # Simple stopword list to ignore
406
+ stopwords = set(['the', 'and', 'is', 'in', 'it', 'of', 'to', 'a', 'for', 'on', 'that', 'with', 'as'])
407
+
408
+ words = re.findall(r'\w+', text.lower())
409
+ filtered = [w for w in words if w not in stopwords and len(w) > 2]
410
+
411
+ counts = Counter(filtered).most_common(10)
412
+
413
+ df = pd.DataFrame(counts, columns=["Keyword", "Count"])
414
+ df['Density %'] = (df['Count'] / len(words) * 100).round(2)
415
+ st.table(df)
416
+
417
+ def tool_plagiarism_check():
418
+ st.header("🕵️ Plagiarism Scanner (Google Check)")
419
+ st.info("Splits text into sentences and searches Google for exact matches.")
420
+ text = st.text_area("Paste Text to Check", height=150,key="plag_text")
421
+
422
+ if st.button("Check Text"):
423
+ sentences = re.split(r'[.!?]', text)
424
+ clean_sentences = [s.strip() for s in sentences if len(s.strip()) > 20]
425
+
426
+ for i, s in enumerate(clean_sentences[:5]): # Limit to first 5 for demo
427
+ query = f'"{s}"'
428
+ url = f"https://www.google.com/search?q={query}"
429
+ st.markdown(f"**Sentence {i+1}:** {s[:50]}...")
430
+ st.link_button(f"🔍 Check Google for Match", url)
431
+
432
+ def tool_code_minifier():
433
+ st.header("🧹 Code Minifier")
434
+ mode = st.radio("Type", ["HTML", "CSS"])
435
+ raw_code = st.text_area("Input Code", height=200)
436
+
437
+ if st.button("Minify"):
438
+ minified = ""
439
+ if mode == "HTML":
440
+ # Basic whitespace removal between tags
441
+ lines = raw_code.split('\n')
442
+ minified = "".join([line.strip() for line in lines])
443
+ elif mode == "CSS":
444
+ # Remove comments and whitespace
445
+ # 1. Remove comments
446
+ minified = re.sub(r'/\*[\s\S]*?\*/', '', raw_code)
447
+ # 2. Remove whitespace around braces/colons
448
+ minified = re.sub(r'\s*([{:;,])\s*', r'\1', minified)
449
+ # 3. Remove newlines
450
+ minified = minified.replace('\n', '').replace('\r', '')
451
+
452
+ st.text_area("Minified Output", minified, height=200)
453
+ # --- BATCH 3: DEVELOPER TOOLS ---
454
+
455
+ def tool_qr_generator():
456
+ with st.spinner("Initializing QR Tool..."):
457
+ import qrcode
458
+ import io
459
+
460
+ st.header("🏁 QR Code Generator")
461
+ data = st.text_input("Enter Link or Text", "https://lexicalspace.blogspot.com")
462
+
463
+ if data:
464
+ qr = qrcode.QRCode(version=1, box_size=10, border=5)
465
+ qr.add_data(data)
466
+ qr.make(fit=True)
467
+ img = qr.make_image(fill='black', back_color='white')
468
+
469
+ buf = io.BytesIO()
470
+ img.save(buf)
471
+ st.image(img.get_image(), width=300)
472
+ st.download_button("Download QR", data=buf.getvalue(), file_name="qrcode.png")
473
+
474
+ def tool_json_formatter():
475
+ st.header("✨ JSON Prettifier")
476
+ raw = st.text_area("Paste Messy JSON", '{"id":1,"name":"Lexical","roles":["admin","dev"]}', height=150)
477
+
478
+ col1, col2 = st.columns(2)
479
+ if col1.button("Format (Pretty)"):
480
+ try:
481
+ parsed = json.loads(raw)
482
+ st.code(json.dumps(parsed, indent=4), language="json")
483
+ except Exception as e:
484
+ st.error(f"Invalid JSON: {e}")
485
+
486
+ if col2.button("Minify (Compact)"):
487
+ try:
488
+ parsed = json.loads(raw)
489
+ st.code(json.dumps(parsed, separators=(',', ':')), language="json")
490
+ except Exception as e:
491
+ st.error(f"Invalid JSON: {e}")
492
+
493
+ def tool_base64():
494
+ st.header("🔐 Base64 Converter")
495
+ mode = st.radio("Action", ["Encode", "Decode"], horizontal=True)
496
+ text = st.text_area("Input Text")
497
+
498
+ if st.button("Process"):
499
+ try:
500
+ if mode == "Encode":
501
+ res = base64.b64encode(text.encode()).decode()
502
+ else:
503
+ res = base64.b64decode(text).decode()
504
+ st.code(res)
505
+ except Exception as e:
506
+ st.error(f"Error: {e}")
507
+
508
+ def tool_url_encoder():
509
+ st.header("🔗 URL Encoder/Decoder")
510
+ text = st.text_input("Input URL", "https://example.com/search?q=hello world")
511
+
512
+ c1, c2 = st.columns(2)
513
+ with c1:
514
+ if st.button("Encode"):
515
+ st.code(urllib.parse.quote(text))
516
+ with c2:
517
+ if st.button("Decode"):
518
+ st.code(urllib.parse.unquote(text))
519
+
520
+ def tool_markdown_editor():
521
+ st.header("📝 Markdown Editor")
522
+
523
+ col1, col2 = st.columns(2)
524
+ with col1:
525
+ md_text = st.text_area("Write Markdown", "# Hello\n* Item 1\n* Item 2", height=400)
526
+
527
+ with col2:
528
+ st.markdown("### Preview")
529
+ st.markdown(md_text)
530
+
531
+ def tool_regex_tester():
532
+ st.header("🧪 Regex Tester")
533
+ pattern = st.text_input("Regex Pattern", r"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b")
534
+ text = st.text_area("Test String", "Contact us at support@lexical.com or admin@site.org")
535
+
536
+ if pattern and text:
537
+ try:
538
+ matches = re.findall(pattern, text)
539
+ st.write(f"Found {len(matches)} matches:")
540
+ st.json(matches)
541
+ except Exception as e:
542
+ st.error(f"Regex Error: {e}")
543
+
544
+ def tool_uuid_gen():
545
+ st.header("🆔 UUID/GUID Generator")
546
+ count = st.number_input("How many?", 1, 100, 5)
547
+
548
+ if st.button("Generate"):
549
+ uuids = [str(uuid.uuid4()) for _ in range(count)]
550
+ st.code("\n".join(uuids), language="text")
551
+
552
+ def tool_hash_gen():
553
+ st.header("🔑 Hash Generator")
554
+ text = st.text_input("Input String", "mypassword")
555
+
556
+ if text:
557
+ st.write("**MD5:**")
558
+ st.code(hashlib.md5(text.encode()).hexdigest())
559
+ st.write("**SHA256:**")
560
+ st.code(hashlib.sha256(text.encode()).hexdigest())
561
+ # ... (Batch 1 & 2 Routing above) ...
562
+
563
+ # BATCH 3 ROUTING
564
+ elif mode == "qrcode": tool_qr_generator()
565
+ elif mode == "json": tool_json_formatter()
566
+ elif mode == "base64": tool_base64()
567
+ elif mode == "url": tool_url_encoder()
568
+ elif mode == "markdown": tool_markdown_editor()
569
+ elif mode == "regex": tool_regex_tester()
570
+ elif mode == "uuid": tool_uuid_gen()
571
+ elif mode == "hash": tool_hash_gen()
572
+
573
+ # ... (Home Dashboard below) ...
574
+
575
+ st.write("### 🛠️ Developer Tools")
576
+ st.markdown("""
577
+ * [🏁 QR Code Gen](?mode=qrcode)
578
+ * [✨ JSON Prettifier](?mode=json)
579
+ * [🔐 Base64 Converter](?mode=base64)
580
+ * [🔗 URL Encode/Decode](?mode=url)
581
+ * [📝 Markdown Editor](?mode=markdown)
582
+ * [🧪 Regex Tester](?mode=regex)
583
+ * [🆔 UUID Generator](?mode=uuid)
584
+ * [🔑 Hash Generator](?mode=hash)
585
+ """)
586
+ # --- BATCH 4: TEXT, UTILITIES & EXTRAS ---
587
+
588
+ def tool_case_converter():
589
+ st.header("🔠 Case Converter")
590
+ text = st.text_area("Input Text", "hello world")
591
+
592
+ c1, c2, c3, c4 = st.columns(4)
593
+ if c1.button("UPPERCASE"): st.code(text.upper(), language="text")
594
+ if c2.button("lowercase"): st.code(text.lower(), language="text")
595
+ if c3.button("Title Case"): st.code(text.title(), language="text")
596
+ if c4.button("aLtErNaTiNg"):
597
+ res = "".join([c.upper() if i%2==0 else c.lower() for i, c in enumerate(text)])
598
+ st.code(res, language="text")
599
+
600
+ def tool_lorem_ipsum():
601
+ st.header("📜 Lorem Ipsum Generator")
602
+ paras = st.slider("Paragraphs", 1, 10, 3)
603
+
604
+ dummy_text = [
605
+ "Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
606
+ "Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
607
+ "Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris.",
608
+ "Duis aute irure dolor in reprehenderit in voluptate velit esse cillum.",
609
+ "Excepteur sint occaecat cupidatat non proident, sunt in culpa."
610
+ ]
611
+
612
+ if st.button("Generate"):
613
+ result = "\n\n".join([random.choice(dummy_text) * 3 for _ in range(paras)])
614
+ st.text_area("Result", result, height=200)
615
+
616
+ def tool_word_counter():
617
+ st.header("🧮 Word & Character Counter")
618
+ text = st.text_area("Paste Text Here", height=200,key="counter_text")
619
+
620
+ if text:
621
+ words = len(text.split())
622
+ chars = len(text)
623
+ no_space = len(text.replace(" ", ""))
624
+ read_time = round(words / 200, 2)
625
+
626
+ c1, c2, c3, c4 = st.columns(4)
627
+ c1.metric("Words", words)
628
+ c2.metric("Chars", chars)
629
+ c3.metric("No Spaces", no_space)
630
+ c4.metric("Read Time", f"{read_time} min")
631
+
632
+ def tool_remove_duplicates():
633
+ st.header("🗑️ Remove Duplicate Lines")
634
+ text = st.text_area("Paste List (One per line)", "Apple\nBanana\nApple\nOrange")
635
+
636
+ if st.button("Clean"):
637
+ lines = text.split('\n')
638
+ seen = set()
639
+ clean = []
640
+ for line in lines:
641
+ if line not in seen and line.strip():
642
+ clean.append(line)
643
+ seen.add(line)
644
+ st.text_area("Cleaned List", "\n".join(clean), height=200)
645
+
646
+ def tool_text_to_speech():
647
+ with st.spinner("Loading Audio Engine..."):
648
+ from gtts import gTTS
649
+ import io
650
+
651
+ st.header("🗣️ Text to Speech")
652
+ text = st.text_area("Enter Text", "Hello, welcome to Lexical Space.")
653
+ lang = st.selectbox("Language", ["en", "es", "fr", "de", "hi"],key="tts_text")
654
+
655
+ if st.button("Speak"):
656
+ try:
657
+ tts = gTTS(text=text, lang=lang, slow=False)
658
+ buf = io.BytesIO()
659
+ tts.write_to_fp(buf)
660
+ st.audio(buf, format='audio/mp3')
661
+ except Exception as e:
662
+ st.error(f"Error: {e}")
663
+
664
+ def tool_timestamp():
665
+ st.header("⏰ Unix Timestamp Converter")
666
+ now = int(time.time())
667
+ st.write(f"Current Timestamp: `{now}`")
668
+
669
+ col1, col2 = st.columns(2)
670
+ with col1:
671
+ ts_input = st.number_input("Timestamp to Date", value=now)
672
+ if st.button("Convert to Date"):
673
+ st.success(datetime.datetime.fromtimestamp(ts_input))
674
+
675
+ with col2:
676
+ d_input = st.date_input("Date to Timestamp")
677
+ if st.button("Convert to Timestamp"):
678
+ ts = int(time.mktime(d_input.timetuple()))
679
+ st.success(ts)
680
+
681
+ def tool_color_palette():
682
+ st.header("🎨 Image Color Palette")
683
+ # ADD key="palette"
684
+ uploaded_file = st.file_uploader("Upload Image", type=['jpg', 'png'], key="palette")
685
+
686
+
687
+
688
+ if uploaded_file:
689
+ img = Image.open(uploaded_file).convert("RGB")
690
+ st.image(img, width=200)
691
+
692
+ # Simple extraction by resizing to 5 pixels
693
+ small = img.resize((5, 1))
694
+ colors = small.getdata()
695
+
696
+ st.write("Dominant Colors:")
697
+ cols = st.columns(5)
698
+ for i, color in enumerate(colors):
699
+ hex_code = '#{:02x}{:02x}{:02x}'.format(*color)
700
+ cols[i].color_picker(f"Color {i+1}", hex_code, disabled=True)
701
+ cols[i].code(hex_code)
702
+
703
+ def tool_password_strength():
704
+ st.header("💪 Password Strength")
705
+ pwd = st.text_input("Test Password", type="password")
706
+
707
+ if pwd:
708
+ score = 0
709
+ if len(pwd) >= 8: score += 1
710
+ if re.search(r"[A-Z]", pwd): score += 1
711
+ if re.search(r"[a-z]", pwd): score += 1
712
+ if re.search(r"\d", pwd): score += 1
713
+ if re.search(r"[!@#$%^&*]", pwd): score += 1
714
+
715
+ st.progress(score / 5)
716
+ if score < 3: st.warning("Weak")
717
+ elif score < 5: st.info("Moderate")
718
+ else: st.success("Strong!")
719
+
720
+ def tool_aspect_ratio():
721
+ st.header("🖥️ Aspect Ratio Calculator")
722
+ w = st.number_input("Width", 1920)
723
+ h = st.number_input("Height", 1080)
724
+
725
+ if w and h:
726
+ def gcd(a, b):
727
+ while b: a, b = b, a % b
728
+ return a
729
+ divisor = gcd(int(w), int(h))
730
+ st.metric("Aspect Ratio", f"{int(w/divisor)}:{int(h/divisor)}")
731
+
732
+ def tool_stopwatch():
733
+ st.header("⏱️ Stopwatch")
734
+ if 'start_time' not in st.session_state: st.session_state.start_time = None
735
+
736
+ if st.button("Start/Reset"):
737
+ st.session_state.start_time = time.time()
738
+
739
+ if st.session_state.start_time:
740
+ elapsed = time.time() - st.session_state.start_time
741
+ st.metric("Time Elapsed", f"{elapsed:.2f}s")
742
+ if st.button("Stop"):
743
+ st.session_state.start_time = None
744
+
745
+
746
+ def tool_python_checker():
747
+ st.header("🐍 Python Syntax & Error Checker")
748
+ st.markdown("Paste your Python code or upload a `.py` file to check for syntax errors.")
749
+
750
+ # Import dependencies inside the function to avoid global scope clutter
751
+ import tempfile
752
+ import os
753
+ import io
754
+ try:
755
+ from pylint.lint import Run
756
+ from pylint.reporters.text import TextReporter
757
+ except ImportError:
758
+ st.error("⚠️ Pylint is not installed. Please add `pylint` to your requirements.txt")
759
+ return
760
+
761
+ # --- INPUTS ---
762
+ col1, col2 = st.columns(2)
763
+ with col1:
764
+ paste_code = st.text_area("Paste Code Here", height=300)
765
+ with col2:
766
+ file_obj = st.file_uploader("Or Upload .py File", type=[".py"])
767
+
768
+ # --- PROCESS BUTTON ---
769
+ if st.button("Check Syntax 🚀", type="primary"):
770
+ code_to_check = ""
771
+
772
+ # Logic: Prefer File > Paste
773
+ if file_obj is not None:
774
+ try:
775
+ code_to_check = file_obj.getvalue().decode("utf-8")
776
+ except Exception as e:
777
+ st.error(f"❌ Error reading file: {str(e)}")
778
+ return
779
+ elif paste_code.strip() != "":
780
+ code_to_check = paste_code
781
+ else:
782
+ st.warning("⚠️ Please either paste code or upload a file.")
783
+ return
784
+
785
+ # --- LINTING LOGIC ---
786
+ # 1. Create Temp File (Pylint needs a file on disk)
787
+ with tempfile.NamedTemporaryFile(delete=False, suffix=".py", mode='w', encoding='utf-8') as temp:
788
+ temp.write(code_to_check)
789
+ temp_path = temp.name
790
+
791
+ # 2. Run Pylint
792
+ pylint_output = io.StringIO()
793
+ reporter = TextReporter(pylint_output)
794
+
795
+ with st.spinner("Analyzing syntax..."):
796
+ try:
797
+ # --errors-only hides warnings, showing only code-breaking errors
798
+ Run([temp_path, "--errors-only"], reporter=reporter, exit=False)
799
+ except Exception as e:
800
+ st.error(f"System Error: {e}")
801
+
802
+ # 3. Cleanup & Display
803
+ os.unlink(temp_path) # Delete temp file
804
+ result = pylint_output.getvalue()
805
+
806
+ st.markdown("---")
807
+ if not result:
808
+ st.success("✅ No Syntax Errors Found! (Code looks valid)")
809
+ st.balloons()
810
+ else:
811
+ st.error("❌ Errors Found:")
812
+ # Hide the messy temp file path from the user
813
+ clean_report = result.replace(temp_path, "Your_Script.py")
814
+ st.code(clean_report, language="text")
815
+
816
+
817
+ # --- FINAL MAIN ROUTER (SPA VERSION) ---
818
+ if __name__ == "__main__":
819
+ # 1. INITIALIZE SESSION STATE (This replaces the URL logic)
820
+ if 'mode' not in st.session_state:
821
+ st.session_state.mode = 'home'
822
+
823
+
824
+ # 3. NAVIGATION HANDLING
825
+ # Function to change mode without URL reload
826
+ def set_mode(new_mode):
827
+ st.session_state.mode = new_mode
828
+
829
+ # Show "Back to Dashboard" button if not home
830
+ if st.session_state.mode != 'home':
831
+ if st.button("⬅️ Back to Grid"):
832
+ set_mode('home')
833
+ st.rerun()
834
+
835
+ # 4. TOOL ROUTING (Checks session_state instead of URL)
836
+ mode = st.session_state.mode
837
+
838
+ if mode == "youtube": tool_youtube_downloader()
839
+ elif mode == "smart_converter": tool_smart_converter()
840
+ elif mode == "compressor": tool_image_compressor()
841
+ elif mode == "resizer": tool_image_resizer()
842
+ elif mode == "thumbnail": tool_thumbnail_generator()
843
+ elif mode == "metatags": tool_meta_tag_generator()
844
+ elif mode == "slug": tool_slug_generator()
845
+ elif mode == "robots": tool_robots_generator()
846
+ elif mode == "sitemap": tool_sitemap_builder()
847
+ elif mode == "density": tool_keyword_density()
848
+ elif mode == "plagiarism": tool_plagiarism_check()
849
+ elif mode == "minify": tool_code_minifier()
850
+ elif mode == "qrcode": tool_qr_generator()
851
+ elif mode == "json": tool_json_formatter()
852
+ elif mode == "base64": tool_base64()
853
+ elif mode == "url": tool_url_encoder()
854
+ elif mode == "markdown": tool_markdown_editor()
855
+ elif mode == "regex": tool_regex_tester()
856
+ elif mode == "uuid": tool_uuid_gen()
857
+ elif mode == "hash": tool_hash_gen()
858
+ elif mode == "case": tool_case_converter()
859
+ elif mode == "lorem": tool_lorem_ipsum()
860
+ elif mode == "counter": tool_word_counter()
861
+ elif mode == "dedupe": tool_remove_duplicates()
862
+ elif mode == "tts": tool_text_to_speech()
863
+ elif mode == "timestamp": tool_timestamp()
864
+ elif mode == "palette": tool_color_palette()
865
+ elif mode == "password": tool_password_strength()
866
+ elif mode == "ratio": tool_aspect_ratio()
867
+ elif mode == "stopwatch": tool_stopwatch()
868
+ elif mode == "python": tool_python_checker()
869
+
870
+ # 5. HOME DASHBOARD (Button Grid)
871
+ else:
872
+ st.write("### ⚡ Select a tool to get started:")
873
+
874
+ # We use standard Streamlit columns to create a grid layout
875
+ # This replaces the Markdown links with actual Buttons
876
+
877
+ c1, c2 = st.columns(2)
878
+
879
+ with c1:
880
+ st.info("**📂 Media & Files**")
881
+ if st.button("🎥 YouTube Downloader"): set_mode("youtube"); st.rerun()
882
+ if st.button("🔄 Smart File Converter"): set_mode("smart_converter"); st.rerun()
883
+ if st.button("📉 Image Compressor"): set_mode("compressor"); st.rerun()
884
+ if st.button("📐 Image Resizer"): set_mode("resizer"); st.rerun()
885
+ if st.button("🖼️ Thumbnail Gen"): set_mode("thumbnail"); st.rerun()
886
+
887
+ st.info("**🛠️ Developer Tools**")
888
+ if st.button("🏁 QR Code Gen"): set_mode("qrcode"); st.rerun()
889
+ if st.button("✨ JSON Prettifier"): set_mode("json"); st.rerun()
890
+ if st.button("🔐 Base64 Converter"): set_mode("base64"); st.rerun()
891
+ if st.button("🔗 URL Encoder"): set_mode("url"); st.rerun()
892
+ if st.button("📝 Markdown Editor"): set_mode("markdown"); st.rerun()
893
+ if st.button("🧪 Regex Tester"): set_mode("regex"); st.rerun()
894
+ if st.button("🆔 UUID Gen"): set_mode("uuid"); st.rerun()
895
+ if st.button("🔑 Hash Gen"): set_mode("hash"); st.rerun()
896
+
897
+ with c2:
898
+ st.info("**🕸️ SEO & Webmaster**")
899
+ if st.button("🏷️ Meta Tag Gen"): set_mode("metatags"); st.rerun()
900
+ if st.button("🐌 Slug Generator"): set_mode("slug"); st.rerun()
901
+ if st.button("🤖 Robots.txt Gen"): set_mode("robots"); st.rerun()
902
+ if st.button("🗺️ Sitemap Builder"): set_mode("sitemap"); st.rerun()
903
+ if st.button("📊 Density Checker"): set_mode("density"); st.rerun()
904
+ if st.button("🕵️ Plagiarism Check"): set_mode("plagiarism"); st.rerun()
905
+ if st.button("🧹 Code Minifier"): set_mode("minify"); st.rerun()
906
+
907
+ st.info("**📝 Text & Utilities**")
908
+ if st.button("🔠 Case Converter"): set_mode("case"); st.rerun()
909
+ if st.button("📜 Lorem Ipsum"): set_mode("lorem"); st.rerun()
910
+ if st.button("🧮 Word Counter"): set_mode("counter"); st.rerun()
911
+ if st.button("🗑️ Dedupe Lines"): set_mode("dedupe"); st.rerun()
912
+ if st.button("🗣️ Text to Speech"): set_mode("tts"); st.rerun()
913
+ if st.button("⏰ Unix Timestamp"): set_mode("timestamp"); st.rerun()
914
+ if st.button("🎨 Color Palette"): set_mode("palette"); st.rerun()
915
+ if st.button("💪 Password Strength"): set_mode("password"); st.rerun()
916
+ if st.button("🖥️ Aspect Ratio"): set_mode("ratio"); st.rerun()
917
+ if st.button("⏱️ Stopwatch"): set_mode("stopwatch"); st.rerun()
918
+ if st.button("Python Checker"): set_mode("python"); st.rerun()