python2mobile 1.0.1__py3-none-any.whl
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.
- examples/example_ecommerce_app.py +189 -0
- examples/example_todo_app.py +159 -0
- p2m/__init__.py +31 -0
- p2m/cli.py +470 -0
- p2m/config.py +205 -0
- p2m/core/__init__.py +18 -0
- p2m/core/api.py +191 -0
- p2m/core/ast_walker.py +171 -0
- p2m/core/database.py +192 -0
- p2m/core/events.py +56 -0
- p2m/core/render_engine.py +597 -0
- p2m/core/runtime.py +128 -0
- p2m/core/state.py +51 -0
- p2m/core/validator.py +284 -0
- p2m/devserver/__init__.py +9 -0
- p2m/devserver/server.py +84 -0
- p2m/i18n/__init__.py +7 -0
- p2m/i18n/translator.py +74 -0
- p2m/imagine/__init__.py +35 -0
- p2m/imagine/agent.py +463 -0
- p2m/imagine/legacy.py +217 -0
- p2m/llm/__init__.py +20 -0
- p2m/llm/anthropic_provider.py +78 -0
- p2m/llm/base.py +42 -0
- p2m/llm/compatible_provider.py +120 -0
- p2m/llm/factory.py +72 -0
- p2m/llm/ollama_provider.py +89 -0
- p2m/llm/openai_provider.py +79 -0
- p2m/testing/__init__.py +41 -0
- p2m/ui/__init__.py +43 -0
- p2m/ui/components.py +301 -0
- python2mobile-1.0.1.dist-info/METADATA +238 -0
- python2mobile-1.0.1.dist-info/RECORD +50 -0
- python2mobile-1.0.1.dist-info/WHEEL +5 -0
- python2mobile-1.0.1.dist-info/entry_points.txt +2 -0
- python2mobile-1.0.1.dist-info/top_level.txt +3 -0
- tests/test_basic_engine.py +281 -0
- tests/test_build_generation.py +603 -0
- tests/test_build_test_gate.py +150 -0
- tests/test_carousel_modal.py +84 -0
- tests/test_config_system.py +272 -0
- tests/test_i18n.py +101 -0
- tests/test_ifood_app_integration.py +172 -0
- tests/test_imagine_cli.py +133 -0
- tests/test_imagine_command.py +341 -0
- tests/test_llm_providers.py +321 -0
- tests/test_new_apps_integration.py +588 -0
- tests/test_ollama_functional.py +329 -0
- tests/test_real_world_apps.py +228 -0
- tests/test_run_integration.py +776 -0
p2m/ui/components.py
ADDED
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
"""
|
|
2
|
+
P2M UI Components - Declarative component library
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import Any, Callable, Dict, List, Optional
|
|
6
|
+
from dataclasses import dataclass, field
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@dataclass
|
|
10
|
+
class Component:
|
|
11
|
+
"""Base component class"""
|
|
12
|
+
|
|
13
|
+
component_type: str
|
|
14
|
+
props: Dict[str, Any] = field(default_factory=dict)
|
|
15
|
+
children: List["Component"] = field(default_factory=list)
|
|
16
|
+
|
|
17
|
+
def add(self, child: "Component") -> "Component":
|
|
18
|
+
"""Add a child component"""
|
|
19
|
+
self.children.append(child)
|
|
20
|
+
return self
|
|
21
|
+
|
|
22
|
+
def build(self) -> Dict[str, Any]:
|
|
23
|
+
"""Build component tree as dictionary"""
|
|
24
|
+
return {
|
|
25
|
+
"type": self.component_type,
|
|
26
|
+
"props": self.props,
|
|
27
|
+
"children": [child.build() if isinstance(child, Component) else child
|
|
28
|
+
for child in self.children],
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
def __repr__(self) -> str:
|
|
32
|
+
return f"<{self.component_type} {self.props}>"
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class Container(Component):
|
|
36
|
+
"""Container component - wrapper for other components"""
|
|
37
|
+
|
|
38
|
+
def __init__(self, class_: str = "", direction: str = "column", scroll: bool = False, **props):
|
|
39
|
+
super().__init__(
|
|
40
|
+
component_type="Container",
|
|
41
|
+
props={
|
|
42
|
+
"class": class_,
|
|
43
|
+
"direction": direction,
|
|
44
|
+
"scroll": scroll,
|
|
45
|
+
**props,
|
|
46
|
+
}
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class Text(Component):
|
|
51
|
+
"""Text component - displays text content"""
|
|
52
|
+
|
|
53
|
+
def __init__(self, value: str, class_: str = "", **props):
|
|
54
|
+
super().__init__(
|
|
55
|
+
component_type="Text",
|
|
56
|
+
props={
|
|
57
|
+
"value": value,
|
|
58
|
+
"class": class_,
|
|
59
|
+
**props,
|
|
60
|
+
}
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class Button(Component):
|
|
65
|
+
"""Button component - clickable button"""
|
|
66
|
+
|
|
67
|
+
def __init__(
|
|
68
|
+
self,
|
|
69
|
+
label: str,
|
|
70
|
+
class_: str = "",
|
|
71
|
+
on_click=None,
|
|
72
|
+
on_click_args: Optional[List] = None,
|
|
73
|
+
**props,
|
|
74
|
+
):
|
|
75
|
+
# on_click can be a callable or a string (handler name)
|
|
76
|
+
if callable(on_click):
|
|
77
|
+
on_click_name = on_click.__name__
|
|
78
|
+
else:
|
|
79
|
+
on_click_name = on_click # already a string or None
|
|
80
|
+
|
|
81
|
+
super().__init__(
|
|
82
|
+
component_type="Button",
|
|
83
|
+
props={
|
|
84
|
+
"label": label,
|
|
85
|
+
"class": class_,
|
|
86
|
+
"on_click": on_click_name,
|
|
87
|
+
"on_click_args": on_click_args or [],
|
|
88
|
+
**props,
|
|
89
|
+
},
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
class Input(Component):
|
|
94
|
+
"""Input component - text input field"""
|
|
95
|
+
|
|
96
|
+
def __init__(
|
|
97
|
+
self,
|
|
98
|
+
placeholder: str = "",
|
|
99
|
+
class_: str = "",
|
|
100
|
+
on_change=None,
|
|
101
|
+
value: str = "",
|
|
102
|
+
input_type: str = "text",
|
|
103
|
+
**props,
|
|
104
|
+
):
|
|
105
|
+
if callable(on_change):
|
|
106
|
+
on_change_name = on_change.__name__
|
|
107
|
+
else:
|
|
108
|
+
on_change_name = on_change # string or None
|
|
109
|
+
|
|
110
|
+
super().__init__(
|
|
111
|
+
component_type="Input",
|
|
112
|
+
props={
|
|
113
|
+
"placeholder": placeholder,
|
|
114
|
+
"class": class_,
|
|
115
|
+
"on_change": on_change_name,
|
|
116
|
+
"value": value,
|
|
117
|
+
"input_type": input_type,
|
|
118
|
+
**props,
|
|
119
|
+
},
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
class Image(Component):
|
|
124
|
+
"""Image component - displays images"""
|
|
125
|
+
|
|
126
|
+
def __init__(self, src: str, class_: str = "", alt: str = "", **props):
|
|
127
|
+
super().__init__(
|
|
128
|
+
component_type="Image",
|
|
129
|
+
props={
|
|
130
|
+
"src": src,
|
|
131
|
+
"class": class_,
|
|
132
|
+
"alt": alt,
|
|
133
|
+
**props,
|
|
134
|
+
}
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
class List(Component):
|
|
139
|
+
"""List component - renders list of items"""
|
|
140
|
+
|
|
141
|
+
def __init__(self, items: List[Any], render_item: Optional[Callable] = None,
|
|
142
|
+
class_: str = "", **props):
|
|
143
|
+
super().__init__(
|
|
144
|
+
component_type="List",
|
|
145
|
+
props={
|
|
146
|
+
"items": items,
|
|
147
|
+
"render_item": render_item.__name__ if render_item else None,
|
|
148
|
+
"class": class_,
|
|
149
|
+
**props,
|
|
150
|
+
}
|
|
151
|
+
)
|
|
152
|
+
self._render_item = render_item
|
|
153
|
+
self._items = items
|
|
154
|
+
|
|
155
|
+
def get_renderer(self) -> Optional[Callable]:
|
|
156
|
+
"""Get the item renderer"""
|
|
157
|
+
return self._render_item
|
|
158
|
+
|
|
159
|
+
def get_items(self) -> List[Any]:
|
|
160
|
+
"""Get items"""
|
|
161
|
+
return self._items
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
class Navigator(Component):
|
|
165
|
+
"""Navigator component - handles navigation between screens"""
|
|
166
|
+
|
|
167
|
+
def __init__(self, routes: Dict[str, Callable], initial: str = "", **props):
|
|
168
|
+
super().__init__(
|
|
169
|
+
component_type="Navigator",
|
|
170
|
+
props={
|
|
171
|
+
"initial": initial,
|
|
172
|
+
**props,
|
|
173
|
+
}
|
|
174
|
+
)
|
|
175
|
+
self._routes = routes
|
|
176
|
+
|
|
177
|
+
def get_routes(self) -> Dict[str, Callable]:
|
|
178
|
+
"""Get routes"""
|
|
179
|
+
return self._routes
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
class Screen(Component):
|
|
183
|
+
"""Screen component - represents a screen/page"""
|
|
184
|
+
|
|
185
|
+
def __init__(self, name: str, class_: str = "", **props):
|
|
186
|
+
super().__init__(
|
|
187
|
+
component_type="Screen",
|
|
188
|
+
props={
|
|
189
|
+
"name": name,
|
|
190
|
+
"class": class_,
|
|
191
|
+
**props,
|
|
192
|
+
}
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
class Modal(Component):
|
|
197
|
+
"""Modal component - displays modal dialog"""
|
|
198
|
+
|
|
199
|
+
def __init__(self, title: str = "", class_: str = "", visible: bool = False, **props):
|
|
200
|
+
super().__init__(
|
|
201
|
+
component_type="Modal",
|
|
202
|
+
props={
|
|
203
|
+
"title": title,
|
|
204
|
+
"class": class_,
|
|
205
|
+
"visible": visible,
|
|
206
|
+
**props,
|
|
207
|
+
}
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
class ScrollView(Component):
|
|
212
|
+
"""ScrollView component - scrollable container"""
|
|
213
|
+
|
|
214
|
+
def __init__(self, class_: str = "", **props):
|
|
215
|
+
super().__init__(
|
|
216
|
+
component_type="ScrollView",
|
|
217
|
+
props={
|
|
218
|
+
"class": class_,
|
|
219
|
+
**props,
|
|
220
|
+
}
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
class Carousel(Component):
|
|
225
|
+
"""Carousel component - horizontal scrollable row of items"""
|
|
226
|
+
|
|
227
|
+
def __init__(self, class_: str = "", **props):
|
|
228
|
+
super().__init__(
|
|
229
|
+
component_type="Carousel",
|
|
230
|
+
props={"class": class_, **props},
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
class Row(Component):
|
|
235
|
+
"""Row component - horizontal layout"""
|
|
236
|
+
|
|
237
|
+
def __init__(self, class_: str = "", **props):
|
|
238
|
+
super().__init__(
|
|
239
|
+
component_type="Row",
|
|
240
|
+
props={
|
|
241
|
+
"class": class_,
|
|
242
|
+
"direction": "row",
|
|
243
|
+
**props,
|
|
244
|
+
}
|
|
245
|
+
)
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
class Column(Component):
|
|
249
|
+
"""Column component - vertical layout"""
|
|
250
|
+
|
|
251
|
+
def __init__(self, class_: str = "", **props):
|
|
252
|
+
super().__init__(
|
|
253
|
+
component_type="Column",
|
|
254
|
+
props={
|
|
255
|
+
"class": class_,
|
|
256
|
+
"direction": "column",
|
|
257
|
+
**props,
|
|
258
|
+
}
|
|
259
|
+
)
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
class Card(Component):
|
|
263
|
+
"""Card component - card container"""
|
|
264
|
+
|
|
265
|
+
def __init__(self, class_: str = "", **props):
|
|
266
|
+
super().__init__(
|
|
267
|
+
component_type="Card",
|
|
268
|
+
props={
|
|
269
|
+
"class": class_,
|
|
270
|
+
**props,
|
|
271
|
+
}
|
|
272
|
+
)
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
class Badge(Component):
|
|
276
|
+
"""Badge component - small label"""
|
|
277
|
+
|
|
278
|
+
def __init__(self, label: str, class_: str = "", **props):
|
|
279
|
+
super().__init__(
|
|
280
|
+
component_type="Badge",
|
|
281
|
+
props={
|
|
282
|
+
"label": label,
|
|
283
|
+
"class": class_,
|
|
284
|
+
**props,
|
|
285
|
+
}
|
|
286
|
+
)
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
class Icon(Component):
|
|
290
|
+
"""Icon component - displays icon"""
|
|
291
|
+
|
|
292
|
+
def __init__(self, name: str, class_: str = "", size: int = 24, **props):
|
|
293
|
+
super().__init__(
|
|
294
|
+
component_type="Icon",
|
|
295
|
+
props={
|
|
296
|
+
"name": name,
|
|
297
|
+
"class": class_,
|
|
298
|
+
"size": size,
|
|
299
|
+
**props,
|
|
300
|
+
}
|
|
301
|
+
)
|
|
@@ -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,50 @@
|
|
|
1
|
+
examples/example_ecommerce_app.py,sha256=0Bh7xLKUR-b8SeyfaSoF2E-jUTQuXqudTOyawtq-Fe8,5340
|
|
2
|
+
examples/example_todo_app.py,sha256=aXwktnqZu9fTuu2OKTyp-VBZiZY1PY8BeGXUs_Vc95g,4632
|
|
3
|
+
p2m/__init__.py,sha256=eaaxBoWz5zsbHCK_A_J0ky6aUfQZOnZTRqeLtCiwxLM,413
|
|
4
|
+
p2m/cli.py,sha256=Nw07QO67RZ6yJGfWxDs-y6qA777O2pNR9QRA_PQQ-jk,17854
|
|
5
|
+
p2m/config.py,sha256=HOaAyCHYAhPADJwVyIJdeg6KmIP53Q0ywQP4dvbjcTQ,6218
|
|
6
|
+
p2m/core/__init__.py,sha256=92EGYDO_gvZZ7E1fAriDG_5OApdThKjKTsjiTcTyXbs,394
|
|
7
|
+
p2m/core/api.py,sha256=lKqHpqJ0Dn_Yr6Dd7wtPxCbRE0sh0WZOqQKXwHYkVuE,5507
|
|
8
|
+
p2m/core/ast_walker.py,sha256=KDvBZsHjpmQvV01nqo5iaqtqlwS9bus0gS4oR0FDesg,5962
|
|
9
|
+
p2m/core/database.py,sha256=3UNpbLBk5R-Sik-Mbcse66dQx3nSu9l2s_ils0ldWIY,5629
|
|
10
|
+
p2m/core/events.py,sha256=CrqHYL1rSzKcr4D3ABKItdXSs_iql8oirj0xlG2N2_U,1300
|
|
11
|
+
p2m/core/render_engine.py,sha256=NMcnhSiHjJsPkOIA00Cdni-OKu1oGPYe4AsdpYxsh_M,24564
|
|
12
|
+
p2m/core/runtime.py,sha256=_lAS0HEUC0xm7_oMtuzYwY4Zz1xuIqnGyuprhcqMPFg,4505
|
|
13
|
+
p2m/core/state.py,sha256=g892vmyQeLFiRap3Q94XWEF5kJJOSxruc5N53514EOg,1428
|
|
14
|
+
p2m/core/validator.py,sha256=KJQEMrKil97aYENxl-EVwhmJInIeUEIN-paHHonEFrA,9755
|
|
15
|
+
p2m/devserver/__init__.py,sha256=iakn0n_aKlfEF2tTEmQYcn4lyKpmhGVyeRqegSKcQ_M,141
|
|
16
|
+
p2m/devserver/server.py,sha256=aogIjdePVQojK-1tRqltNBHricKBDnw1_3-5iUYiQMA,2871
|
|
17
|
+
p2m/i18n/__init__.py,sha256=L76Nw2JvKa8rzGtFafU94qxzwK1O68K-L2vw6uJohZM,179
|
|
18
|
+
p2m/i18n/translator.py,sha256=Mo9N3G9X70xFwDp8abSholStaqtQWkq1DLouCk5gDXc,2285
|
|
19
|
+
p2m/imagine/__init__.py,sha256=JXvfKo7raswHUYnkUCkf7ifWq34yuWvCVe208QMntAo,1041
|
|
20
|
+
p2m/imagine/agent.py,sha256=uAkbPLnpr8bGhYL8jfQcYmSeK1ahaH9W2wn21TgVj1s,15265
|
|
21
|
+
p2m/imagine/legacy.py,sha256=FVuvCswuWLXRasqhOISdF-z73Fyl8Xg4r0HMdrLI7aY,7517
|
|
22
|
+
p2m/llm/__init__.py,sha256=bUuVzJ67xLGV-FPVC7fBejWTpe-Yv_33Ee9SigpgbPU,531
|
|
23
|
+
p2m/llm/anthropic_provider.py,sha256=nDVudksnq468brHZdDbQve9I-vgmSgu9Q8iNnXbRgq0,2663
|
|
24
|
+
p2m/llm/base.py,sha256=C5rpsKZacwCjTVEQ0XlH20ux6G0_L1031GWYLtCI_Wc,1144
|
|
25
|
+
p2m/llm/compatible_provider.py,sha256=GnhsXHCoKUn0eB48fjE64YIvJyBfNXep4Ukqe1o2j_A,4112
|
|
26
|
+
p2m/llm/factory.py,sha256=GiizwKzh19gz2J08lRytRUniCG5yq4rVpFcEoR4C7-o,2277
|
|
27
|
+
p2m/llm/ollama_provider.py,sha256=bMACVJtbaQsN7ZWoHPy6XsbJFvlvL8U9-YQZO0_ysCc,3182
|
|
28
|
+
p2m/llm/openai_provider.py,sha256=EIoR5G_j_NBuNwKz_y6rTd19Ou7k7muqQLLUTjj7fo8,2715
|
|
29
|
+
p2m/testing/__init__.py,sha256=xT-zSz8QZ1Nf4jt2sPk8WRPgmOjWKAL11iyRuq_-KVU,1124
|
|
30
|
+
p2m/ui/__init__.py,sha256=tu19MdndY0qDBICCgV7Z7p--IlCZQsA9VJ4ywgJzxos,526
|
|
31
|
+
p2m/ui/components.py,sha256=VrSAijUj65_DLJkmx7fBnuchDXKtD_VItwpb6W3Ip-4,7809
|
|
32
|
+
tests/test_basic_engine.py,sha256=i_SYFrAASAi9Bj_bCK5eAFTpdPQsMXWfnbgsnNgmNfI,8211
|
|
33
|
+
tests/test_build_generation.py,sha256=yM88NI-jroEmHiAxJbDoOONENp2TaL_pCU0cH8jY0JM,29749
|
|
34
|
+
tests/test_build_test_gate.py,sha256=Ac4YwCppoO4TBP1VfF9i6ffxLTaxduH03q8YQwW7rT0,5694
|
|
35
|
+
tests/test_carousel_modal.py,sha256=Bu3GOMXPkhWDEDsvuKOfw8oefaXaIN5QKLGOAwUjym8,2876
|
|
36
|
+
tests/test_config_system.py,sha256=jyB7me5v41RAYTwOOTOBgQZc6dzEadcVzZzXuaLL3HI,7768
|
|
37
|
+
tests/test_i18n.py,sha256=5g6cCA_xJvYw2fDAqUFWAySHOqic5jQeyylml9rvP2s,3394
|
|
38
|
+
tests/test_ifood_app_integration.py,sha256=HvwUCy8S4QmtgfydBJAcB_mPeq-jEHToMRTcdxEEFIc,5902
|
|
39
|
+
tests/test_imagine_cli.py,sha256=4_JuzVjgwYIyRqHKb4jLqB59YoohNkajFwRLKCpXXJ8,3397
|
|
40
|
+
tests/test_imagine_command.py,sha256=WZl3mpM0PZlpm_-HC5x1oJnjtVDBHyWc1qI2boSrLKw,9201
|
|
41
|
+
tests/test_llm_providers.py,sha256=tuMaxim-RZChwlESIOVEVV-fjYDMURXeDcRh2KWki2U,9040
|
|
42
|
+
tests/test_new_apps_integration.py,sha256=_OSjwQGzNkRLn_geFcdLImMbSjpUgSThHAWMd9QxKKk,22744
|
|
43
|
+
tests/test_ollama_functional.py,sha256=WDNcLEo7FZnOxdiqraS9s3EDahQ8RWUr4od5BK7sX9U,8925
|
|
44
|
+
tests/test_real_world_apps.py,sha256=xZa2i5nXTFTI1L7l2X0mLxF6l4IQMitiJIDMcj-BN_8,6932
|
|
45
|
+
tests/test_run_integration.py,sha256=aZD1vx7MPYKiAQOLyiCByXOk0yI9SnvwLMKwyCtldX0,32541
|
|
46
|
+
python2mobile-1.0.1.dist-info/METADATA,sha256=GP_02pc-Fu5RMu1-vvQaSFMQpxDjBdcCjgSAvNi6gBU,7818
|
|
47
|
+
python2mobile-1.0.1.dist-info/WHEEL,sha256=YCfwYGOYMi5Jhw2fU4yNgwErybb2IX5PEwBKV4ZbdBo,91
|
|
48
|
+
python2mobile-1.0.1.dist-info/entry_points.txt,sha256=mNxeUNlwXFdWbI2s1o3vVXOSXjd9G6vPlZsQlMg2jDE,37
|
|
49
|
+
python2mobile-1.0.1.dist-info/top_level.txt,sha256=sSRsQeWtjFSXKIQPaBeqoOkwFf1Csyg34U2L5RjP0OM,19
|
|
50
|
+
python2mobile-1.0.1.dist-info/RECORD,,
|