web-in-python-lol 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,2 @@
1
+ from Engine import core
2
+
@@ -0,0 +1,453 @@
1
+ import sqlite3, re, json, traceback, threading, webbrowser
2
+ from http.server import HTTPServer, BaseHTTPRequestHandler
3
+ from urllib.parse import parse_qs, urlparse
4
+
5
+ # --- DATABASE LAYER ---
6
+ class Database:
7
+ def __init__(self, db_name="system_data.db"):
8
+ self.db_name = db_name
9
+ self.lock = threading.Lock()
10
+ self._init_db()
11
+
12
+ def _init_db(self):
13
+ with self.lock:
14
+ with sqlite3.connect(self.db_name, check_same_thread=False) as conn:
15
+ conn.execute("CREATE TABLE IF NOT EXISTS storage (key TEXT PRIMARY KEY, value TEXT, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP)")
16
+
17
+ def save(self, key, data):
18
+ with self.lock:
19
+ with sqlite3.connect(self.db_name, check_same_thread=False) as conn:
20
+ conn.execute("INSERT OR REPLACE INTO storage (key, value, updated_at) VALUES (?, ?, CURRENT_TIMESTAMP)", (key, json.dumps(data)))
21
+
22
+ def load(self, key, default=None):
23
+ with self.lock:
24
+ with sqlite3.connect(self.db_name, check_same_thread=False) as conn:
25
+ row = conn.execute("SELECT value FROM storage WHERE key = ?", (key,)).fetchone()
26
+ return json.loads(row[0]) if row else default
27
+
28
+ def get_last_update(self):
29
+ with self.lock:
30
+ with sqlite3.connect(self.db_name, check_same_thread=False) as conn:
31
+ res = conn.execute("SELECT MAX(updated_at) FROM storage").fetchone()
32
+ return res[0] if res and res[0] else "0"
33
+
34
+ # --- CORE ENGINE ---
35
+ class ShadowEngine(BaseHTTPRequestHandler):
36
+ def do_GET(self):
37
+ if self.path == '/__poll__':
38
+ self.send_response(200); self.end_headers()
39
+ self.wfile.write(str(self.server.app_instance.db.get_last_update()).encode())
40
+ return
41
+ self.handle_request()
42
+
43
+ def do_POST(self):
44
+ length = int(self.headers.get('Content-Length', 0))
45
+ params = {k: v[0] for k, v in parse_qs(self.rfile.read(length).decode()).items()}
46
+ func, args = self.find_route(urlparse(self.path).path)
47
+ if func: func(self.server.app_instance, params, *args, is_post=True)
48
+ self.send_response(303); self.send_header('Location', self.headers.get('Referer', '/')); self.end_headers()
49
+
50
+ def handle_request(self):
51
+ func, args = self.find_route(urlparse(self.path).path)
52
+ params = {k: v[0] for k, v in parse_qs(urlparse(self.path).query).items()}
53
+ self.send_response(200); self.send_header("Content-type", "text/html"); self.end_headers()
54
+ try:
55
+ content = func(self.server.app_instance, params, *args) if func else "<h1>404</h1>"
56
+ except:
57
+ content = f"<pre style='color:red;'>{traceback.format_exc()}</pre>"
58
+ self.wfile.write(self.server.app_instance._wrap(content).encode())
59
+
60
+ def find_route(self, path):
61
+ for pattern, func in self.server.app_instance.routes:
62
+ match = pattern.match(path)
63
+ if match: return func, match.groups()
64
+ return None, []
65
+
66
+ class WebApp:
67
+ def __init__(self, name="ShadowUI"):
68
+ self.name = name
69
+ self.db = Database()
70
+ self.routes = []
71
+
72
+ def store(self, k, v): self.db.save(k, v)
73
+ def fetch(self, k, d=None): return self.db.load(k, d)
74
+ def page(self, path):
75
+ def d(f): self.routes.append((re.compile(f"^{path}$"), f)); return f
76
+ return d
77
+
78
+ def _wrap(self, content):
79
+ return f"""
80
+ <html>
81
+ <head>
82
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
83
+ <script src="https://unpkg.com/lucide@latest"></script>
84
+ <style>
85
+ body {{ margin:0; background:#0e1117; color:#fff; font-family:sans-serif; overflow-x: hidden; }}
86
+ * {{ box-sizing:border-box; }}
87
+
88
+ .nav-links {{ display: flex; align-items: center; }}
89
+ .menu-toggle {{
90
+ display: none; cursor: pointer; color: #6366f1;
91
+ background: none; border: none; padding: 5px;
92
+ }}
93
+ /* Fix icon centering */
94
+ .menu-toggle i {{ display: block; }}
95
+
96
+ @media (max-width: 768px) {{
97
+ .nav-links {{
98
+ display: none; flex-direction: column; width: 100%;
99
+ position: absolute; top: 60px; left: 0;
100
+ background: #000; padding: 20px; border-bottom: 1px solid #333;
101
+ }}
102
+ .nav-links.active {{ display: flex; }}
103
+ .nav-links a {{ margin: 10px 0 !important; width: 100%; text-align: center; }}
104
+ .menu-toggle {{ display: block; }}
105
+ }}
106
+ div[style*="max-width:1000px"] {{ max-width: 100% !important; padding: 20px !important; }}
107
+ </style>
108
+ <script>
109
+ function toggleMenu() {{
110
+ const nav = document.getElementById('mobile-nav');
111
+ nav.classList.toggle('active');
112
+ }}
113
+ // Initialize icons after page load
114
+ window.onload = () => lucide.createIcons();
115
+
116
+ let last=null;
117
+ setInterval(async()=>{{
118
+ let r=await fetch('/__poll__');
119
+ let t=await r.text();
120
+ if(last&&t!==last)location.reload();
121
+ last=t;
122
+ }},1500);
123
+ </script>
124
+ </head>
125
+ <body>{content}</body>
126
+ </html>"""
127
+
128
+ def start(self, port=8080):
129
+ srv = HTTPServer(("0.0.0.0", port), ShadowEngine)
130
+ srv.app_instance = self
131
+ print(f"Running on port {port}"); srv.serve_forever()
132
+
133
+
134
+
135
+ class Component:
136
+ def __init__(self, style=None):
137
+ self._styles = style or {}
138
+
139
+ def css(self):
140
+ return "; ".join([f"{k.replace('_', '-')}:{v}" for k, v in self._styles.items()])
141
+
142
+ # --- HELPER ---
143
+ def set_style(self, prop, val):
144
+ self._styles[prop] = val
145
+ return self
146
+ def on_click(self, js_code):
147
+ return self.set_style("onclick", js_code)
148
+
149
+ # --- 1. LAYOUT & FLEXBOX (The most used) ---
150
+ def display(self, v): return self.set_style('display', v)
151
+ def flex(self, v): return self.set_style('flex', v)
152
+ def flex_direction(self, v): return self.set_style('flex-direction', v)
153
+ def justify_content(self, v): return self.set_style('justify-content', v)
154
+ def align_items(self, v): return self.set_style('align-items', v)
155
+ def gap(self, v): return self.set_style('gap', v)
156
+ def flex_wrap(self, v): return self.set_style('flex-wrap', v)
157
+ def flex_grow(self, v): return self.set_style('flex-grow', v)
158
+ def order(self, v): return self.set_style('order', v)
159
+
160
+ # --- 2. SPACING ---
161
+ def padding(self, v): return self.set_style('padding', v)
162
+ def p_top(self, v): return self.set_style('padding-top', v)
163
+ def p_bottom(self, v): return self.set_style('padding-bottom', v)
164
+ def p_left(self, v): return self.set_style('padding-left', v)
165
+ def p_right(self, v): return self.set_style('padding-right', v)
166
+ def margin(self, v): return self.set_style('margin', v)
167
+ def m_top(self, v): return self.set_style('margin-top', v)
168
+ def m_bottom(self, v): return self.set_style('margin-bottom', v)
169
+ def m_left(self, v): return self.set_style('margin-left', v)
170
+ def m_right(self, v): return self.set_style('margin-right', v)
171
+
172
+ # --- 3. SIZING ---
173
+ def width(self, v): return self.set_style('width', v)
174
+ def min_width(self, v): return self.set_style('min-width', v)
175
+ def max_width(self, v): return self.set_style('max-width', v)
176
+ def height(self, v): return self.set_style('height', v)
177
+ def min_height(self, v): return self.set_style('min-height', v)
178
+ def max_height(self, v): return self.set_style('max-height', v)
179
+
180
+ # --- 4. COLOR & BACKGROUND ---
181
+ def bg(self, v): return self.set_style('background', v)
182
+ def bg_color(self, v): return self.set_style('background-color', v)
183
+ def bg_image(self, v): return self.set_style('background-image', v)
184
+ def bg_size(self, v): return self.set_style('background-size', v)
185
+ def color(self, v): return self.set_style('color', v)
186
+ def opacity(self, v): return self.set_style('opacity', v)
187
+
188
+ # --- 5. BORDERS & RADIUS ---
189
+ def border(self, v): return self.set_style('border', v)
190
+ def border_top(self, v): return self.set_style('border-top', v)
191
+ def border_bottom(self, v): return self.set_style('border-bottom', v)
192
+ def border_left(self, v): return self.set_style('border-left', v)
193
+ def border_right(self, v): return self.set_style('border-right', v)
194
+ def border_color(self, v): return self.set_style('border-color', v)
195
+ def border_style(self, v): return self.set_style('border-style', v)
196
+ def radius(self, v): return self.set_style('border-radius', v)
197
+ def r_top_left(self, v): return self.set_style('border-top-left-radius', v)
198
+ def r_top_right(self, v): return self.set_style('border-top-right-radius', v)
199
+ def outline(self, v): return self.set_style('outline', v)
200
+
201
+ # --- 6. TYPOGRAPHY ---
202
+ def font_size(self, v): return self.set_style('font-size', v)
203
+ def weight(self, v): return self.set_style('font-weight', v)
204
+ def font_family(self, v): return self.set_style('font-family', v)
205
+ def align(self, v): return self.set_style('text-align', v)
206
+ def decoration(self, v): return self.set_style('text-decoration', v)
207
+ def transform(self, v): return self.set_style('text-transform', v)
208
+ def line_height(self, v): return self.set_style('line-height', v)
209
+ def letter_spacing(self, v): return self.set_style('letter-spacing', v)
210
+ def white_space(self, v): return self.set_style('white-space', v)
211
+
212
+ # --- 7. POSITIONING ---
213
+ def position(self, v): return self.set_style('position', v)
214
+ def top(self, v): return self.set_style('top', v)
215
+ def bottom(self, v): return self.set_style('bottom', v)
216
+ def left(self, v): return self.set_style('left', v)
217
+ def right(self, v): return self.set_style('right', v)
218
+ def z_index(self, v): return self.set_style('z-index', v)
219
+ def overflow(self, v): return self.set_style('overflow', v)
220
+
221
+ # --- 8. EFFECTS & TRANSITIONS ---
222
+ def shadow(self, v): return self.set_style('box-shadow', v)
223
+ def transition(self, v): return self.set_style('transition', v)
224
+ def cursor(self, v): return self.set_style('cursor', v)
225
+ def filter(self, v): return self.set_style('filter', v)
226
+ def blur(self, v): return self.set_style('backdrop-filter', f'blur({v})')
227
+ def transform_effect(self, v): return self.set_style('transform', v)
228
+
229
+ # --- 9. GRID SPECIFIC ---
230
+ def grid_cols(self, v): return self.set_style('grid-template-columns', v)
231
+ def grid_rows(self, v): return self.set_style('grid-template-rows', v)
232
+ def grid_area(self, v): return self.set_style('grid-area', v)
233
+
234
+ # --- 10. UTILITIES ---
235
+ def hide(self): return self.set_style('display', 'none')
236
+ def show(self): return self.set_style('display', 'block')
237
+ def pointer(self): return self.set_style('cursor', 'pointer')
238
+
239
+
240
+ def render(self): return ""
241
+
242
+ class Group(Component):
243
+ def __init__(self, items):
244
+ super().__init__()
245
+ self.items = items
246
+ def render(self):
247
+ return "".join([i.render() if hasattr(i, 'render') else str(i) for i in self.items])
248
+
249
+ class Container(Component):
250
+ def __init__(self, items):
251
+ super().__init__()
252
+ self.items = items
253
+ def render(self):
254
+ # Default container styles can still be overridden by chaining
255
+ style = {"max-width":"1000px", "margin":"0 auto", "padding":"40px"}
256
+ style.update(self._styles)
257
+ self._styles = style
258
+ return f'<div style="{self.css()}">{Group(self.items).render()}</div>'
259
+
260
+ class Card(Component):
261
+ def __init__(self, items):
262
+ super().__init__()
263
+ self.items = items
264
+ def render(self):
265
+ style = {"background":"#1a1d23", "padding":"20px", "border-radius":"12px", "border":"1px solid #333"}
266
+ style.update(self._styles)
267
+ self._styles = style
268
+ return f'<div style="{self.css()}">{Group(self.items).render()}</div>'
269
+
270
+ class Text(Component):
271
+ def __init__(self, text):
272
+ super().__init__()
273
+ self._content = text # Changed from self.text to avoid killing .text() method
274
+ def render(self):
275
+ return f'<div style="{self.css()}">{self._content}</div>'
276
+
277
+ class Button(Component):
278
+ def __init__(self, text, primary=True):
279
+ super().__init__()
280
+ self.text, self.p = text, primary
281
+
282
+ def render(self):
283
+ bg = "#6366f1" if self.p else "#333"
284
+ base = {"background":bg, "color":"#fff", "border":"none", "padding":"10px 20px", "border-radius":"8px", "cursor":"pointer", "font-weight":"bold"}
285
+ base.update(self._styles)
286
+
287
+ # --- NEW LOGIC: Separate JS Events from CSS ---
288
+ js_events = " ".join([f'{k}="{v}"' for k, v in base.items() if k.startswith("on")])
289
+ css_only = "; ".join([f"{k.replace('_', '-')}:{v}" for k, v in base.items() if not k.startswith("on")])
290
+
291
+ return f'<button {js_events} style="{css_only}">{self.text}</button>'
292
+
293
+ class Row(Component):
294
+ def __init__(self, items, gap="20px"):
295
+ super().__init__()
296
+ self.items = items
297
+ self._internal_gap = gap
298
+
299
+ def render(self):
300
+ g = self._styles.get('gap', self._internal_gap)
301
+ # Added flex-wrap: wrap
302
+ base = {"display": "flex", "flex-direction": "row", "flex-wrap": "wrap", "gap": g, "align-items": "center"}
303
+ base.update(self._styles)
304
+ self._styles = base
305
+ return f'<div style="{self.css()}">{Group(self.items).render()}</div>'
306
+
307
+ class TextInput(Component):
308
+ def __init__(self, label, name, placeholder="", type="text", value=""):
309
+ super().__init__()
310
+ self.label, self.name, self.ph, self.type, self.val = label, name, placeholder, type, value
311
+
312
+ def render(self):
313
+ base = {"width": "100%", "padding": "12px", "background": "#000", "border": "1px solid #333",
314
+ "color": "#fff", "border-radius": "8px", "outline": "none", "margin-top": "5px"}
315
+ base.update(self._styles)
316
+ self._styles = base
317
+ return f'''
318
+ <div style="margin-bottom:15px; width:100%;">
319
+ <label style="color:#888; font-size:12px; font-weight:bold;">{self.label}</label>
320
+ <input type="{self.type}" name="{self.name}" placeholder="{self.ph}" value="{self.val}" style="{self.css()}">
321
+ </div>'''
322
+
323
+ class Form(Component):
324
+ def __init__(self, action, items, method="POST"):
325
+ super().__init__()
326
+ self.action, self.items, self.method = action, items, method
327
+ def render(self):
328
+ # Forms usually just need standard padding or margins
329
+ return f'<form action="{self.action}" method="{self.method}" style="{self.css()}">{Group(self.items).render()}</form>'
330
+
331
+ class Column(Component):
332
+ def __init__(self, items, gap="10px"):
333
+ super().__init__()
334
+ self.items = items
335
+ self._internal_gap = gap # Changed from self.gap
336
+
337
+ def render(self):
338
+ # Allow the .gap() method to override the default constructor gap
339
+ g = self._styles.get('gap', self._internal_gap)
340
+ base = {"display": "flex", "flex-direction": "column", "gap": g}
341
+ base.update(self._styles)
342
+ self._styles = base
343
+ return f'<div style="{self.css()}">{Group(self.items).render()}</div>'
344
+
345
+ class Grid(Component):
346
+ def __init__(self, items, columns=3, gap="20px", min_width="250px"):
347
+ super().__init__()
348
+ self.items, self.cols, self.gap, self.min_w = items, columns, gap, min_width
349
+
350
+ def render(self):
351
+ # Instead of fixed columns, we use repeat(auto-fit) for responsiveness
352
+ base = {
353
+ "display": "grid",
354
+ "grid-template-columns": f"repeat(auto-fit, minmax({self.min_w}, 1fr))",
355
+ "gap": self.gap
356
+ }
357
+ base.update(self._styles)
358
+ self._styles = base
359
+ return f'<div style="{self.css()}">{Group(self.items).render()}</div>'
360
+
361
+
362
+ class Navbar(Component):
363
+ def __init__(self, brand_name, links):
364
+ super().__init__()
365
+ self.brand, self.links = brand_name, links
366
+
367
+ def render(self):
368
+ base = {
369
+ "padding": "15px 40px", "background": "#000", "border-bottom": "1px solid #333",
370
+ "display": "flex", "justify-content": "space-between", "align-items": "center",
371
+ "position":"sticky", "top":"0", "z-index": "1000"
372
+ }
373
+ base.update(self._styles)
374
+ self._styles = base
375
+
376
+ btns = "".join([
377
+ f'<a href="{u}" style="color:#888; margin-left:20px; text-decoration:none; font-size:14px; font-weight:500;">{n}</a>'
378
+ for n, u in self.links
379
+ ])
380
+
381
+ return f'''
382
+ <nav style="{self.css()}">
383
+ <div style="font-weight:bold; font-size:20px; color:#6366f1;">{self.brand}</div>
384
+
385
+ <button class="menu-toggle" onclick="toggleMenu()">
386
+ <i data-lucide="menu"></i>
387
+ </button>
388
+
389
+ <div id="mobile-nav" class="nav-links">
390
+ {btns}
391
+ </div>
392
+ </nav>'''
393
+
394
+ class Image(Component):
395
+ def __init__(self, src, alt="image"):
396
+ super().__init__()
397
+ self.src, self.alt = src, alt
398
+ def render(self):
399
+ base = {"max-width": "100%", "height": "auto", "border-radius": "8px"}
400
+ base.update(self._styles)
401
+ self._styles = base
402
+ return f'<img src="{self.src}" alt="{self.alt}" style="{self.css()}">'
403
+
404
+
405
+ class Badge(Component):
406
+ def __init__(self, text, color="#6366f1"):
407
+ super().__init__()
408
+ self.text, self.color = text, color
409
+ def render(self):
410
+ base = {"background": f"{self.color}22", "color": self.color, "padding": "4px 10px",
411
+ "border-radius": "6px", "font-size": "11px", "font-weight": "bold", "display": "inline-block"}
412
+ base.update(self._styles)
413
+ self._styles = base
414
+ return f'<span style="{self.css()}">{self.text}</span>'
415
+
416
+ class Spacer(Component):
417
+ def __init__(self, h="20px", w="20px"):
418
+ super().__init__()
419
+ self.h, self.w = h, w
420
+ def render(self):
421
+ return f'<div style="height:{self.h}; width:{self.w};"></div>'
422
+
423
+ class Icon(Component):
424
+ def __init__(self, name, size=20, color="currentColor"):
425
+ super().__init__()
426
+ self.name, self.size, self.color = name, size, color
427
+ def render(self):
428
+ return f'<i data-lucide="{self.name}" style="width:{self.size}px; height:{self.size}px; color:{self.color}; {self.css()}"></i>'
429
+
430
+
431
+
432
+
433
+
434
+
435
+
436
+
437
+
438
+
439
+
440
+
441
+
442
+
443
+
444
+
445
+
446
+
447
+
448
+
449
+
450
+
451
+
452
+
453
+
@@ -0,0 +1,10 @@
1
+ from setuptools import setup, find_packages
2
+
3
+ setup(
4
+ name="web-in-python-lol",
5
+ version="0.1.0",
6
+ description="A lightweight Python-only UI engine for rapid dashboards",
7
+ author="Basel Ezzat",
8
+ packages=find_packages(),
9
+ install_requires=[], # Since you used only standard libs, keep this empty!
10
+ )
@@ -0,0 +1,10 @@
1
+ Metadata-Version: 2.4
2
+ Name: web-in-python-lol
3
+ Version: 0.1.0
4
+ Summary: A responsive, Python-only UI engine for rapid dashboards
5
+ Author-email: Basel Ezzat <lmcteam206@gmail.com>
6
+ Classifier: Programming Language :: Python :: 3
7
+ Classifier: License :: OSI Approved :: MIT License
8
+ Classifier: Operating System :: OS Independent
9
+ Requires-Python: >=3.7
10
+ Description-Content-Type: text/markdown
@@ -0,0 +1,21 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "web-in-python-lol"
7
+ version = "0.1.0"
8
+ authors = [
9
+ { name="Basel Ezzat", email="lmcteam206@gmail.com" },
10
+ ]
11
+ description = "A responsive, Python-only UI engine for rapid dashboards"
12
+ readme = "README.md"
13
+ requires-python = ">=3.7"
14
+ classifiers = [
15
+ "Programming Language :: Python :: 3",
16
+ "License :: OSI Approved :: MIT License",
17
+ "Operating System :: OS Independent",
18
+ ]
19
+
20
+ [tool.setuptools.packages.find]
21
+ where = ["."]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,10 @@
1
+ Metadata-Version: 2.4
2
+ Name: web-in-python-lol
3
+ Version: 0.1.0
4
+ Summary: A responsive, Python-only UI engine for rapid dashboards
5
+ Author-email: Basel Ezzat <lmcteam206@gmail.com>
6
+ Classifier: Programming Language :: Python :: 3
7
+ Classifier: License :: OSI Approved :: MIT License
8
+ Classifier: Operating System :: OS Independent
9
+ Requires-Python: >=3.7
10
+ Description-Content-Type: text/markdown
@@ -0,0 +1,8 @@
1
+ pyproject.toml
2
+ Engine/__init__.py
3
+ Engine/core.py
4
+ Engine/setup.py
5
+ web_in_python_lol.egg-info/PKG-INFO
6
+ web_in_python_lol.egg-info/SOURCES.txt
7
+ web_in_python_lol.egg-info/dependency_links.txt
8
+ web_in_python_lol.egg-info/top_level.txt