python2mobile 1.0.1__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.
- python2mobile-1.0.1/PKG-INFO +238 -0
- python2mobile-1.0.1/README.md +203 -0
- python2mobile-1.0.1/examples/example_ecommerce_app.py +189 -0
- python2mobile-1.0.1/examples/example_todo_app.py +159 -0
- python2mobile-1.0.1/p2m/__init__.py +31 -0
- python2mobile-1.0.1/p2m/cli.py +470 -0
- python2mobile-1.0.1/p2m/config.py +205 -0
- python2mobile-1.0.1/p2m/core/__init__.py +18 -0
- python2mobile-1.0.1/p2m/core/api.py +191 -0
- python2mobile-1.0.1/p2m/core/ast_walker.py +171 -0
- python2mobile-1.0.1/p2m/core/database.py +192 -0
- python2mobile-1.0.1/p2m/core/events.py +56 -0
- python2mobile-1.0.1/p2m/core/render_engine.py +597 -0
- python2mobile-1.0.1/p2m/core/runtime.py +128 -0
- python2mobile-1.0.1/p2m/core/state.py +51 -0
- python2mobile-1.0.1/p2m/core/validator.py +284 -0
- python2mobile-1.0.1/p2m/devserver/__init__.py +9 -0
- python2mobile-1.0.1/p2m/devserver/server.py +84 -0
- python2mobile-1.0.1/p2m/i18n/__init__.py +7 -0
- python2mobile-1.0.1/p2m/i18n/translator.py +74 -0
- python2mobile-1.0.1/p2m/imagine/__init__.py +35 -0
- python2mobile-1.0.1/p2m/imagine/agent.py +463 -0
- python2mobile-1.0.1/p2m/imagine/legacy.py +217 -0
- python2mobile-1.0.1/p2m/llm/__init__.py +20 -0
- python2mobile-1.0.1/p2m/llm/anthropic_provider.py +78 -0
- python2mobile-1.0.1/p2m/llm/base.py +42 -0
- python2mobile-1.0.1/p2m/llm/compatible_provider.py +120 -0
- python2mobile-1.0.1/p2m/llm/factory.py +72 -0
- python2mobile-1.0.1/p2m/llm/ollama_provider.py +89 -0
- python2mobile-1.0.1/p2m/llm/openai_provider.py +79 -0
- python2mobile-1.0.1/p2m/testing/__init__.py +41 -0
- python2mobile-1.0.1/p2m/ui/__init__.py +43 -0
- python2mobile-1.0.1/p2m/ui/components.py +301 -0
- python2mobile-1.0.1/pyproject.toml +68 -0
- python2mobile-1.0.1/python2mobile.egg-info/PKG-INFO +238 -0
- python2mobile-1.0.1/python2mobile.egg-info/SOURCES.txt +53 -0
- python2mobile-1.0.1/python2mobile.egg-info/dependency_links.txt +1 -0
- python2mobile-1.0.1/python2mobile.egg-info/entry_points.txt +2 -0
- python2mobile-1.0.1/python2mobile.egg-info/requires.txt +20 -0
- python2mobile-1.0.1/python2mobile.egg-info/top_level.txt +4 -0
- python2mobile-1.0.1/setup.cfg +4 -0
- python2mobile-1.0.1/tests/test_basic_engine.py +281 -0
- python2mobile-1.0.1/tests/test_build_generation.py +603 -0
- python2mobile-1.0.1/tests/test_build_test_gate.py +150 -0
- python2mobile-1.0.1/tests/test_carousel_modal.py +84 -0
- python2mobile-1.0.1/tests/test_config_system.py +272 -0
- python2mobile-1.0.1/tests/test_i18n.py +101 -0
- python2mobile-1.0.1/tests/test_ifood_app_integration.py +172 -0
- python2mobile-1.0.1/tests/test_imagine_cli.py +133 -0
- python2mobile-1.0.1/tests/test_imagine_command.py +341 -0
- python2mobile-1.0.1/tests/test_llm_providers.py +321 -0
- python2mobile-1.0.1/tests/test_new_apps_integration.py +588 -0
- python2mobile-1.0.1/tests/test_ollama_functional.py +329 -0
- python2mobile-1.0.1/tests/test_real_world_apps.py +228 -0
- python2mobile-1.0.1/tests/test_run_integration.py +776 -0
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: python2mobile
|
|
3
|
+
Version: 1.0.1
|
|
4
|
+
Summary: Python to Mobile - Write mobile apps in pure Python with DSL
|
|
5
|
+
Author-email: P2M Team <team@python2mobile.dev>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Keywords: mobile,python,dsl,flutter,react-native,llm,ai
|
|
8
|
+
Classifier: Development Status :: 3 - Alpha
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Requires-Python: >=3.9
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
Requires-Dist: click>=8.1.0
|
|
17
|
+
Requires-Dist: fastapi>=0.104.0
|
|
18
|
+
Requires-Dist: uvicorn>=0.24.0
|
|
19
|
+
Requires-Dist: watchdog>=3.0.0
|
|
20
|
+
Requires-Dist: jinja2>=3.1.0
|
|
21
|
+
Requires-Dist: pydantic>=2.0.0
|
|
22
|
+
Requires-Dist: python-dotenv>=1.0.0
|
|
23
|
+
Requires-Dist: requests>=2.31.0
|
|
24
|
+
Requires-Dist: openai>=1.3.0
|
|
25
|
+
Requires-Dist: anthropic>=0.7.0
|
|
26
|
+
Requires-Dist: toml>=0.10.0
|
|
27
|
+
Requires-Dist: websockets>=12.0
|
|
28
|
+
Requires-Dist: agno>=2.1.0
|
|
29
|
+
Provides-Extra: dev
|
|
30
|
+
Requires-Dist: pytest>=7.4.0; extra == "dev"
|
|
31
|
+
Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
|
|
32
|
+
Requires-Dist: black>=23.0.0; extra == "dev"
|
|
33
|
+
Requires-Dist: ruff>=0.1.0; extra == "dev"
|
|
34
|
+
Requires-Dist: mypy>=1.5.0; extra == "dev"
|
|
35
|
+
|
|
36
|
+
# Python2Mobile (P2M)
|
|
37
|
+
|
|
38
|
+
Write mobile apps in **pure Python** with a declarative DSL. P2M generates or runs native/hybrid apps for Android, iOS, and Web.
|
|
39
|
+
|
|
40
|
+
## Features
|
|
41
|
+
|
|
42
|
+
- **Python DSL** - Write UI and logic in familiar Python syntax
|
|
43
|
+
- **Tailwind-like Styling** - Use CSS class names for mobile styling
|
|
44
|
+
- **Hot Reload** - See changes instantly during development
|
|
45
|
+
- **Multi-LLM Support** - OpenAI, Claude, Ollama, or any OpenAI-compatible API
|
|
46
|
+
- **Multiple Targets** - Generate React Native or Flutter code
|
|
47
|
+
- **DevServer** - Preview apps in browser before building
|
|
48
|
+
|
|
49
|
+
## Quick Start
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
pip install python2mobile
|
|
53
|
+
|
|
54
|
+
# Create a new app
|
|
55
|
+
p2m new myapp
|
|
56
|
+
cd myapp
|
|
57
|
+
|
|
58
|
+
# Run in development mode (hot reload)
|
|
59
|
+
p2m run
|
|
60
|
+
|
|
61
|
+
# Build for production
|
|
62
|
+
p2m build --target android
|
|
63
|
+
p2m build --target ios
|
|
64
|
+
p2m build --target web
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Example App
|
|
68
|
+
|
|
69
|
+
```python
|
|
70
|
+
from p2m.core import Render
|
|
71
|
+
from p2m.ui import Container, Text, Button
|
|
72
|
+
|
|
73
|
+
def click_button():
|
|
74
|
+
print("Button clicked!")
|
|
75
|
+
|
|
76
|
+
def create_view():
|
|
77
|
+
container = Container(class_="bg-gray-100 min-h-screen flex items-center justify-center")
|
|
78
|
+
inner = Container(class_="text-center space-y-6 p-8 bg-white rounded-2xl shadow-lg")
|
|
79
|
+
|
|
80
|
+
text = Text("Welcome to P2M", class_="text-gray-800 text-2xl font-bold")
|
|
81
|
+
button = Button(
|
|
82
|
+
"Click Me",
|
|
83
|
+
class_="bg-blue-600 text-white font-semibold py-3 px-8 rounded-xl hover:bg-blue-700",
|
|
84
|
+
on_click=click_button
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
inner.add(text).add(button)
|
|
88
|
+
container.add(inner)
|
|
89
|
+
return container.build()
|
|
90
|
+
|
|
91
|
+
def main():
|
|
92
|
+
Render.execute(create_view)
|
|
93
|
+
|
|
94
|
+
if __name__ == "__main__":
|
|
95
|
+
main()
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## Configuration
|
|
99
|
+
|
|
100
|
+
Create a `p2m.toml` file:
|
|
101
|
+
|
|
102
|
+
```toml
|
|
103
|
+
[project]
|
|
104
|
+
name = "MyApp"
|
|
105
|
+
version = "0.1.0"
|
|
106
|
+
entry = "main.py"
|
|
107
|
+
|
|
108
|
+
[build]
|
|
109
|
+
target = ["android", "ios", "web"]
|
|
110
|
+
generator = "flutter" # or "react-native"
|
|
111
|
+
llm_provider = "openai" # openai | anthropic | ollama | openai-compatible
|
|
112
|
+
llm_model = "gpt-4o"
|
|
113
|
+
output_dir = "./build"
|
|
114
|
+
cache = true
|
|
115
|
+
|
|
116
|
+
[devserver]
|
|
117
|
+
port = 3000
|
|
118
|
+
hot_reload = true
|
|
119
|
+
mobile_frame = true
|
|
120
|
+
|
|
121
|
+
[style]
|
|
122
|
+
system = "tailwind"
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## LLM Configuration
|
|
126
|
+
|
|
127
|
+
### OpenAI
|
|
128
|
+
```toml
|
|
129
|
+
[llm.openai]
|
|
130
|
+
api_key = "sk-..."
|
|
131
|
+
model = "gpt-4o"
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### Claude (Anthropic)
|
|
135
|
+
```toml
|
|
136
|
+
[llm.anthropic]
|
|
137
|
+
api_key = "sk-ant-..."
|
|
138
|
+
model = "claude-3-opus-20240229"
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Ollama (Local)
|
|
142
|
+
```toml
|
|
143
|
+
[llm.ollama]
|
|
144
|
+
base_url = "http://localhost:11434"
|
|
145
|
+
model = "llama2"
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### OpenAI Compatible
|
|
149
|
+
```toml
|
|
150
|
+
[llm.custom]
|
|
151
|
+
base_url = "https://api.example.com/v1"
|
|
152
|
+
api_key = "your-api-key"
|
|
153
|
+
model = "your-model-name"
|
|
154
|
+
x_api_key = "optional-header"
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## Architecture
|
|
158
|
+
|
|
159
|
+
```
|
|
160
|
+
┌─────────────────────────────────────────────────────┐
|
|
161
|
+
│ DEVELOPER │
|
|
162
|
+
│ writes in Python P2M │
|
|
163
|
+
└──────────────────────┬──────────────────────────────┘
|
|
164
|
+
│
|
|
165
|
+
┌────────────▼────────────┐
|
|
166
|
+
│ p2m CLI │
|
|
167
|
+
│ (p2m run / p2m build) │
|
|
168
|
+
└────────────┬────────────┘
|
|
169
|
+
│
|
|
170
|
+
┌─────────────┴──────────────┐
|
|
171
|
+
│ │
|
|
172
|
+
┌────▼────┐ ┌─────▼──────┐
|
|
173
|
+
│ p2m run │ │ p2m build │
|
|
174
|
+
│ (dev) │ │ (prod) │
|
|
175
|
+
└────┬────┘ └─────┬──────┘
|
|
176
|
+
│ │
|
|
177
|
+
┌────▼────────────┐ ┌──────▼───────────┐
|
|
178
|
+
│ P2M Runtime │ │ AI Code Generator│
|
|
179
|
+
│ - AST Parser │ │ - LLM Integration│
|
|
180
|
+
│ - Safe Eval │ │ - React Native │
|
|
181
|
+
│ - HTML Renderer │ │ - Flutter │
|
|
182
|
+
│ - Hot Reload │ │ - Web │
|
|
183
|
+
└────┬────────────┘ └──────┬───────────┘
|
|
184
|
+
│ │
|
|
185
|
+
┌────▼────────────┐ ┌──────▼───────────┐
|
|
186
|
+
│ Web Visualizer │ │ /build/ │
|
|
187
|
+
│ localhost:3000 │ │ android/ │
|
|
188
|
+
│ Mobile Preview │ │ ios/ │
|
|
189
|
+
└─────────────────┘ │ web/ │
|
|
190
|
+
└──────────────────┘
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
## Project Structure
|
|
194
|
+
|
|
195
|
+
```
|
|
196
|
+
p2m/
|
|
197
|
+
├── __init__.py
|
|
198
|
+
├── cli.py # CLI entry point
|
|
199
|
+
├── config.py # Configuration management
|
|
200
|
+
├── core/
|
|
201
|
+
│ ├── __init__.py
|
|
202
|
+
│ ├── runtime.py # Python runtime engine
|
|
203
|
+
│ ├── ast_walker.py # AST analysis
|
|
204
|
+
│ ├── render_engine.py # Component tree → HTML
|
|
205
|
+
│ ├── hot_reload.py # File watcher
|
|
206
|
+
│ └── event_bridge.py # JS ↔ Python bridge
|
|
207
|
+
├── ui/
|
|
208
|
+
│ ├── __init__.py
|
|
209
|
+
│ ├── components.py # Base components
|
|
210
|
+
│ ├── layouts.py # Layout components
|
|
211
|
+
│ └── styles.py # Style system
|
|
212
|
+
├── devserver/
|
|
213
|
+
│ ├── __init__.py
|
|
214
|
+
│ ├── server.py # FastAPI server
|
|
215
|
+
│ ├── templates.py # Jinja2 templates
|
|
216
|
+
│ └── websocket_handler.py # WebSocket bridge
|
|
217
|
+
├── llm/
|
|
218
|
+
│ ├── __init__.py
|
|
219
|
+
│ ├── base.py # Base LLM interface
|
|
220
|
+
│ ├── openai_provider.py # OpenAI integration
|
|
221
|
+
│ ├── anthropic_provider.py # Claude integration
|
|
222
|
+
│ ├── ollama_provider.py # Ollama integration
|
|
223
|
+
│ └── factory.py # LLM factory
|
|
224
|
+
├── build/
|
|
225
|
+
│ ├── __init__.py
|
|
226
|
+
│ ├── generator.py # Code generator
|
|
227
|
+
│ ├── react_native.py # React Native templates
|
|
228
|
+
│ ├── flutter.py # Flutter templates
|
|
229
|
+
│ └── cache.py # Build cache
|
|
230
|
+
└── utils/
|
|
231
|
+
├── __init__.py
|
|
232
|
+
├── logger.py # Logging
|
|
233
|
+
└── validators.py # Validation
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
## License
|
|
237
|
+
|
|
238
|
+
MIT
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
# Python2Mobile (P2M)
|
|
2
|
+
|
|
3
|
+
Write mobile apps in **pure Python** with a declarative DSL. P2M generates or runs native/hybrid apps for Android, iOS, and Web.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Python DSL** - Write UI and logic in familiar Python syntax
|
|
8
|
+
- **Tailwind-like Styling** - Use CSS class names for mobile styling
|
|
9
|
+
- **Hot Reload** - See changes instantly during development
|
|
10
|
+
- **Multi-LLM Support** - OpenAI, Claude, Ollama, or any OpenAI-compatible API
|
|
11
|
+
- **Multiple Targets** - Generate React Native or Flutter code
|
|
12
|
+
- **DevServer** - Preview apps in browser before building
|
|
13
|
+
|
|
14
|
+
## Quick Start
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
pip install python2mobile
|
|
18
|
+
|
|
19
|
+
# Create a new app
|
|
20
|
+
p2m new myapp
|
|
21
|
+
cd myapp
|
|
22
|
+
|
|
23
|
+
# Run in development mode (hot reload)
|
|
24
|
+
p2m run
|
|
25
|
+
|
|
26
|
+
# Build for production
|
|
27
|
+
p2m build --target android
|
|
28
|
+
p2m build --target ios
|
|
29
|
+
p2m build --target web
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Example App
|
|
33
|
+
|
|
34
|
+
```python
|
|
35
|
+
from p2m.core import Render
|
|
36
|
+
from p2m.ui import Container, Text, Button
|
|
37
|
+
|
|
38
|
+
def click_button():
|
|
39
|
+
print("Button clicked!")
|
|
40
|
+
|
|
41
|
+
def create_view():
|
|
42
|
+
container = Container(class_="bg-gray-100 min-h-screen flex items-center justify-center")
|
|
43
|
+
inner = Container(class_="text-center space-y-6 p-8 bg-white rounded-2xl shadow-lg")
|
|
44
|
+
|
|
45
|
+
text = Text("Welcome to P2M", class_="text-gray-800 text-2xl font-bold")
|
|
46
|
+
button = Button(
|
|
47
|
+
"Click Me",
|
|
48
|
+
class_="bg-blue-600 text-white font-semibold py-3 px-8 rounded-xl hover:bg-blue-700",
|
|
49
|
+
on_click=click_button
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
inner.add(text).add(button)
|
|
53
|
+
container.add(inner)
|
|
54
|
+
return container.build()
|
|
55
|
+
|
|
56
|
+
def main():
|
|
57
|
+
Render.execute(create_view)
|
|
58
|
+
|
|
59
|
+
if __name__ == "__main__":
|
|
60
|
+
main()
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Configuration
|
|
64
|
+
|
|
65
|
+
Create a `p2m.toml` file:
|
|
66
|
+
|
|
67
|
+
```toml
|
|
68
|
+
[project]
|
|
69
|
+
name = "MyApp"
|
|
70
|
+
version = "0.1.0"
|
|
71
|
+
entry = "main.py"
|
|
72
|
+
|
|
73
|
+
[build]
|
|
74
|
+
target = ["android", "ios", "web"]
|
|
75
|
+
generator = "flutter" # or "react-native"
|
|
76
|
+
llm_provider = "openai" # openai | anthropic | ollama | openai-compatible
|
|
77
|
+
llm_model = "gpt-4o"
|
|
78
|
+
output_dir = "./build"
|
|
79
|
+
cache = true
|
|
80
|
+
|
|
81
|
+
[devserver]
|
|
82
|
+
port = 3000
|
|
83
|
+
hot_reload = true
|
|
84
|
+
mobile_frame = true
|
|
85
|
+
|
|
86
|
+
[style]
|
|
87
|
+
system = "tailwind"
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## LLM Configuration
|
|
91
|
+
|
|
92
|
+
### OpenAI
|
|
93
|
+
```toml
|
|
94
|
+
[llm.openai]
|
|
95
|
+
api_key = "sk-..."
|
|
96
|
+
model = "gpt-4o"
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### Claude (Anthropic)
|
|
100
|
+
```toml
|
|
101
|
+
[llm.anthropic]
|
|
102
|
+
api_key = "sk-ant-..."
|
|
103
|
+
model = "claude-3-opus-20240229"
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Ollama (Local)
|
|
107
|
+
```toml
|
|
108
|
+
[llm.ollama]
|
|
109
|
+
base_url = "http://localhost:11434"
|
|
110
|
+
model = "llama2"
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### OpenAI Compatible
|
|
114
|
+
```toml
|
|
115
|
+
[llm.custom]
|
|
116
|
+
base_url = "https://api.example.com/v1"
|
|
117
|
+
api_key = "your-api-key"
|
|
118
|
+
model = "your-model-name"
|
|
119
|
+
x_api_key = "optional-header"
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Architecture
|
|
123
|
+
|
|
124
|
+
```
|
|
125
|
+
┌─────────────────────────────────────────────────────┐
|
|
126
|
+
│ DEVELOPER │
|
|
127
|
+
│ writes in Python P2M │
|
|
128
|
+
└──────────────────────┬──────────────────────────────┘
|
|
129
|
+
│
|
|
130
|
+
┌────────────▼────────────┐
|
|
131
|
+
│ p2m CLI │
|
|
132
|
+
│ (p2m run / p2m build) │
|
|
133
|
+
└────────────┬────────────┘
|
|
134
|
+
│
|
|
135
|
+
┌─────────────┴──────────────┐
|
|
136
|
+
│ │
|
|
137
|
+
┌────▼────┐ ┌─────▼──────┐
|
|
138
|
+
│ p2m run │ │ p2m build │
|
|
139
|
+
│ (dev) │ │ (prod) │
|
|
140
|
+
└────┬────┘ └─────┬──────┘
|
|
141
|
+
│ │
|
|
142
|
+
┌────▼────────────┐ ┌──────▼───────────┐
|
|
143
|
+
│ P2M Runtime │ │ AI Code Generator│
|
|
144
|
+
│ - AST Parser │ │ - LLM Integration│
|
|
145
|
+
│ - Safe Eval │ │ - React Native │
|
|
146
|
+
│ - HTML Renderer │ │ - Flutter │
|
|
147
|
+
│ - Hot Reload │ │ - Web │
|
|
148
|
+
└────┬────────────┘ └──────┬───────────┘
|
|
149
|
+
│ │
|
|
150
|
+
┌────▼────────────┐ ┌──────▼───────────┐
|
|
151
|
+
│ Web Visualizer │ │ /build/ │
|
|
152
|
+
│ localhost:3000 │ │ android/ │
|
|
153
|
+
│ Mobile Preview │ │ ios/ │
|
|
154
|
+
└─────────────────┘ │ web/ │
|
|
155
|
+
└──────────────────┘
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
## Project Structure
|
|
159
|
+
|
|
160
|
+
```
|
|
161
|
+
p2m/
|
|
162
|
+
├── __init__.py
|
|
163
|
+
├── cli.py # CLI entry point
|
|
164
|
+
├── config.py # Configuration management
|
|
165
|
+
├── core/
|
|
166
|
+
│ ├── __init__.py
|
|
167
|
+
│ ├── runtime.py # Python runtime engine
|
|
168
|
+
│ ├── ast_walker.py # AST analysis
|
|
169
|
+
│ ├── render_engine.py # Component tree → HTML
|
|
170
|
+
│ ├── hot_reload.py # File watcher
|
|
171
|
+
│ └── event_bridge.py # JS ↔ Python bridge
|
|
172
|
+
├── ui/
|
|
173
|
+
│ ├── __init__.py
|
|
174
|
+
│ ├── components.py # Base components
|
|
175
|
+
│ ├── layouts.py # Layout components
|
|
176
|
+
│ └── styles.py # Style system
|
|
177
|
+
├── devserver/
|
|
178
|
+
│ ├── __init__.py
|
|
179
|
+
│ ├── server.py # FastAPI server
|
|
180
|
+
│ ├── templates.py # Jinja2 templates
|
|
181
|
+
│ └── websocket_handler.py # WebSocket bridge
|
|
182
|
+
├── llm/
|
|
183
|
+
│ ├── __init__.py
|
|
184
|
+
│ ├── base.py # Base LLM interface
|
|
185
|
+
│ ├── openai_provider.py # OpenAI integration
|
|
186
|
+
│ ├── anthropic_provider.py # Claude integration
|
|
187
|
+
│ ├── ollama_provider.py # Ollama integration
|
|
188
|
+
│ └── factory.py # LLM factory
|
|
189
|
+
├── build/
|
|
190
|
+
│ ├── __init__.py
|
|
191
|
+
│ ├── generator.py # Code generator
|
|
192
|
+
│ ├── react_native.py # React Native templates
|
|
193
|
+
│ ├── flutter.py # Flutter templates
|
|
194
|
+
│ └── cache.py # Build cache
|
|
195
|
+
└── utils/
|
|
196
|
+
├── __init__.py
|
|
197
|
+
├── logger.py # Logging
|
|
198
|
+
└── validators.py # Validation
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
## License
|
|
202
|
+
|
|
203
|
+
MIT
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Example: E-commerce App - Real-world P2M application
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from p2m.core import Render
|
|
6
|
+
from p2m.ui import Container, Text, Button, Input, Image, Row, Column, Card, Badge
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
# Mock product data
|
|
10
|
+
products = [
|
|
11
|
+
{
|
|
12
|
+
"id": 1,
|
|
13
|
+
"name": "Wireless Headphones",
|
|
14
|
+
"price": 79.99,
|
|
15
|
+
"rating": 4.5,
|
|
16
|
+
"image": "https://via.placeholder.com/200x200?text=Headphones",
|
|
17
|
+
"in_stock": True,
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"id": 2,
|
|
21
|
+
"name": "Smart Watch",
|
|
22
|
+
"price": 199.99,
|
|
23
|
+
"rating": 4.8,
|
|
24
|
+
"image": "https://via.placeholder.com/200x200?text=SmartWatch",
|
|
25
|
+
"in_stock": True,
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"id": 3,
|
|
29
|
+
"name": "USB-C Cable",
|
|
30
|
+
"price": 12.99,
|
|
31
|
+
"rating": 4.2,
|
|
32
|
+
"image": "https://via.placeholder.com/200x200?text=Cable",
|
|
33
|
+
"in_stock": False,
|
|
34
|
+
},
|
|
35
|
+
]
|
|
36
|
+
|
|
37
|
+
# Shopping cart
|
|
38
|
+
cart = []
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def add_to_cart(product_id: int):
|
|
42
|
+
"""Add product to cart"""
|
|
43
|
+
product = next((p for p in products if p["id"] == product_id), None)
|
|
44
|
+
if product:
|
|
45
|
+
cart.append(product)
|
|
46
|
+
print(f"Added {product['name']} to cart")
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def remove_from_cart(product_id: int):
|
|
50
|
+
"""Remove product from cart"""
|
|
51
|
+
global cart
|
|
52
|
+
cart = [p for p in cart if p["id"] != product_id]
|
|
53
|
+
print(f"Removed product {product_id} from cart")
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def render_product_card(product: dict):
|
|
57
|
+
"""Render a product card"""
|
|
58
|
+
card = Card(class_="bg-white rounded-lg shadow-md overflow-hidden hover:shadow-lg transition-shadow")
|
|
59
|
+
|
|
60
|
+
# Product image
|
|
61
|
+
image = Image(
|
|
62
|
+
src=product["image"],
|
|
63
|
+
alt=product["name"],
|
|
64
|
+
class_="w-full h-48 object-cover"
|
|
65
|
+
)
|
|
66
|
+
card.add(image)
|
|
67
|
+
|
|
68
|
+
# Product info
|
|
69
|
+
info = Container(class_="p-4")
|
|
70
|
+
|
|
71
|
+
# Name
|
|
72
|
+
name = Text(product["name"], class_="text-lg font-semibold text-gray-800 mb-2")
|
|
73
|
+
info.add(name)
|
|
74
|
+
|
|
75
|
+
# Rating and price row
|
|
76
|
+
rating_price = Row(class_="flex justify-between items-center mb-3")
|
|
77
|
+
|
|
78
|
+
rating = Badge(
|
|
79
|
+
label=f"⭐ {product['rating']}",
|
|
80
|
+
class_="bg-yellow-100 text-yellow-800 px-2 py-1 rounded text-sm"
|
|
81
|
+
)
|
|
82
|
+
rating_price.add(rating)
|
|
83
|
+
|
|
84
|
+
price = Text(f"${product['price']}", class_="text-xl font-bold text-blue-600")
|
|
85
|
+
rating_price.add(price)
|
|
86
|
+
|
|
87
|
+
info.add(rating_price)
|
|
88
|
+
|
|
89
|
+
# Stock status
|
|
90
|
+
stock_class = "bg-green-100 text-green-800" if product["in_stock"] else "bg-red-100 text-red-800"
|
|
91
|
+
stock_text = "In Stock" if product["in_stock"] else "Out of Stock"
|
|
92
|
+
stock = Badge(
|
|
93
|
+
label=stock_text,
|
|
94
|
+
class_=f"{stock_class} px-2 py-1 rounded text-xs font-semibold"
|
|
95
|
+
)
|
|
96
|
+
info.add(stock)
|
|
97
|
+
|
|
98
|
+
# Add to cart button
|
|
99
|
+
btn_class = "bg-blue-600 hover:bg-blue-700 text-white" if product["in_stock"] else "bg-gray-400 text-gray-600 cursor-not-allowed"
|
|
100
|
+
add_btn = Button(
|
|
101
|
+
"Add to Cart",
|
|
102
|
+
class_=f"{btn_class} w-full mt-4 px-4 py-2 rounded font-semibold",
|
|
103
|
+
on_click=lambda: add_to_cart(product["id"]) if product["in_stock"] else None
|
|
104
|
+
)
|
|
105
|
+
info.add(add_btn)
|
|
106
|
+
|
|
107
|
+
card.add(info)
|
|
108
|
+
return card
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def create_view():
|
|
112
|
+
"""Create the e-commerce app view"""
|
|
113
|
+
|
|
114
|
+
# Main container
|
|
115
|
+
main = Container(class_="bg-gray-50 min-h-screen")
|
|
116
|
+
|
|
117
|
+
# Header
|
|
118
|
+
header = Container(class_="bg-blue-600 text-white p-4 shadow-md")
|
|
119
|
+
|
|
120
|
+
title = Text("TechStore", class_="text-2xl font-bold mb-2")
|
|
121
|
+
header.add(title)
|
|
122
|
+
|
|
123
|
+
subtitle = Text("Your favorite tech products", class_="text-blue-100")
|
|
124
|
+
header.add(subtitle)
|
|
125
|
+
|
|
126
|
+
main.add(header)
|
|
127
|
+
|
|
128
|
+
# Search bar
|
|
129
|
+
search_section = Container(class_="bg-white p-4 border-b border-gray-200")
|
|
130
|
+
search_input = Input(
|
|
131
|
+
placeholder="Search products...",
|
|
132
|
+
class_="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
133
|
+
)
|
|
134
|
+
search_section.add(search_input)
|
|
135
|
+
main.add(search_section)
|
|
136
|
+
|
|
137
|
+
# Products section
|
|
138
|
+
products_section = Container(class_="p-4")
|
|
139
|
+
|
|
140
|
+
section_title = Text("Featured Products", class_="text-xl font-bold text-gray-800 mb-4")
|
|
141
|
+
products_section.add(section_title)
|
|
142
|
+
|
|
143
|
+
# Products grid
|
|
144
|
+
products_grid = Container(class_="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4")
|
|
145
|
+
|
|
146
|
+
for product in products:
|
|
147
|
+
product_card = render_product_card(product)
|
|
148
|
+
products_grid.add(product_card)
|
|
149
|
+
|
|
150
|
+
products_section.add(products_grid)
|
|
151
|
+
main.add(products_section)
|
|
152
|
+
|
|
153
|
+
# Cart summary
|
|
154
|
+
cart_section = Container(class_="bg-white p-4 border-t border-gray-200 sticky bottom-0")
|
|
155
|
+
|
|
156
|
+
cart_row = Row(class_="flex justify-between items-center")
|
|
157
|
+
|
|
158
|
+
cart_info = Column()
|
|
159
|
+
cart_label = Text("Shopping Cart", class_="font-semibold text-gray-800")
|
|
160
|
+
cart_info.add(cart_label)
|
|
161
|
+
|
|
162
|
+
cart_count = Text(
|
|
163
|
+
f"{len(cart)} items • ${sum(p['price'] for p in cart):.2f}",
|
|
164
|
+
class_="text-sm text-gray-600"
|
|
165
|
+
)
|
|
166
|
+
cart_info.add(cart_count)
|
|
167
|
+
|
|
168
|
+
cart_row.add(cart_info)
|
|
169
|
+
|
|
170
|
+
checkout_btn = Button(
|
|
171
|
+
"Checkout",
|
|
172
|
+
class_="bg-green-600 hover:bg-green-700 text-white px-6 py-2 rounded font-semibold",
|
|
173
|
+
on_click=lambda: print("Checkout clicked")
|
|
174
|
+
)
|
|
175
|
+
cart_row.add(checkout_btn)
|
|
176
|
+
|
|
177
|
+
cart_section.add(cart_row)
|
|
178
|
+
main.add(cart_section)
|
|
179
|
+
|
|
180
|
+
return main.build()
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def main():
|
|
184
|
+
"""Main entry point"""
|
|
185
|
+
Render.execute(create_view)
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
if __name__ == "__main__":
|
|
189
|
+
main()
|