blux-framework 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.
Files changed (26) hide show
  1. blux_framework-0.1.0/PKG-INFO +100 -0
  2. blux_framework-0.1.0/README.md +90 -0
  3. blux_framework-0.1.0/pyproject.toml +33 -0
  4. blux_framework-0.1.0/src/blux/__init__.py +10 -0
  5. blux_framework-0.1.0/src/blux/__projeto_test/app/api/payments/appypay.py +24 -0
  6. blux_framework-0.1.0/src/blux/__projeto_test/app/api/produtos/index.py +18 -0
  7. blux_framework-0.1.0/src/blux/__projeto_test/app/assets/globals.css +47 -0
  8. blux_framework-0.1.0/src/blux/__projeto_test/app/pages/index.py +68 -0
  9. blux_framework-0.1.0/src/blux/__projeto_test/app/pages/sobre.py +16 -0
  10. blux_framework-0.1.0/src/blux/__projeto_test/box.config.py +0 -0
  11. blux_framework-0.1.0/src/blux/app.py +48 -0
  12. blux_framework-0.1.0/src/blux/cli.py +103 -0
  13. blux_framework-0.1.0/src/blux/core/api.py +33 -0
  14. blux_framework-0.1.0/src/blux/core/elements.py +264 -0
  15. blux_framework-0.1.0/src/blux/core/render.py +68 -0
  16. blux_framework-0.1.0/src/blux/core/routes.py +199 -0
  17. blux_framework-0.1.0/src/blux/core/state.py +26 -0
  18. blux_framework-0.1.0/src/blux/core/types.py +0 -0
  19. blux_framework-0.1.0/src/blux/utils.py +32 -0
  20. blux_framework-0.1.0/src/blux/web/static/css/base.css +17 -0
  21. blux_framework-0.1.0/src/blux/web/static/js/app.js +0 -0
  22. blux_framework-0.1.0/src/blux/web/static/js/htmx-ext-head.js +1 -0
  23. blux_framework-0.1.0/src/blux/web/static/js/htmx.js +1 -0
  24. blux_framework-0.1.0/src/blux/web/static/js/idiomorph-ext.min.js +1 -0
  25. blux_framework-0.1.0/src/blux/web/templates/_head.html +9 -0
  26. blux_framework-0.1.0/src/blux/web/templates/index.html +12 -0
@@ -0,0 +1,100 @@
1
+ Metadata-Version: 2.3
2
+ Name: blux-framework
3
+ Version: 0.1.0
4
+ Summary: Um framework full-stack declarativo
5
+ Author: Ricardo
6
+ Author-email: Ricardo <ricardokayoca@gmail.com>
7
+ Requires-Dist: fastapi[standard]>=0.141.1
8
+ Requires-Python: >=3.14
9
+ Description-Content-Type: text/markdown
10
+
11
+ # blux
12
+ blux é um framework full-stack declarativo feito em Python para criar interfaces web com renderização dinâmica, ações no backend e roteamento baseado em diretórios.
13
+
14
+ ## Visão geral
15
+
16
+ - roteamento baseado em diretórios
17
+ - componentes para UI
18
+ - text
19
+ - view
20
+ - button
21
+ - input
22
+ - textarea
23
+ - image
24
+ - link
25
+ - table
26
+ - modal
27
+ - form
28
+ - atualização parcial do componente quando uma action é chamada
29
+ - actions: funções executadas no backend quando um evento ocorre
30
+
31
+ ## Documentação
32
+
33
+ A documentação está em `docs/` e distribuída com MkDocs (Material).
34
+
35
+ ### Rodar a versão local
36
+
37
+ ```bash
38
+ python -m pip install -e '.[docs]'
39
+ mkdocs serve
40
+ ```
41
+
42
+ ### Conteúdo principal
43
+
44
+ - [Início](docs/index.md)
45
+ - [Primeiros passos](docs/quickstart.md)
46
+ - [Conceitos](docs/concepts.md)
47
+ - [Componentes](docs/components.md)
48
+ - [Actions](docs/actions.md)
49
+ - [API](docs/api.md)
50
+
51
+ ### Build da documentação
52
+
53
+ ```bash
54
+ mkdocs build
55
+ ```
56
+
57
+ ## Status do projeto
58
+
59
+ - [x] atualizar componente sempre que uma action é chamada
60
+ - [x] actions: função que serão chamadas no back-end assim que um evento for chamado
61
+ - [ ] api: rotas publicas
62
+ - [ ] Middleware: deve executar antes da renderisação
63
+ - [ ] Autenticação
64
+ - [ ] Proteção contra CSRF
65
+ - [ ] State
66
+
67
+ ## Estrutura principal
68
+
69
+ ```text
70
+ app/
71
+ pages/
72
+ src/
73
+ blux/
74
+ ```
75
+
76
+ ## Exemplo rápido
77
+
78
+ ```python
79
+ import blux
80
+
81
+ class HomePage(blux.Page):
82
+ title = "Home"
83
+
84
+ @blux.action
85
+ def salvar(self, request):
86
+ form = request.state.form
87
+ print(form.get("nome"))
88
+
89
+ def render(self, request):
90
+ return blux.Form(
91
+ action=self.salvar,
92
+ elements=[
93
+ blux.Input(name="nome", placeholder="Digite seu nome"),
94
+ blux.Button(content="Enviar", type="submit"),
95
+ ],
96
+ )
97
+ ```
98
+
99
+
100
+
@@ -0,0 +1,90 @@
1
+ # blux
2
+ blux é um framework full-stack declarativo feito em Python para criar interfaces web com renderização dinâmica, ações no backend e roteamento baseado em diretórios.
3
+
4
+ ## Visão geral
5
+
6
+ - roteamento baseado em diretórios
7
+ - componentes para UI
8
+ - text
9
+ - view
10
+ - button
11
+ - input
12
+ - textarea
13
+ - image
14
+ - link
15
+ - table
16
+ - modal
17
+ - form
18
+ - atualização parcial do componente quando uma action é chamada
19
+ - actions: funções executadas no backend quando um evento ocorre
20
+
21
+ ## Documentação
22
+
23
+ A documentação está em `docs/` e distribuída com MkDocs (Material).
24
+
25
+ ### Rodar a versão local
26
+
27
+ ```bash
28
+ python -m pip install -e '.[docs]'
29
+ mkdocs serve
30
+ ```
31
+
32
+ ### Conteúdo principal
33
+
34
+ - [Início](docs/index.md)
35
+ - [Primeiros passos](docs/quickstart.md)
36
+ - [Conceitos](docs/concepts.md)
37
+ - [Componentes](docs/components.md)
38
+ - [Actions](docs/actions.md)
39
+ - [API](docs/api.md)
40
+
41
+ ### Build da documentação
42
+
43
+ ```bash
44
+ mkdocs build
45
+ ```
46
+
47
+ ## Status do projeto
48
+
49
+ - [x] atualizar componente sempre que uma action é chamada
50
+ - [x] actions: função que serão chamadas no back-end assim que um evento for chamado
51
+ - [ ] api: rotas publicas
52
+ - [ ] Middleware: deve executar antes da renderisação
53
+ - [ ] Autenticação
54
+ - [ ] Proteção contra CSRF
55
+ - [ ] State
56
+
57
+ ## Estrutura principal
58
+
59
+ ```text
60
+ app/
61
+ pages/
62
+ src/
63
+ blux/
64
+ ```
65
+
66
+ ## Exemplo rápido
67
+
68
+ ```python
69
+ import blux
70
+
71
+ class HomePage(blux.Page):
72
+ title = "Home"
73
+
74
+ @blux.action
75
+ def salvar(self, request):
76
+ form = request.state.form
77
+ print(form.get("nome"))
78
+
79
+ def render(self, request):
80
+ return blux.Form(
81
+ action=self.salvar,
82
+ elements=[
83
+ blux.Input(name="nome", placeholder="Digite seu nome"),
84
+ blux.Button(content="Enviar", type="submit"),
85
+ ],
86
+ )
87
+ ```
88
+
89
+
90
+
@@ -0,0 +1,33 @@
1
+ [project]
2
+ name = "blux-framework"
3
+ version = "0.1.0"
4
+ description = "Um framework full-stack declarativo"
5
+ readme = "README.md"
6
+ authors = [
7
+ { name = "Ricardo", email = "ricardokayoca@gmail.com" }
8
+ ]
9
+ requires-python = ">=3.14"
10
+ dependencies = [
11
+ "fastapi[standard]>=0.141.1",
12
+ ]
13
+
14
+ [project.scripts]
15
+ blux = "blux.cli:main"
16
+
17
+ [build-system]
18
+ requires = ["uv_build>=0.11.29,<0.12.0"]
19
+ build-backend = "uv_build"
20
+
21
+ [tool.uv.build-backend]
22
+ module-name = "blux"
23
+
24
+ [dependency-groups]
25
+ dev = [
26
+ "mkdocs-material>=9.7.7",
27
+ "pymdown-extensions>=11.0.2",
28
+ ]
29
+
30
+ [tool.uv.workspace]
31
+ members = [
32
+ "test_app",
33
+ ]
@@ -0,0 +1,10 @@
1
+
2
+ from .core.elements import *
3
+ from .core.render import (
4
+ Page,
5
+ action,
6
+ args
7
+ )
8
+ from .core.api import *
9
+ from fastapi.requests import Request
10
+ from starlette.datastructures import FormData
@@ -0,0 +1,24 @@
1
+ import blux
2
+ from pydantic import BaseModel
3
+
4
+ class Payment(BaseModel):
5
+ amount:float
6
+ client_name:str
7
+ client_email: str
8
+
9
+ @blux.api()
10
+ def get_payment(nome:str):
11
+ """ Criar um novo produto"""
12
+
13
+ return {"ok":"ok"}
14
+
15
+
16
+ @blux.api(method="POST")
17
+ def create_produtos(nome:str):
18
+ """ Criar um novo produto"""
19
+
20
+ return {"ok":"ok"}
21
+
22
+
23
+
24
+
@@ -0,0 +1,18 @@
1
+ import blux
2
+
3
+ @blux.api()
4
+ def get_produtos(nome:str):
5
+ """ Criar um novo produto"""
6
+
7
+ return {"ok":"ok"}
8
+
9
+
10
+ @blux.api(method="POST")
11
+ def create_produtos(nome:str):
12
+ """ Criar um novo produto"""
13
+
14
+ return {"ok":"ok"}
15
+
16
+
17
+
18
+
@@ -0,0 +1,47 @@
1
+ .content_app {
2
+ padding: 10px;
3
+ height: 100%;
4
+
5
+ display: flex;
6
+ justify-content: center;
7
+ align-items: center;
8
+ padding: 2rem;
9
+ text-align: center;
10
+
11
+ font-family: Verdana, Geneva, Tahoma, sans-serif;
12
+
13
+ }
14
+
15
+ .title {
16
+ font-weight: bold;
17
+ font-size: 2rem;
18
+ }
19
+
20
+ .text {
21
+ font-size: .8rem;
22
+ opacity: .7;
23
+ max-width: 70%;
24
+ margin: auto;
25
+ }
26
+
27
+ .content {
28
+ display: flex;
29
+ flex-direction: column;
30
+ gap: 10px;
31
+ }
32
+
33
+ .button_count {
34
+ background-color: #141414;
35
+ padding: 10px;
36
+ border: 0;
37
+ border-radius: 10px;
38
+ color: #d2d2d2;
39
+ cursor: pointer;
40
+ }
41
+
42
+ .row2 {
43
+ display: flex;
44
+ align-items: center;
45
+ gap: 10px;
46
+ justify-content: center;
47
+ }
@@ -0,0 +1,68 @@
1
+ import blux
2
+
3
+ num = 0
4
+
5
+ class Index(blux.Page):
6
+
7
+ title = "index"
8
+ styles = ["globals.css"]
9
+
10
+ @blux.action
11
+ def contador(self, request:blux.Request):
12
+ global num
13
+ num += 1
14
+
15
+ def render(self, request:blux.Request):
16
+
17
+ return (
18
+ blux.View(
19
+ state=self.state,
20
+ class_name="content_app",
21
+ elements=[
22
+ blux.View(
23
+ class_name="content",
24
+ elements=[
25
+
26
+ blux.Text(content="Bem vindo ao Box", class_name="title"),
27
+ blux.Text(
28
+ content="O framework pensado na produtividade de criar aplicações full-stack com apenas uma base de codigo",
29
+ class_name="text"
30
+ ),
31
+ blux.View(
32
+ elements=[
33
+ blux.Button(
34
+ content=f"contador {num}",
35
+ on_click=self.contador,
36
+ class_name="button_count"
37
+ ),
38
+ ]
39
+ ),
40
+ blux.View(
41
+ elements=[
42
+ blux.Link(
43
+ content="Develop",
44
+ href="http://ricardocayoca.onrender.com/",
45
+ target="_blank"
46
+ ),
47
+ blux.Link(
48
+ content="Doc box",
49
+ href="/"
50
+ ),
51
+ blux.Link(
52
+ content="Swager",
53
+ href="/docs",
54
+ target="_blank"
55
+ ),
56
+ blux.Link(
57
+ content="API doc",
58
+ href="/redoc",
59
+ target="_blank"
60
+ )
61
+ ],
62
+ class_name="row2"
63
+ )
64
+ ]
65
+ )
66
+ ]
67
+ )
68
+ )
@@ -0,0 +1,16 @@
1
+ import blux
2
+
3
+
4
+ class Sobre(blux.Page):
5
+
6
+ title = "sobre"
7
+
8
+ def render(self, request):
9
+ return (
10
+ blux.View(
11
+ elements=[
12
+ blux.Text(content="Pagina sobre"),
13
+ blux.Link(content="voltar", href="/")
14
+ ]
15
+ )
16
+ )
@@ -0,0 +1,48 @@
1
+ import os
2
+ from pathlib import Path
3
+
4
+ from fastapi import FastAPI
5
+ from fastapi.staticfiles import StaticFiles
6
+ from fastapi.templating import Jinja2Templates
7
+
8
+ from .core.routes import build_router
9
+ from .utils import install_form_middleware
10
+
11
+ APP_DIR = None
12
+ WEB_DIR = os.path.join(os.path.dirname(__file__), "web")
13
+
14
+
15
+ template = Jinja2Templates(
16
+ directory=os.path.join(WEB_DIR, "templates")
17
+ )
18
+
19
+ def create_app():
20
+ app = FastAPI()
21
+
22
+ install_form_middleware(app)
23
+
24
+ app.mount("/static", StaticFiles(
25
+ directory=os.path.join(WEB_DIR, "static")
26
+ )
27
+ )
28
+
29
+ for page in Path(os.getcwd()).rglob("**/app"):
30
+ if page.exists():
31
+ APP_DIR = page
32
+ break
33
+
34
+ if not APP_DIR: raise FileNotFoundError(
35
+ f"Diretório de páginas não encontrado: app\n"
36
+ f"Rode o comando a partir da raiz do projeto, ou passe --app-dir."
37
+ )
38
+
39
+
40
+
41
+ app.mount("/assets", StaticFiles(
42
+ directory=APP_DIR.joinpath("assets")
43
+ ))
44
+
45
+ for route in build_router(APP_DIR, template):
46
+ app.include_router(route, include_in_schema=True)
47
+
48
+ return app
@@ -0,0 +1,103 @@
1
+
2
+ from __future__ import annotations
3
+
4
+ import os
5
+ import shutil
6
+ from pathlib import Path
7
+
8
+ import typer
9
+ import uvicorn
10
+
11
+
12
+ app = typer.Typer(help="CLI do blux — sobe o servidor e escaneia as rotas de app/")
13
+
14
+ FACTORY_TARGET = "blux.app:create_app"
15
+
16
+
17
+ @app.command()
18
+ def dev(
19
+ host: str = typer.Option("127.0.0.1", help="Endereço para escutar"),
20
+ port: int = typer.Option(8000, help="Porta para escutar"),
21
+ ):
22
+ """Sobe o servidor de desenvolvimento com reload automático."""
23
+ typer.echo(f"blux dev -> http://{host}:{port}")
24
+ uvicorn.run(FACTORY_TARGET, factory=True, host=host, port=port, reload=True)
25
+
26
+
27
+ @app.command()
28
+ def init(name:str=typer.Argument(".", help="Diretório")):
29
+ """ Cria um estrutura do projeto """
30
+
31
+ ROOT = Path(name)
32
+
33
+ if ROOT.exists():
34
+ typer.echo(f"O projeto '{ROOT}' Já existe")
35
+ return
36
+
37
+ origem = Path(os.path.dirname(__file__)).joinpath("__projeto_test")
38
+ print(origem)
39
+
40
+ for arquivo in origem.rglob("*"):
41
+ if arquivo.is_file():
42
+ relativo = arquivo.relative_to(origem)
43
+ destino_arquivo = ROOT / relativo
44
+
45
+ destino_arquivo.parent.mkdir(parents=True, exist_ok=True)
46
+ shutil.copy2(arquivo, destino_arquivo)
47
+
48
+
49
+ typer.echo(f"Projeto {ROOT} inicializado")
50
+ typer.echo(f"use: cd {ROOT}")
51
+ typer.echo("start develop server: blux dev")
52
+ typer.echo("cli help use: blux --help")
53
+
54
+
55
+ return True
56
+
57
+
58
+
59
+
60
+
61
+
62
+
63
+
64
+
65
+ @app.command()
66
+ def start(
67
+ host: str = typer.Option("0.0.0.0", help="Endereço para escutar"),
68
+ port: int = typer.Option(8000, help="Porta para escutar"),
69
+ app_dir: str = typer.Option("app", "--app-dir", help="Diretório com as páginas"),
70
+ workers: int = typer.Option(1, help="Número de processos worker"),
71
+ ):
72
+ """Sobe o servidor em modo produção (sem reload)."""
73
+ os.environ["blux_APP_DIR"] = app_dir
74
+ typer.echo(f"backpay start -> http://{host}:{port} (páginas em ./{app_dir}, workers={workers})")
75
+ uvicorn.run(FACTORY_TARGET, factory=True, host=host, port=port, workers=workers)
76
+
77
+
78
+ @app.command()
79
+ def routes(
80
+ app_dir: str = typer.Option("app", "--app-dir", help="Diretório com as páginas"),
81
+ ):
82
+ """Lista as rotas descobertas em app/, sem subir o servidor."""
83
+ from pathlib import Path
84
+
85
+ from .core.routes import discover_pages
86
+
87
+ pages = discover_pages(Path(app_dir))
88
+ if not pages:
89
+ typer.echo(f"Nenhuma página encontrada em ./{app_dir}")
90
+ raise typer.Exit(code=1)
91
+
92
+ for page in pages:
93
+ typer.echo(f"GET {page.route_path}")
94
+ base = page.route_path.rstrip("/")
95
+ typer.echo(f"POST {base}/_action/{{component_id}}/{{action_name}}")
96
+
97
+
98
+ def main():
99
+ app()
100
+
101
+
102
+ if __name__ == "__main__":
103
+ main()
@@ -0,0 +1,33 @@
1
+ from functools import wraps
2
+ from fastapi import (
3
+ APIRouter,
4
+ HTTPException,
5
+ Depends
6
+ )
7
+
8
+ from typing import Literal
9
+
10
+
11
+ def api(
12
+ method:Literal["GET", "POST", "PUT"]="GET",
13
+ middeware:list=[],
14
+ title="",
15
+ ):
16
+
17
+ def decorador(f:callable):
18
+ @wraps(f)
19
+ def wrapper(*args, **kwargs):
20
+
21
+ return f(*args, **kwargs)
22
+ wrapper._api = True
23
+ wrapper._method = method
24
+ wrapper._title = title
25
+ return wrapper
26
+
27
+ return decorador
28
+
29
+
30
+
31
+
32
+
33
+