claide-web 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,8 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *.egg-info/
4
+ build/
5
+ dist/
6
+ .venv/
7
+ venv/
8
+ .pytest_cache/
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Kurodaki
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,230 @@
1
+ Metadata-Version: 2.5
2
+ Name: claide-web
3
+ Version: 0.1.0
4
+ Summary: Build complete websites in Python: pages, routes, forms, APIs, sessions and Uiverse.io components.
5
+ Project-URL: Homepage, https://github.com/kurodaki-dev/Claide
6
+ Project-URL: Issues, https://github.com/kurodaki-dev/Claide/issues
7
+ Author: kurodaki-dev
8
+ License-Expression: MIT
9
+ License-File: LICENSE
10
+ Keywords: css,framework,html,uiverse,web,website,wsgi
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Natural Language :: English
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3 :: Only
17
+ Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
18
+ Classifier: Topic :: Internet :: WWW/HTTP :: WSGI :: Application
19
+ Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
20
+ Requires-Python: >=3.9
21
+ Provides-Extra: dev
22
+ Requires-Dist: pytest>=7; extra == 'dev'
23
+ Description-Content-Type: text/markdown
24
+
25
+ # Claide ✦
26
+
27
+ **Build complete websites in Python, from A to Z**: HTML pages, dynamic routes,
28
+ forms, file uploads, JSON APIs, sessions, your own `style.css` / `script.js`, and
29
+ the thousands of components from [cssbuttons.io](https://cssbuttons.io) / [Uiverse.io](https://uiverse.io)
30
+ in a single line. Zero dependencies.
31
+
32
+ ```bash
33
+ pip install claide-web
34
+ ```
35
+
36
+ The package is published on PyPI as `claide-web`, but you import it as `claide`.
37
+
38
+ ## Quick start
39
+
40
+ ```bash
41
+ claide new mysite
42
+ cd mysite
43
+ python app.py # -> http://127.0.0.1:8000 (auto-reload)
44
+ ```
45
+
46
+ ```python
47
+ from claide import App, Page, tags as t
48
+
49
+ app = App(__name__)
50
+
51
+ @app.route("/")
52
+ def home():
53
+ return Page("Home", t.h1("Hello!"), t.p("My first Claide website."))
54
+
55
+ app.run()
56
+ ```
57
+
58
+ ## Uiverse / cssbuttons.io components
59
+
60
+ Copy the URL of an element on uiverse.io (cssbuttons.io redirects to uiverse.io/buttons):
61
+
62
+ ```python
63
+ from claide import UITemplate # or: from claide import uitemplate
64
+
65
+ button = UITemplate("https://uiverse.io/0x-Sarthak/hungry-penguin-30")
66
+ # or UITemplate("0x-Sarthak/hungry-penguin-30")
67
+
68
+ page.add(
69
+ button
70
+ .text("Contact Us", "Write me") # change the text
71
+ .attrs(id="send", onclick="go()") # add attributes
72
+ .link("/contact") # turn it into a link
73
+ )
74
+ ```
75
+
76
+ - The component's CSS is **injected automatically** into the `<head>` (only once).
77
+ - Tailwind-based components load Tailwind by themselves.
78
+ - `.scoped()` / `.prefix("x-")` rename the CSS classes to avoid conflicts
79
+ (e.g. two components that both use `.button`).
80
+ - `.add_css("...")` lets you override colors.
81
+ - `UITemplate(html="...", css="...")` or `UITemplate.from_file("button.html")` for your own code.
82
+ - `UITemplate.builtin("glow", "Send")`: bundled components that work offline
83
+ (`gradient`, `glow`, `slide`, `pushable`, `spinner`, `toggle`, `card`).
84
+ - Components are downloaded from the official `uiverse-io/galaxy` repository
85
+ (MIT license) and cached in `~/.cache/claide`.
86
+
87
+ From the command line: `claide ui author/name --preview` or `--save button.html`.
88
+
89
+ ## Linking your CSS / JS files
90
+
91
+ ```
92
+ mysite/
93
+ ├── app.py
94
+ └── static/
95
+ ├── style.css
96
+ ├── script.js
97
+ └── logo.png
98
+ ```
99
+
100
+ ```python
101
+ app.css(app.static("style.css")) # on every page
102
+ app.js(app.static("script.js"))
103
+
104
+ page = Page("Contact")
105
+ page.css("/static/contact.css") # on a single page
106
+ page.js("https://cdn.jsdelivr.net/npm/chart.js")
107
+ page.style("h1 { color: red }") # inline CSS
108
+ page.script("console.log('hi')") # inline JS
109
+ page.style_file("theme.css") # inline the content of a file
110
+ ```
111
+
112
+ ## Building HTML in Python
113
+
114
+ ```python
115
+ from claide import tags as t, raw
116
+
117
+ t.div(
118
+ t.h1("Title", class_="big"), # class_ -> class
119
+ t.a("Link", href="/x", data_id=3), # data_id -> data-id
120
+ t.input(type="checkbox", checked=True),
121
+ t.ul([t.li(x) for x in ["a", "b"]]),
122
+ raw("<b>raw HTML</b>"),
123
+ style={"padding": "1rem"},
124
+ )
125
+ ```
126
+
127
+ Text is escaped automatically (XSS protection) except with `raw()`.
128
+
129
+ ### Layout components
130
+
131
+ ```python
132
+ from claide.components import Navbar, Hero, Section, Grid, Card, Footer, Form, Field, Button, Alert
133
+
134
+ Page("Home",
135
+ Navbar("My site", [("Home", "/"), ("Contact", "/contact")]),
136
+ Hero("Welcome", "Subtitle", Button("Get started", "/start")),
137
+ Section(Grid(Card("A", "text"), Card("B", "text", image="/static/b.jpg")), title="Services"),
138
+ Footer("© 2026"),
139
+ )
140
+ ```
141
+
142
+ ## Dynamic server
143
+
144
+ ```python
145
+ from claide import App, Page, Router, redirect, abort, send_file
146
+
147
+ app = App(__name__, secret_key="change-me")
148
+
149
+ @app.get("/user/<int:uid>") # converters: str, int, float, slug, path
150
+ def user(uid, request):
151
+ page = request.args.get("page", "1") # ?page=2
152
+ return Page(f"User {uid}", ...)
153
+
154
+ @app.route("/contact", methods=["GET", "POST"])
155
+ def contact(request):
156
+ if request.method == "POST":
157
+ name = request.form["name"]
158
+ request.files["cv"].save("uploads/") # file upload
159
+ request.session["name"] = name # session (signed cookie)
160
+ return redirect("/thanks")
161
+ return Page(...)
162
+
163
+ @app.post("/api/items")
164
+ def create(request):
165
+ data = request.json
166
+ return {"ok": True, "item": data}, 201 # dict/list -> JSON
167
+
168
+ @app.errorhandler(404)
169
+ def not_found():
170
+ return Page("Oops", t.h1("Page not found"))
171
+
172
+ @app.layout # applied to every Page
173
+ def base(page):
174
+ page.body.children.insert(0, navbar)
175
+
176
+ @app.before_request
177
+ def auth(request):
178
+ if request.path.startswith("/admin") and not request.session.get("admin"):
179
+ return redirect("/login")
180
+
181
+ api = Router("/api/v2")
182
+ @api.get("/ping")
183
+ def ping(): return {"pong": True}
184
+ app.include(api)
185
+ ```
186
+
187
+ A route can return: a `Page`, an element, a `UITemplate`, `str`, `dict`/`list` (JSON),
188
+ a `Response`, a generator (streaming), or a `(body, status, headers)` tuple.
189
+
190
+ ### Production
191
+
192
+ `App` is a standard **WSGI** application:
193
+
194
+ ```bash
195
+ pip install gunicorn
196
+ gunicorn app:app
197
+ ```
198
+
199
+ ### Static export (optional)
200
+
201
+ ```bash
202
+ claide build app:app --out dist # or app.build("dist")
203
+ ```
204
+
205
+ ## Testing
206
+
207
+ ```python
208
+ client = app.test_client()
209
+ assert client.get("/").status == 200
210
+ assert client.post("/api/items", json_data={"a": 1}).json["ok"]
211
+ ```
212
+
213
+ ## Automatic PyPI publishing
214
+
215
+ `.github/workflows/ci.yml` runs the tests, builds the package, then publishes it
216
+ to PyPI on every push to `main` (and on `v*` tags). If the version already exists,
217
+ nothing is uploaded: **bump `__version__` in `claide/__init__.py` to release.**
218
+
219
+ One-time setup:
220
+
221
+ 1. On [pypi.org](https://pypi.org/manage/account/token/), create an API token.
222
+ 2. On GitHub: *Settings → Secrets and variables → Actions → New repository secret*,
223
+ name **`PYPI_API_TOKEN`**, value = the token (`pypi-...`).
224
+ 3. The job uses the `pypi` GitHub environment (created automatically; you can add
225
+ a manual approval in *Settings → Environments*).
226
+
227
+ ## License
228
+
229
+ MIT. Uiverse.io components are MIT licensed; please credit their authors
230
+ (Claide automatically adds a credit comment in the CSS).
@@ -0,0 +1,206 @@
1
+ # Claide ✦
2
+
3
+ **Build complete websites in Python, from A to Z**: HTML pages, dynamic routes,
4
+ forms, file uploads, JSON APIs, sessions, your own `style.css` / `script.js`, and
5
+ the thousands of components from [cssbuttons.io](https://cssbuttons.io) / [Uiverse.io](https://uiverse.io)
6
+ in a single line. Zero dependencies.
7
+
8
+ ```bash
9
+ pip install claide-web
10
+ ```
11
+
12
+ The package is published on PyPI as `claide-web`, but you import it as `claide`.
13
+
14
+ ## Quick start
15
+
16
+ ```bash
17
+ claide new mysite
18
+ cd mysite
19
+ python app.py # -> http://127.0.0.1:8000 (auto-reload)
20
+ ```
21
+
22
+ ```python
23
+ from claide import App, Page, tags as t
24
+
25
+ app = App(__name__)
26
+
27
+ @app.route("/")
28
+ def home():
29
+ return Page("Home", t.h1("Hello!"), t.p("My first Claide website."))
30
+
31
+ app.run()
32
+ ```
33
+
34
+ ## Uiverse / cssbuttons.io components
35
+
36
+ Copy the URL of an element on uiverse.io (cssbuttons.io redirects to uiverse.io/buttons):
37
+
38
+ ```python
39
+ from claide import UITemplate # or: from claide import uitemplate
40
+
41
+ button = UITemplate("https://uiverse.io/0x-Sarthak/hungry-penguin-30")
42
+ # or UITemplate("0x-Sarthak/hungry-penguin-30")
43
+
44
+ page.add(
45
+ button
46
+ .text("Contact Us", "Write me") # change the text
47
+ .attrs(id="send", onclick="go()") # add attributes
48
+ .link("/contact") # turn it into a link
49
+ )
50
+ ```
51
+
52
+ - The component's CSS is **injected automatically** into the `<head>` (only once).
53
+ - Tailwind-based components load Tailwind by themselves.
54
+ - `.scoped()` / `.prefix("x-")` rename the CSS classes to avoid conflicts
55
+ (e.g. two components that both use `.button`).
56
+ - `.add_css("...")` lets you override colors.
57
+ - `UITemplate(html="...", css="...")` or `UITemplate.from_file("button.html")` for your own code.
58
+ - `UITemplate.builtin("glow", "Send")`: bundled components that work offline
59
+ (`gradient`, `glow`, `slide`, `pushable`, `spinner`, `toggle`, `card`).
60
+ - Components are downloaded from the official `uiverse-io/galaxy` repository
61
+ (MIT license) and cached in `~/.cache/claide`.
62
+
63
+ From the command line: `claide ui author/name --preview` or `--save button.html`.
64
+
65
+ ## Linking your CSS / JS files
66
+
67
+ ```
68
+ mysite/
69
+ ├── app.py
70
+ └── static/
71
+ ├── style.css
72
+ ├── script.js
73
+ └── logo.png
74
+ ```
75
+
76
+ ```python
77
+ app.css(app.static("style.css")) # on every page
78
+ app.js(app.static("script.js"))
79
+
80
+ page = Page("Contact")
81
+ page.css("/static/contact.css") # on a single page
82
+ page.js("https://cdn.jsdelivr.net/npm/chart.js")
83
+ page.style("h1 { color: red }") # inline CSS
84
+ page.script("console.log('hi')") # inline JS
85
+ page.style_file("theme.css") # inline the content of a file
86
+ ```
87
+
88
+ ## Building HTML in Python
89
+
90
+ ```python
91
+ from claide import tags as t, raw
92
+
93
+ t.div(
94
+ t.h1("Title", class_="big"), # class_ -> class
95
+ t.a("Link", href="/x", data_id=3), # data_id -> data-id
96
+ t.input(type="checkbox", checked=True),
97
+ t.ul([t.li(x) for x in ["a", "b"]]),
98
+ raw("<b>raw HTML</b>"),
99
+ style={"padding": "1rem"},
100
+ )
101
+ ```
102
+
103
+ Text is escaped automatically (XSS protection) except with `raw()`.
104
+
105
+ ### Layout components
106
+
107
+ ```python
108
+ from claide.components import Navbar, Hero, Section, Grid, Card, Footer, Form, Field, Button, Alert
109
+
110
+ Page("Home",
111
+ Navbar("My site", [("Home", "/"), ("Contact", "/contact")]),
112
+ Hero("Welcome", "Subtitle", Button("Get started", "/start")),
113
+ Section(Grid(Card("A", "text"), Card("B", "text", image="/static/b.jpg")), title="Services"),
114
+ Footer("© 2026"),
115
+ )
116
+ ```
117
+
118
+ ## Dynamic server
119
+
120
+ ```python
121
+ from claide import App, Page, Router, redirect, abort, send_file
122
+
123
+ app = App(__name__, secret_key="change-me")
124
+
125
+ @app.get("/user/<int:uid>") # converters: str, int, float, slug, path
126
+ def user(uid, request):
127
+ page = request.args.get("page", "1") # ?page=2
128
+ return Page(f"User {uid}", ...)
129
+
130
+ @app.route("/contact", methods=["GET", "POST"])
131
+ def contact(request):
132
+ if request.method == "POST":
133
+ name = request.form["name"]
134
+ request.files["cv"].save("uploads/") # file upload
135
+ request.session["name"] = name # session (signed cookie)
136
+ return redirect("/thanks")
137
+ return Page(...)
138
+
139
+ @app.post("/api/items")
140
+ def create(request):
141
+ data = request.json
142
+ return {"ok": True, "item": data}, 201 # dict/list -> JSON
143
+
144
+ @app.errorhandler(404)
145
+ def not_found():
146
+ return Page("Oops", t.h1("Page not found"))
147
+
148
+ @app.layout # applied to every Page
149
+ def base(page):
150
+ page.body.children.insert(0, navbar)
151
+
152
+ @app.before_request
153
+ def auth(request):
154
+ if request.path.startswith("/admin") and not request.session.get("admin"):
155
+ return redirect("/login")
156
+
157
+ api = Router("/api/v2")
158
+ @api.get("/ping")
159
+ def ping(): return {"pong": True}
160
+ app.include(api)
161
+ ```
162
+
163
+ A route can return: a `Page`, an element, a `UITemplate`, `str`, `dict`/`list` (JSON),
164
+ a `Response`, a generator (streaming), or a `(body, status, headers)` tuple.
165
+
166
+ ### Production
167
+
168
+ `App` is a standard **WSGI** application:
169
+
170
+ ```bash
171
+ pip install gunicorn
172
+ gunicorn app:app
173
+ ```
174
+
175
+ ### Static export (optional)
176
+
177
+ ```bash
178
+ claide build app:app --out dist # or app.build("dist")
179
+ ```
180
+
181
+ ## Testing
182
+
183
+ ```python
184
+ client = app.test_client()
185
+ assert client.get("/").status == 200
186
+ assert client.post("/api/items", json_data={"a": 1}).json["ok"]
187
+ ```
188
+
189
+ ## Automatic PyPI publishing
190
+
191
+ `.github/workflows/ci.yml` runs the tests, builds the package, then publishes it
192
+ to PyPI on every push to `main` (and on `v*` tags). If the version already exists,
193
+ nothing is uploaded: **bump `__version__` in `claide/__init__.py` to release.**
194
+
195
+ One-time setup:
196
+
197
+ 1. On [pypi.org](https://pypi.org/manage/account/token/), create an API token.
198
+ 2. On GitHub: *Settings → Secrets and variables → Actions → New repository secret*,
199
+ name **`PYPI_API_TOKEN`**, value = the token (`pypi-...`).
200
+ 3. The job uses the `pypi` GitHub environment (created automatically; you can add
201
+ a manual approval in *Settings → Environments*).
202
+
203
+ ## License
204
+
205
+ MIT. Uiverse.io components are MIT licensed; please credit their authors
206
+ (Claide automatically adds a credit comment in the CSS).
@@ -0,0 +1,26 @@
1
+ """Claide — build complete websites in Python, from A to Z.
2
+
3
+ from claide import App, Page, UITemplate, tags as t
4
+
5
+ See https://github.com/kurodaki-dev/Claide
6
+ """
7
+
8
+ from .app import (
9
+ App, HTTPError, Request, Response, Router, UploadedFile, abort, redirect, send_file,
10
+ )
11
+ from .html import Element, Fragment, Raw, escape, raw, tags
12
+ from .page import Page
13
+ from .uitemplate import CATEGORIES, UITemplate, UITemplateError, builtin_templates
14
+
15
+ # Also allows "from claide import uitemplate" then uitemplate.UITemplate(...)
16
+ from . import components, uitemplate # noqa: E402
17
+
18
+ __version__ = "0.1.0"
19
+
20
+ __all__ = [
21
+ "App", "Router", "Request", "Response", "UploadedFile", "HTTPError",
22
+ "abort", "redirect", "send_file",
23
+ "Element", "Fragment", "Raw", "escape", "raw", "tags",
24
+ "Page", "UITemplate", "UITemplateError", "CATEGORIES", "builtin_templates",
25
+ "components", "uitemplate", "__version__",
26
+ ]
@@ -0,0 +1,3 @@
1
+ from .cli import main
2
+
3
+ main()