dars-framework 1.1.0__tar.gz → 1.1.2__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.
- dars_framework-1.1.2/MANIFEST.in +6 -0
- {dars_framework-1.1.0/dars_framework.egg-info → dars_framework-1.1.2}/PKG-INFO +8 -3
- {dars_framework-1.1.0 → dars_framework-1.1.2}/README.md +6 -2
- {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/all.py +3 -1
- {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/cli/main.py +195 -52
- {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/cli/translations.py +5 -1
- dars_framework-1.1.2/dars/components/basic/markdown.py +86 -0
- {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/core/app.py +23 -5
- dars_framework-1.1.2/dars/docs/app.md +178 -0
- dars_framework-1.1.2/dars/docs/cli.md +80 -0
- dars_framework-1.1.2/dars/docs/components.md +1678 -0
- dars_framework-1.1.2/dars/docs/custom_components.md +30 -0
- dars_framework-1.1.2/dars/docs/events.md +45 -0
- dars_framework-1.1.2/dars/docs/exporters.md +158 -0
- dars_framework-1.1.2/dars/docs/getting_started.md +78 -0
- dars_framework-1.1.2/dars/docs/index.md +16 -0
- dars_framework-1.1.2/dars/docs/scripts.md +593 -0
- {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/exporters/web/html_css_js.py +255 -1
- dars_framework-1.1.2/dars/templates/__pycache__/__init__.cpython-311.pyc +0 -0
- dars_framework-1.1.2/dars/templates/examples/README.md +4 -0
- dars_framework-1.1.2/dars/templates/examples/__pycache__/dynamic_event_demo.cpython-311.pyc +0 -0
- dars_framework-1.1.2/dars/templates/examples/basic/PWA/icon-192x192.png +0 -0
- dars_framework-1.1.2/dars/templates/examples/basic/PWA/icon-512x512.png +0 -0
- dars_framework-1.1.2/dars/templates/examples/basic/__init__.py +0 -0
- dars_framework-1.1.2/dars/templates/examples/demo/__pycache__/complete_app.cpython-311.pyc +0 -0
- dars_framework-1.1.2/dars/templates/examples/markdown/MarkdownTemplate/README.md +159 -0
- dars_framework-1.1.2/dars/templates/examples/markdown/MarkdownTemplate/markdown_template.py +21 -0
- dars_framework-1.1.2/dars/templates/examples/markdown/MarkdownTemplate/other_docs.md +1 -0
- dars_framework-1.1.2/dars/templates/examples/markdown/__init__.py +0 -0
- dars_framework-1.1.2/dars/templates/html/__init__.py +0 -0
- {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/version.py +2 -2
- {dars_framework-1.1.0 → dars_framework-1.1.2/dars_framework.egg-info}/PKG-INFO +8 -3
- {dars_framework-1.1.0 → dars_framework-1.1.2}/dars_framework.egg-info/SOURCES.txt +35 -12
- {dars_framework-1.1.0 → dars_framework-1.1.2}/dars_framework.egg-info/requires.txt +1 -0
- {dars_framework-1.1.0 → dars_framework-1.1.2}/pyproject.toml +2 -1
- {dars_framework-1.1.0 → dars_framework-1.1.2}/LICENSE +0 -0
- {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/__init__.py +0 -0
- {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/cli/__init__.py +0 -0
- {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/cli/hot_reload.py +0 -0
- {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/cli/preview.py +0 -0
- {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/components/__init__.py +0 -0
- {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/components/advanced/__init__.py +0 -0
- {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/components/advanced/accordion.py +0 -0
- {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/components/advanced/card.py +0 -0
- {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/components/advanced/modal.py +0 -0
- {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/components/advanced/navbar.py +0 -0
- {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/components/advanced/table.py +0 -0
- {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/components/advanced/tabs.py +0 -0
- {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/components/basic/__init__.py +0 -0
- {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/components/basic/button.py +0 -0
- {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/components/basic/checkbox.py +0 -0
- {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/components/basic/container.py +0 -0
- {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/components/basic/datepicker.py +0 -0
- {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/components/basic/image.py +0 -0
- {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/components/basic/input.py +0 -0
- {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/components/basic/link.py +0 -0
- {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/components/basic/page.py +0 -0
- {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/components/basic/progressbar.py +0 -0
- {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/components/basic/radiobutton.py +0 -0
- {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/components/basic/select.py +0 -0
- {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/components/basic/slider.py +0 -0
- {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/components/basic/spinner.py +0 -0
- {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/components/basic/text.py +0 -0
- {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/components/basic/textarea.py +0 -0
- {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/components/basic/tooltip.py +0 -0
- {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/components/layout/__init__.py +0 -0
- {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/components/layout/anchor.py +0 -0
- {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/components/layout/flex.py +0 -0
- {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/components/layout/grid.py +0 -0
- {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/core/__init__.py +0 -0
- {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/core/component.py +0 -0
- {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/core/events.py +0 -0
- {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/core/properties.py +0 -0
- {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/docs/__init__.py +0 -0
- {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/exporters/__init__.py +0 -0
- {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/exporters/base.py +0 -0
- {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/exporters/web/OLD/html_css_js_OLD4.py +0 -0
- {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/exporters/web/OLD/html_css_js_old.py +0 -0
- {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/exporters/web/OLD/html_css_js_old2.py +0 -0
- {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/exporters/web/__init__.py +0 -0
- {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/scripts/__init__.py +0 -0
- {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/scripts/dscript.py +0 -0
- {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/scripts/script.py +0 -0
- {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/templates/__init__.py +0 -0
- {dars_framework-1.1.0/dars/templates/examples/advanced → dars_framework-1.1.2/dars/templates/examples/advanced/Modal_Demo}/advanced_modal_demo.py +0 -0
- {dars_framework-1.1.0/dars/templates/examples/advanced → dars_framework-1.1.2/dars/templates/examples/advanced/SimpleDashboard}/dashboard.py +0 -0
- {dars_framework-1.1.0/dars/templates/examples/advanced → dars_framework-1.1.2/dars/templates/examples/advanced/SimpleModermWeb}/modern_web_app.py +0 -0
- {dars_framework-1.1.0/dars/templates/examples/advanced → dars_framework-1.1.2/dars/templates/examples/advanced/VariousComponents}/all_components_demo.py +0 -0
- {dars_framework-1.1.0/dars/templates/html → dars_framework-1.1.2/dars/templates/examples/advanced}/__init__.py +0 -0
- {dars_framework-1.1.0/dars/templates/examples/basic → dars_framework-1.1.2/dars/templates/examples/basic/Forms}/form_components.py +0 -0
- {dars_framework-1.1.0/dars/templates/examples/basic → dars_framework-1.1.2/dars/templates/examples/basic/Forms}/simple_form.py +0 -0
- {dars_framework-1.1.0/dars/templates/examples/basic → dars_framework-1.1.2/dars/templates/examples/basic/HelloWorld}/hello_world.py +0 -0
- {dars_framework-1.1.0/dars/templates/examples/basic → dars_framework-1.1.2/dars/templates/examples/basic/Layouts}/flex_layout_responsive.py +0 -0
- {dars_framework-1.1.0/dars/templates/examples/basic → dars_framework-1.1.2/dars/templates/examples/basic/Layouts}/grid_layout_responsive.py +0 -0
- {dars_framework-1.1.0/dars/templates/examples/basic → dars_framework-1.1.2/dars/templates/examples/basic/Layouts}/layout_multipage_demo.py +0 -0
- {dars_framework-1.1.0/dars/templates/examples/basic → dars_framework-1.1.2/dars/templates/examples/basic/Multipage}/multipage_example.py +0 -0
- {dars_framework-1.1.0/dars/templates/examples/basic → dars_framework-1.1.2/dars/templates/examples/basic/PWA}/pwa_custom_icons.py +0 -0
- {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/templates/examples/demo/complete_app.py +0 -0
- {dars_framework-1.1.0 → dars_framework-1.1.2}/dars_framework.egg-info/dependency_links.txt +0 -0
- {dars_framework-1.1.0 → dars_framework-1.1.2}/dars_framework.egg-info/entry_points.txt +0 -0
- {dars_framework-1.1.0 → dars_framework-1.1.2}/dars_framework.egg-info/top_level.txt +0 -0
- {dars_framework-1.1.0 → dars_framework-1.1.2}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dars-framework
|
|
3
|
-
Version: 1.1.
|
|
3
|
+
Version: 1.1.2
|
|
4
4
|
Summary: Dars is a Python UI framework for building modern, interactive web apps with only Python code. Write your interface in Python, export it to static HTML/CSS/JS, and deploy anywhere.
|
|
5
5
|
Author-email: ztamdev <zondax2009@gmail.com>
|
|
6
6
|
License: MIT License
|
|
@@ -31,18 +31,21 @@ Requires-Dist: rich
|
|
|
31
31
|
Requires-Dist: bs4
|
|
32
32
|
Requires-Dist: uvicorn
|
|
33
33
|
Requires-Dist: fastapi
|
|
34
|
+
Requires-Dist: markdown2
|
|
34
35
|
Dynamic: license-file
|
|
35
36
|
|
|
36
37
|
# Dars Framework
|
|
37
38
|
|
|
38
39
|
Dars is a Python UI framework for building modern, interactive web apps with Python code. Write your interface in Python, export it to static HTML/CSS/JS, and deploy anywhere.
|
|
39
40
|
|
|
40
|
-
> Some Javascript or frontend stack required.
|
|
41
|
-
|
|
42
41
|
```bash
|
|
43
42
|
pip install dars-framework
|
|
44
43
|
```
|
|
45
44
|
|
|
45
|
+
> Some Javascript or frontend stack required.
|
|
46
|
+
|
|
47
|
+
Try dars without installing nothing just visit the [Dars Playground](https://dars-playground.vercel.app/)
|
|
48
|
+
|
|
46
49
|
## How It Works
|
|
47
50
|
- Build your UI using Python classes and components (like Text, Button, Container, Page, etc).
|
|
48
51
|
- Preview instantly with hot-reload using `app.rTimeCompile()`.
|
|
@@ -51,6 +54,7 @@ pip install dars-framework
|
|
|
51
54
|
- For mor information visit the [Documentation](dars/docs/index.md)
|
|
52
55
|
|
|
53
56
|
## Quick Example: Your First App
|
|
57
|
+
> Note: this is an single page example but you can build multipage apps with Page component see the [Components Documentation](dars/docs/components.md) to know more.
|
|
54
58
|
|
|
55
59
|
```python
|
|
56
60
|
from dars import App, Container, Text, Button, InlineScript
|
|
@@ -107,6 +111,7 @@ if __name__ == "__main__":
|
|
|
107
111
|
## More
|
|
108
112
|
- [Project Roadmap](ROADMAP.md)
|
|
109
113
|
- [Getting Started](dars/docs/getting_started.md)
|
|
114
|
+
- [App Class and PWA](dars/docs/app.md)
|
|
110
115
|
- [Components Documentation](dars/docs/components.md)
|
|
111
116
|
- [Custom Components](dars/docs/custom_components.md)
|
|
112
117
|
- [Event Handling](dars/docs/events.md)
|
|
@@ -2,12 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
Dars is a Python UI framework for building modern, interactive web apps with Python code. Write your interface in Python, export it to static HTML/CSS/JS, and deploy anywhere.
|
|
4
4
|
|
|
5
|
-
> Some Javascript or frontend stack required.
|
|
6
|
-
|
|
7
5
|
```bash
|
|
8
6
|
pip install dars-framework
|
|
9
7
|
```
|
|
10
8
|
|
|
9
|
+
> Some Javascript or frontend stack required.
|
|
10
|
+
|
|
11
|
+
Try dars without installing nothing just visit the [Dars Playground](https://dars-playground.vercel.app/)
|
|
12
|
+
|
|
11
13
|
## How It Works
|
|
12
14
|
- Build your UI using Python classes and components (like Text, Button, Container, Page, etc).
|
|
13
15
|
- Preview instantly with hot-reload using `app.rTimeCompile()`.
|
|
@@ -16,6 +18,7 @@ pip install dars-framework
|
|
|
16
18
|
- For mor information visit the [Documentation](dars/docs/index.md)
|
|
17
19
|
|
|
18
20
|
## Quick Example: Your First App
|
|
21
|
+
> Note: this is an single page example but you can build multipage apps with Page component see the [Components Documentation](dars/docs/components.md) to know more.
|
|
19
22
|
|
|
20
23
|
```python
|
|
21
24
|
from dars import App, Container, Text, Button, InlineScript
|
|
@@ -72,6 +75,7 @@ if __name__ == "__main__":
|
|
|
72
75
|
## More
|
|
73
76
|
- [Project Roadmap](ROADMAP.md)
|
|
74
77
|
- [Getting Started](dars/docs/getting_started.md)
|
|
78
|
+
- [App Class and PWA](dars/docs/app.md)
|
|
75
79
|
- [Components Documentation](dars/docs/components.md)
|
|
76
80
|
- [Custom Components](dars/docs/custom_components.md)
|
|
77
81
|
- [Event Handling](dars/docs/events.md)
|
|
@@ -24,6 +24,7 @@ from dars.components.basic.spinner import Spinner
|
|
|
24
24
|
from dars.components.basic.text import Text
|
|
25
25
|
from dars.components.basic.textarea import Textarea
|
|
26
26
|
from dars.components.basic.tooltip import Tooltip
|
|
27
|
+
from dars.components.basic.markdown import Markdown
|
|
27
28
|
|
|
28
29
|
# Advanced Components
|
|
29
30
|
from dars.components.advanced.accordion import Accordion
|
|
@@ -33,6 +34,7 @@ from dars.components.advanced.navbar import Navbar
|
|
|
33
34
|
from dars.components.advanced.table import Table
|
|
34
35
|
from dars.components.advanced.tabs import Tabs
|
|
35
36
|
|
|
37
|
+
|
|
36
38
|
# Layout
|
|
37
39
|
from dars.components.layout.grid import GridLayout, LayoutBase
|
|
38
40
|
from dars.components.layout.flex import FlexLayout
|
|
@@ -55,5 +57,5 @@ __all__ = [
|
|
|
55
57
|
'Accordion', 'Card', 'Modal', 'Navbar', 'Table', 'Tabs',
|
|
56
58
|
'GridLayout', 'FlexLayout', 'LayoutBase', 'AnchorPoint',
|
|
57
59
|
'InlineScript', 'FileScript', 'dScript', 'HTMLCSSJSExporter',
|
|
58
|
-
'EventTypes', 'EventHandler', 'EventEmitter', 'EventManager'
|
|
60
|
+
'EventTypes', 'EventHandler', 'EventEmitter', 'EventManager', 'Markdown'
|
|
59
61
|
]
|
|
@@ -390,8 +390,46 @@ class DarsExporter:
|
|
|
390
390
|
|
|
391
391
|
|
|
392
392
|
def init_project(self, name: str, template: Optional[str] = None):
|
|
393
|
-
|
|
394
|
-
|
|
393
|
+
"""Initializes a base Dars project, optionally using a template"""
|
|
394
|
+
if os.path.exists(name):
|
|
395
|
+
console.print(f"[red]❌ {translator.get('directory_exists').format(name=name)}[/red]")
|
|
396
|
+
return
|
|
397
|
+
|
|
398
|
+
# Create project directory
|
|
399
|
+
os.makedirs(name)
|
|
400
|
+
console.print(f"[green]✔ {translator.get('directory_created').format(name=name)}[/green]")
|
|
401
|
+
|
|
402
|
+
if template:
|
|
403
|
+
# Get template information
|
|
404
|
+
templates = list_templates()
|
|
405
|
+
if template not in templates:
|
|
406
|
+
console.print(f"[red]❌ {translator.get('template_not_found').format(template=template)}[/red]")
|
|
407
|
+
return
|
|
408
|
+
|
|
409
|
+
template_info = templates[template]
|
|
410
|
+
template_dir = template_info['template_dir']
|
|
411
|
+
extra_files = template_info['extra_files']
|
|
412
|
+
|
|
413
|
+
if not extra_files:
|
|
414
|
+
console.print(f"[yellow]⚠ {translator.get('template_empty').format(template=template)}[/yellow]")
|
|
415
|
+
return
|
|
416
|
+
|
|
417
|
+
# Copy ALL files (no main_file anymore)
|
|
418
|
+
for extra_file in extra_files:
|
|
419
|
+
src_file = template_dir / extra_file
|
|
420
|
+
dest_file = os.path.join(name, extra_file)
|
|
421
|
+
|
|
422
|
+
# Create directories if needed
|
|
423
|
+
os.makedirs(os.path.dirname(dest_file), exist_ok=True)
|
|
424
|
+
|
|
425
|
+
if src_file.exists():
|
|
426
|
+
shutil.copy2(src_file, dest_file)
|
|
427
|
+
console.print(f"[green]✔ {translator.get('extra_file_copied').format(file=extra_file)}[/green]")
|
|
428
|
+
|
|
429
|
+
console.print(f"[green]✔ {translator.get('template_copied').format(template=template)}[/green]")
|
|
430
|
+
else:
|
|
431
|
+
# Default hello world code (sin template)
|
|
432
|
+
HELLO_WORLD_CODE = """
|
|
395
433
|
from dars.core.app import App
|
|
396
434
|
from dars.components.basic.text import Text
|
|
397
435
|
from dars.components.basic.button import Button
|
|
@@ -429,36 +467,17 @@ app.add_script(script)
|
|
|
429
467
|
if __name__ == '__main__':
|
|
430
468
|
app.rTimeCompile()
|
|
431
469
|
"""
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
console.print(f"[red]❌ {translator.get('directory_exists').format(name=name)}[/red]")
|
|
435
|
-
return
|
|
436
|
-
|
|
437
|
-
# 1. Create project directory
|
|
438
|
-
os.makedirs(name)
|
|
439
|
-
console.print(f"[green]✔ {translator.get('directory_created').format(name=name)}[/green]")
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
# 4. Generate main.py
|
|
443
|
-
main_py = Path(name) / "main.py"
|
|
444
|
-
if template:
|
|
445
|
-
templates = list_templates()
|
|
446
|
-
src = templates[template]
|
|
447
|
-
shutil.copy(src, main_py)
|
|
448
|
-
console.print(f"[green]✔ {translator.get('template_copied').format(template=template)}[/green]")
|
|
449
|
-
else:
|
|
450
|
-
# embedded hello world code...
|
|
451
|
-
main_py.write_text(HELLO_WORLD_CODE, encoding="utf-8")
|
|
470
|
+
main_py = Path(name) / "main.py"
|
|
471
|
+
main_py.write_text(HELLO_WORLD_CODE.strip(), encoding="utf-8")
|
|
452
472
|
console.print(f"[green]✔ {translator.get('main_py_created')}[/green]")
|
|
453
473
|
|
|
454
|
-
#
|
|
474
|
+
# Final instructions
|
|
455
475
|
console.print(f"\n[bold cyan]🎉 {translator.get('project_initialized')}[/bold cyan]")
|
|
456
476
|
console.print(Syntax(f"cd {name}", "bash"))
|
|
457
477
|
console.print(Syntax(f"\n{translator.get('export_command')}:", "bash"))
|
|
458
|
-
console.print(Syntax(f"dars export
|
|
478
|
+
console.print(Syntax(f"dars export (python file) --format html --output build", "bash"))
|
|
459
479
|
console.print(Syntax(f"\n{translator.get('preview_command')}:", "bash"))
|
|
460
|
-
console.print(Syntax(f"
|
|
461
|
-
|
|
480
|
+
console.print(Syntax(f"python (python file)", "bash"))
|
|
462
481
|
|
|
463
482
|
def print_version_info():
|
|
464
483
|
import importlib.util
|
|
@@ -493,13 +512,25 @@ def create_parser() -> argparse.ArgumentParser:
|
|
|
493
512
|
# Export command
|
|
494
513
|
export_parser = subparsers.add_parser('export', help=translator.get('export_help'))
|
|
495
514
|
export_parser.add_argument('file', help=translator.get('file_help'))
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
515
|
+
|
|
516
|
+
# --format opcional (default: html)
|
|
517
|
+
export_parser.add_argument(
|
|
518
|
+
'--format', '-f',
|
|
519
|
+
choices=["html"],
|
|
520
|
+
default="html",
|
|
521
|
+
help=translator.get('format_help') + " (default: html)"
|
|
522
|
+
)
|
|
523
|
+
|
|
524
|
+
# --output opcional (default: ./dist)
|
|
525
|
+
export_parser.add_argument(
|
|
526
|
+
'--output', '-o',
|
|
527
|
+
default="./dist",
|
|
528
|
+
help=translator.get('output_help') + " (default: ./dist)"
|
|
529
|
+
)
|
|
530
|
+
|
|
501
531
|
export_parser.add_argument('--preview', '-p', action='store_true',
|
|
502
|
-
|
|
532
|
+
help=translator.get('preview_help'))
|
|
533
|
+
|
|
503
534
|
|
|
504
535
|
# Info command
|
|
505
536
|
info_parser = subparsers.add_parser('info', help=translator.get('info_help'))
|
|
@@ -511,14 +542,16 @@ def create_parser() -> argparse.ArgumentParser:
|
|
|
511
542
|
# Preview command
|
|
512
543
|
preview_parser = subparsers.add_parser('preview', help=translator.get('preview_cmd_help'))
|
|
513
544
|
preview_parser.add_argument('path', help=translator.get('path_help'))
|
|
514
|
-
|
|
515
|
-
# Init command
|
|
545
|
+
|
|
516
546
|
init_parser = subparsers.add_parser('init', help=translator.get('init_help'))
|
|
517
|
-
init_parser.add_argument('name', help=translator.get('name_help'))
|
|
518
|
-
|
|
547
|
+
init_parser.add_argument('name', nargs='?', help=translator.get('name_help'))
|
|
548
|
+
init_parser.add_argument(
|
|
549
|
+
'--list-templates', '-L', # Cambia -l por -L
|
|
550
|
+
action='store_true',
|
|
551
|
+
help=translator.get('list_templates_help')
|
|
552
|
+
)
|
|
519
553
|
init_parser.add_argument(
|
|
520
|
-
'
|
|
521
|
-
choices=tmpl_choices,
|
|
554
|
+
'--template', '-t',
|
|
522
555
|
help=translator.get('template_help')
|
|
523
556
|
)
|
|
524
557
|
# Add language option to all subparsers
|
|
@@ -527,19 +560,123 @@ def create_parser() -> argparse.ArgumentParser:
|
|
|
527
560
|
help=translator.get('lang_help'))
|
|
528
561
|
|
|
529
562
|
return parser
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
563
|
+
|
|
564
|
+
from pathlib import Path
|
|
565
|
+
from typing import Dict
|
|
566
|
+
|
|
567
|
+
from pathlib import Path
|
|
568
|
+
from typing import Dict
|
|
569
|
+
|
|
570
|
+
from pathlib import Path
|
|
571
|
+
from typing import Dict
|
|
572
|
+
|
|
573
|
+
from pathlib import Path
|
|
574
|
+
from typing import Dict
|
|
575
|
+
|
|
576
|
+
from pathlib import Path
|
|
577
|
+
from typing import Dict
|
|
578
|
+
|
|
579
|
+
from pathlib import Path
|
|
580
|
+
from typing import Dict
|
|
581
|
+
|
|
582
|
+
def list_templates(debug: bool = False) -> Dict[str, Dict]:
|
|
583
|
+
"""
|
|
584
|
+
Descubre templates:
|
|
585
|
+
- ignora dirs en IGNORED_DIRS (ej: __pycache__, .git, node_modules)
|
|
586
|
+
- ignora extensiones compiladas ('.pyc', '.pyo', '.pyd')
|
|
587
|
+
- ignora solo archivos ocultos que empiezan con '.' (ej: .env)
|
|
588
|
+
- incluye TODOS los demás archivos ('.py', '.md', '.png', '.json', etc.)
|
|
589
|
+
- salida determinista (ordenada)
|
|
590
|
+
"""
|
|
591
|
+
current_file = Path(__file__).resolve()
|
|
592
|
+
templates_base = current_file.parent.parent / "templates" / "examples"
|
|
593
|
+
|
|
594
|
+
if not templates_base.exists():
|
|
595
|
+
# usa console.print si tienes rich.console; aquí dejo print para compatibilidad
|
|
596
|
+
print(f"[red]Error: Template directory not found: {templates_base}[/red]")
|
|
597
|
+
return {}
|
|
598
|
+
|
|
599
|
+
IGNORED_DIRS = {'__pycache__', '.git', '.venv', 'node_modules', '.pytest_cache'}
|
|
600
|
+
IGNORE_EXTS = {'.pyc', '.pyo', '.pyd'}
|
|
601
|
+
|
|
602
|
+
templates: Dict[str, Dict] = {}
|
|
603
|
+
|
|
604
|
+
for category_dir in sorted(templates_base.iterdir()):
|
|
605
|
+
if not (category_dir.is_dir() and not category_dir.name.startswith('__')):
|
|
606
|
+
continue
|
|
607
|
+
|
|
608
|
+
for template_dir in sorted(category_dir.iterdir()):
|
|
609
|
+
if not (template_dir.is_dir() and not template_dir.name.startswith('__')):
|
|
610
|
+
continue
|
|
611
|
+
|
|
612
|
+
found_files = []
|
|
613
|
+
for file_path in sorted(template_dir.rglob('*')):
|
|
614
|
+
# 1) archivo
|
|
615
|
+
if not file_path.is_file():
|
|
616
|
+
if debug: print(f"SKIP (not file): {file_path}")
|
|
617
|
+
continue
|
|
618
|
+
|
|
619
|
+
# 2) si alguna parte del path es una carpeta ignorada
|
|
620
|
+
intersect = set(file_path.parts) & IGNORED_DIRS
|
|
621
|
+
if intersect:
|
|
622
|
+
if debug: print(f"SKIP (ignored dir {intersect}): {file_path}")
|
|
623
|
+
continue
|
|
624
|
+
|
|
625
|
+
# 3) extensiones compiladas
|
|
626
|
+
if file_path.suffix.lower() in IGNORE_EXTS:
|
|
627
|
+
if debug: print(f"SKIP (ignored ext): {file_path}")
|
|
628
|
+
continue
|
|
629
|
+
|
|
630
|
+
# 4) solo ocultos que empiezan con '.' (por ejemplo .gitignore, .env)
|
|
631
|
+
if file_path.name.startswith('.'):
|
|
632
|
+
if debug: print(f"SKIP (hidden file): {file_path}")
|
|
633
|
+
continue
|
|
634
|
+
|
|
635
|
+
# si pasó todos los filtros, lo guardamos (ruta relativa al template)
|
|
636
|
+
rel = str(file_path.relative_to(template_dir))
|
|
637
|
+
if debug: print(f"INCLUDE: {rel}")
|
|
638
|
+
found_files.append(rel)
|
|
639
|
+
|
|
640
|
+
found_files = sorted(found_files)
|
|
641
|
+
|
|
642
|
+
template_key = f"{category_dir.name}/{template_dir.name}"
|
|
643
|
+
templates[template_key] = {
|
|
644
|
+
'main_file': None, # ya no usamos main_file
|
|
645
|
+
'extra_files': found_files,
|
|
646
|
+
'category': category_dir.name,
|
|
647
|
+
'template_dir': template_dir,
|
|
648
|
+
'all_files': found_files
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
return templates
|
|
652
|
+
|
|
653
|
+
|
|
654
|
+
|
|
655
|
+
|
|
656
|
+
def list_templates_detailed():
|
|
657
|
+
"""Muestra información detallada de los templates disponibles"""
|
|
658
|
+
templates = list_templates()
|
|
659
|
+
|
|
660
|
+
if not templates:
|
|
661
|
+
console.print("[yellow]No templates found[/yellow]")
|
|
662
|
+
return
|
|
663
|
+
|
|
664
|
+
table = Table(title="Available Templates")
|
|
665
|
+
table.add_column("Template", style="cyan")
|
|
666
|
+
table.add_column("Category", style="green")
|
|
667
|
+
table.add_column("Extra Files", style="white")
|
|
668
|
+
table.add_column("Description", style="dim")
|
|
669
|
+
|
|
670
|
+
for template_name, template_info in templates.items():
|
|
671
|
+
extra_files = ", ".join(template_info['extra_files']) if template_info['extra_files'] else "None"
|
|
672
|
+
table.add_row(
|
|
673
|
+
template_name,
|
|
674
|
+
template_info['category'],
|
|
675
|
+
extra_files,
|
|
676
|
+
f"Template with {len(template_info['extra_files'])} extra files"
|
|
677
|
+
)
|
|
678
|
+
|
|
679
|
+
console.print(table)
|
|
543
680
|
def main():
|
|
544
681
|
"""Main CLI function"""
|
|
545
682
|
# Check for language parameter before parsing arguments
|
|
@@ -624,7 +761,13 @@ def main():
|
|
|
624
761
|
exporter.show_supported_formats()
|
|
625
762
|
|
|
626
763
|
elif args.command == 'init':
|
|
627
|
-
|
|
764
|
+
if args.list_templates:
|
|
765
|
+
list_templates_detailed()
|
|
766
|
+
elif not args.name:
|
|
767
|
+
console.print("[red]Error: Project name is required[/red]")
|
|
768
|
+
parser.parse_args(['init', '--help'])
|
|
769
|
+
else:
|
|
770
|
+
exporter.init_project(args.name, template=args.template)
|
|
628
771
|
|
|
629
772
|
|
|
630
773
|
elif args.command == 'preview':
|
|
@@ -19,7 +19,9 @@ translations = {
|
|
|
19
19
|
'preview_help': "Preview information",
|
|
20
20
|
'preview_cmd_help': "Preview exported application",
|
|
21
21
|
'init_help': "Create a Dars project",
|
|
22
|
-
|
|
22
|
+
'template_not_found': "Template '{template}' not found",
|
|
23
|
+
'extra_file_copied': "Extra file '{file}' copied",
|
|
24
|
+
|
|
23
25
|
# Export command
|
|
24
26
|
'file_help': "Python file with Dars application",
|
|
25
27
|
'format_help': "Export format",
|
|
@@ -158,6 +160,8 @@ translations = {
|
|
|
158
160
|
'cli_description': "Dars Exporter - Exporta aplicaciones Dars a Web",
|
|
159
161
|
'cli_subtitle': "Framework de UI multiplataforma en Python",
|
|
160
162
|
'main_description': "Dars Exporter - Exporta aplicaciones Dars a Web",
|
|
163
|
+
'template_not_found': "Template '{template}' not found",
|
|
164
|
+
'extra_file_copied': "Extra file '{file}' copied",
|
|
161
165
|
|
|
162
166
|
# Commands
|
|
163
167
|
'available_commands': "Comandos disponibles",
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
from typing import Optional, Dict, Any, Callable, Union
|
|
2
|
+
import os
|
|
3
|
+
from dars.core.component import Component
|
|
4
|
+
|
|
5
|
+
class Markdown(Component):
|
|
6
|
+
def __init__(
|
|
7
|
+
self,
|
|
8
|
+
content: Optional[str] = None,
|
|
9
|
+
file_path: Optional[str] = None,
|
|
10
|
+
id: Optional[str] = None,
|
|
11
|
+
class_name: Optional[str] = None,
|
|
12
|
+
style: Optional[Dict[str, Any]] = None,
|
|
13
|
+
dark_theme: bool = False,
|
|
14
|
+
**kwargs
|
|
15
|
+
):
|
|
16
|
+
"""
|
|
17
|
+
Markdown component that converts markdown content to HTML.
|
|
18
|
+
|
|
19
|
+
Args:
|
|
20
|
+
content: String with markdown content
|
|
21
|
+
file_path: Path to a .md file to load content from
|
|
22
|
+
id: Component ID
|
|
23
|
+
class_name: CSS class name
|
|
24
|
+
style: CSS styles
|
|
25
|
+
dark_theme: Enable dark theme styling
|
|
26
|
+
"""
|
|
27
|
+
super().__init__(id=id, class_name=class_name, style=style, **kwargs)
|
|
28
|
+
|
|
29
|
+
if content and file_path:
|
|
30
|
+
raise ValueError("Only content or file_path can be specified, not both")
|
|
31
|
+
|
|
32
|
+
if not content and not file_path:
|
|
33
|
+
raise ValueError("Either content or file_path must be specified")
|
|
34
|
+
|
|
35
|
+
self.content = content
|
|
36
|
+
self.file_path = file_path
|
|
37
|
+
self.dark_theme = dark_theme
|
|
38
|
+
self.rendered_html = ""
|
|
39
|
+
|
|
40
|
+
# Load and process markdown content
|
|
41
|
+
self._load_and_process_content()
|
|
42
|
+
|
|
43
|
+
def _load_and_process_content(self):
|
|
44
|
+
"""Load and process markdown content."""
|
|
45
|
+
if self.file_path:
|
|
46
|
+
if not os.path.exists(self.file_path):
|
|
47
|
+
raise FileNotFoundError(f"File {self.file_path} does not exist")
|
|
48
|
+
|
|
49
|
+
if not self.file_path.endswith('.md'):
|
|
50
|
+
raise ValueError("File must have .md extension")
|
|
51
|
+
|
|
52
|
+
with open(self.file_path, 'r', encoding='utf-8') as f:
|
|
53
|
+
self.content = f.read()
|
|
54
|
+
|
|
55
|
+
def update_content(self, new_content: Optional[str] = None, new_file_path: Optional[str] = None):
|
|
56
|
+
"""
|
|
57
|
+
Update the markdown content of the component.
|
|
58
|
+
|
|
59
|
+
Args:
|
|
60
|
+
new_content: New markdown content as string
|
|
61
|
+
new_file_path: New markdown file path
|
|
62
|
+
"""
|
|
63
|
+
if new_content and new_file_path:
|
|
64
|
+
raise ValueError("Only new_content or new_file_path can be specified, not both")
|
|
65
|
+
|
|
66
|
+
if new_content:
|
|
67
|
+
self.content = new_content
|
|
68
|
+
self.file_path = None
|
|
69
|
+
elif new_file_path:
|
|
70
|
+
self.file_path = new_file_path
|
|
71
|
+
self.content = None
|
|
72
|
+
|
|
73
|
+
self._load_and_process_content()
|
|
74
|
+
|
|
75
|
+
def set_dark_theme(self, enabled: bool = True):
|
|
76
|
+
"""Enable or disable dark theme"""
|
|
77
|
+
self.dark_theme = enabled
|
|
78
|
+
# Add dark theme class dynamically
|
|
79
|
+
if enabled:
|
|
80
|
+
self.class_name = f"{self.class_name or ''} dars-markdown-dark"
|
|
81
|
+
else:
|
|
82
|
+
self.class_name = self.class_name.replace("dars-markdown-dark", "") if self.class_name else ""
|
|
83
|
+
|
|
84
|
+
def render(self, exporter: Any) -> str:
|
|
85
|
+
# El método render será implementado por cada exportador
|
|
86
|
+
raise NotImplementedError("El método render debe ser implementado por el exportador")
|
|
@@ -127,7 +127,7 @@ class Page:
|
|
|
127
127
|
class App:
|
|
128
128
|
"""Main class that represents a Dars application"""
|
|
129
129
|
|
|
130
|
-
def rTimeCompile(self, exporter=None, port=None, add_file_types=
|
|
130
|
+
def rTimeCompile(self, exporter=None, port=None, add_file_types=".py, .js, .css"):
|
|
131
131
|
"""
|
|
132
132
|
Generates a quick preview of the app on a local server using an exporter
|
|
133
133
|
(default: HTMLCSSJSExporter) and serving the files from a temporary directory.
|
|
@@ -368,7 +368,7 @@ class App:
|
|
|
368
368
|
pass
|
|
369
369
|
|
|
370
370
|
if not new_app:
|
|
371
|
-
(console.print("[red]No App instance found after reload.
|
|
371
|
+
(console.print("[red]No App instance found after reload.")
|
|
372
372
|
if console else print("[Dars] No App instance found after reload."))
|
|
373
373
|
return
|
|
374
374
|
|
|
@@ -480,7 +480,7 @@ class App:
|
|
|
480
480
|
|
|
481
481
|
|
|
482
482
|
def __init__(
|
|
483
|
-
self,
|
|
483
|
+
self,
|
|
484
484
|
title: str = "Dars App",
|
|
485
485
|
description: str = "",
|
|
486
486
|
author: str = "",
|
|
@@ -554,6 +554,7 @@ class App:
|
|
|
554
554
|
self._index_page: str = None # Nombre de la página principal (si existe)
|
|
555
555
|
self.scripts: List['Script'] = []
|
|
556
556
|
self.global_styles: Dict[str, Any] = {}
|
|
557
|
+
self.global_style_files: List[str] = []
|
|
557
558
|
self.event_manager = EventManager()
|
|
558
559
|
self.config = config
|
|
559
560
|
|
|
@@ -618,9 +619,26 @@ class App:
|
|
|
618
619
|
"""Adds a script to the app"""
|
|
619
620
|
self.scripts.append(script)
|
|
620
621
|
|
|
621
|
-
def add_global_style(self, selector: str, styles: Dict[str, Any]):
|
|
622
|
-
"""
|
|
622
|
+
def add_global_style(self, selector: str = None, styles: Dict[str, Any] = None, file_path: str = None):
|
|
623
|
+
"""
|
|
624
|
+
Adds a global style to the app.
|
|
625
|
+
|
|
626
|
+
- If file_path is provided, the CSS file is read and stored.
|
|
627
|
+
- If selector and styles are provided, they are stored as inline CSS rules.
|
|
628
|
+
- It is invalid to mix file_path with selector/styles.
|
|
629
|
+
"""
|
|
630
|
+
if file_path:
|
|
631
|
+
if selector or styles:
|
|
632
|
+
raise ValueError("Cannot use selector/styles when file_path is provided.")
|
|
633
|
+
if file_path not in self.global_style_files:
|
|
634
|
+
self.global_style_files.append(file_path)
|
|
635
|
+
return self
|
|
636
|
+
|
|
637
|
+
if not selector or not styles:
|
|
638
|
+
raise ValueError("Must provide selector and styles when file_path is not used.")
|
|
639
|
+
|
|
623
640
|
self.global_styles[selector] = styles
|
|
641
|
+
return self
|
|
624
642
|
|
|
625
643
|
def set_theme(self, theme: str):
|
|
626
644
|
"""Set the theme for the app"""
|