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.
Files changed (102) hide show
  1. dars_framework-1.1.2/MANIFEST.in +6 -0
  2. {dars_framework-1.1.0/dars_framework.egg-info → dars_framework-1.1.2}/PKG-INFO +8 -3
  3. {dars_framework-1.1.0 → dars_framework-1.1.2}/README.md +6 -2
  4. {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/all.py +3 -1
  5. {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/cli/main.py +195 -52
  6. {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/cli/translations.py +5 -1
  7. dars_framework-1.1.2/dars/components/basic/markdown.py +86 -0
  8. {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/core/app.py +23 -5
  9. dars_framework-1.1.2/dars/docs/app.md +178 -0
  10. dars_framework-1.1.2/dars/docs/cli.md +80 -0
  11. dars_framework-1.1.2/dars/docs/components.md +1678 -0
  12. dars_framework-1.1.2/dars/docs/custom_components.md +30 -0
  13. dars_framework-1.1.2/dars/docs/events.md +45 -0
  14. dars_framework-1.1.2/dars/docs/exporters.md +158 -0
  15. dars_framework-1.1.2/dars/docs/getting_started.md +78 -0
  16. dars_framework-1.1.2/dars/docs/index.md +16 -0
  17. dars_framework-1.1.2/dars/docs/scripts.md +593 -0
  18. {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/exporters/web/html_css_js.py +255 -1
  19. dars_framework-1.1.2/dars/templates/__pycache__/__init__.cpython-311.pyc +0 -0
  20. dars_framework-1.1.2/dars/templates/examples/README.md +4 -0
  21. dars_framework-1.1.2/dars/templates/examples/__pycache__/dynamic_event_demo.cpython-311.pyc +0 -0
  22. dars_framework-1.1.2/dars/templates/examples/basic/PWA/icon-192x192.png +0 -0
  23. dars_framework-1.1.2/dars/templates/examples/basic/PWA/icon-512x512.png +0 -0
  24. dars_framework-1.1.2/dars/templates/examples/basic/__init__.py +0 -0
  25. dars_framework-1.1.2/dars/templates/examples/demo/__pycache__/complete_app.cpython-311.pyc +0 -0
  26. dars_framework-1.1.2/dars/templates/examples/markdown/MarkdownTemplate/README.md +159 -0
  27. dars_framework-1.1.2/dars/templates/examples/markdown/MarkdownTemplate/markdown_template.py +21 -0
  28. dars_framework-1.1.2/dars/templates/examples/markdown/MarkdownTemplate/other_docs.md +1 -0
  29. dars_framework-1.1.2/dars/templates/examples/markdown/__init__.py +0 -0
  30. dars_framework-1.1.2/dars/templates/html/__init__.py +0 -0
  31. {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/version.py +2 -2
  32. {dars_framework-1.1.0 → dars_framework-1.1.2/dars_framework.egg-info}/PKG-INFO +8 -3
  33. {dars_framework-1.1.0 → dars_framework-1.1.2}/dars_framework.egg-info/SOURCES.txt +35 -12
  34. {dars_framework-1.1.0 → dars_framework-1.1.2}/dars_framework.egg-info/requires.txt +1 -0
  35. {dars_framework-1.1.0 → dars_framework-1.1.2}/pyproject.toml +2 -1
  36. {dars_framework-1.1.0 → dars_framework-1.1.2}/LICENSE +0 -0
  37. {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/__init__.py +0 -0
  38. {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/cli/__init__.py +0 -0
  39. {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/cli/hot_reload.py +0 -0
  40. {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/cli/preview.py +0 -0
  41. {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/components/__init__.py +0 -0
  42. {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/components/advanced/__init__.py +0 -0
  43. {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/components/advanced/accordion.py +0 -0
  44. {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/components/advanced/card.py +0 -0
  45. {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/components/advanced/modal.py +0 -0
  46. {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/components/advanced/navbar.py +0 -0
  47. {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/components/advanced/table.py +0 -0
  48. {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/components/advanced/tabs.py +0 -0
  49. {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/components/basic/__init__.py +0 -0
  50. {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/components/basic/button.py +0 -0
  51. {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/components/basic/checkbox.py +0 -0
  52. {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/components/basic/container.py +0 -0
  53. {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/components/basic/datepicker.py +0 -0
  54. {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/components/basic/image.py +0 -0
  55. {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/components/basic/input.py +0 -0
  56. {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/components/basic/link.py +0 -0
  57. {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/components/basic/page.py +0 -0
  58. {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/components/basic/progressbar.py +0 -0
  59. {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/components/basic/radiobutton.py +0 -0
  60. {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/components/basic/select.py +0 -0
  61. {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/components/basic/slider.py +0 -0
  62. {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/components/basic/spinner.py +0 -0
  63. {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/components/basic/text.py +0 -0
  64. {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/components/basic/textarea.py +0 -0
  65. {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/components/basic/tooltip.py +0 -0
  66. {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/components/layout/__init__.py +0 -0
  67. {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/components/layout/anchor.py +0 -0
  68. {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/components/layout/flex.py +0 -0
  69. {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/components/layout/grid.py +0 -0
  70. {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/core/__init__.py +0 -0
  71. {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/core/component.py +0 -0
  72. {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/core/events.py +0 -0
  73. {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/core/properties.py +0 -0
  74. {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/docs/__init__.py +0 -0
  75. {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/exporters/__init__.py +0 -0
  76. {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/exporters/base.py +0 -0
  77. {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/exporters/web/OLD/html_css_js_OLD4.py +0 -0
  78. {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/exporters/web/OLD/html_css_js_old.py +0 -0
  79. {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/exporters/web/OLD/html_css_js_old2.py +0 -0
  80. {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/exporters/web/__init__.py +0 -0
  81. {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/scripts/__init__.py +0 -0
  82. {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/scripts/dscript.py +0 -0
  83. {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/scripts/script.py +0 -0
  84. {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/templates/__init__.py +0 -0
  85. {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
  86. {dars_framework-1.1.0/dars/templates/examples/advanced → dars_framework-1.1.2/dars/templates/examples/advanced/SimpleDashboard}/dashboard.py +0 -0
  87. {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
  88. {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
  89. {dars_framework-1.1.0/dars/templates/html → dars_framework-1.1.2/dars/templates/examples/advanced}/__init__.py +0 -0
  90. {dars_framework-1.1.0/dars/templates/examples/basic → dars_framework-1.1.2/dars/templates/examples/basic/Forms}/form_components.py +0 -0
  91. {dars_framework-1.1.0/dars/templates/examples/basic → dars_framework-1.1.2/dars/templates/examples/basic/Forms}/simple_form.py +0 -0
  92. {dars_framework-1.1.0/dars/templates/examples/basic → dars_framework-1.1.2/dars/templates/examples/basic/HelloWorld}/hello_world.py +0 -0
  93. {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
  94. {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
  95. {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
  96. {dars_framework-1.1.0/dars/templates/examples/basic → dars_framework-1.1.2/dars/templates/examples/basic/Multipage}/multipage_example.py +0 -0
  97. {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
  98. {dars_framework-1.1.0 → dars_framework-1.1.2}/dars/templates/examples/demo/complete_app.py +0 -0
  99. {dars_framework-1.1.0 → dars_framework-1.1.2}/dars_framework.egg-info/dependency_links.txt +0 -0
  100. {dars_framework-1.1.0 → dars_framework-1.1.2}/dars_framework.egg-info/entry_points.txt +0 -0
  101. {dars_framework-1.1.0 → dars_framework-1.1.2}/dars_framework.egg-info/top_level.txt +0 -0
  102. {dars_framework-1.1.0 → dars_framework-1.1.2}/setup.cfg +0 -0
@@ -0,0 +1,6 @@
1
+ include README.md
2
+ include LICENSE
3
+ recursive-include dars/templates *
4
+ recursive-include dars/docs *
5
+ recursive-include dars *.json
6
+ recursive-include dars *.md
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dars-framework
3
- Version: 1.1.0
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
- # 2. Create main.py with example
394
- HELLO_WORLD_CODE = """
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
- """Initializes a base Dars project, optionally using a template"""
433
- if os.path.exists(name):
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
- # 5. Final instructions
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 main.py --format html --output build", "bash"))
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"dars preview build", "bash"))
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
- export_parser.add_argument('--format', '-f', required=True,
497
- choices=["html"],
498
- help=translator.get('format_help'))
499
- export_parser.add_argument('--output', '-o', required=True,
500
- help=translator.get('output_help'))
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
- help=translator.get('preview_help'))
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
- tmpl_choices = list_templates().keys()
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
- '-t', '--template',
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
- def list_templates() -> Dict[str, Path]:
531
- """
532
- Busca carpetas dentro de dars/templates/examples y devuelve un dict
533
- { "basic/hello_world.py": Path(...), ... }
534
- """
535
- tmpl_root = Path(resources.files("dars.templates") / "examples")
536
- templates = {}
537
- for category in tmpl_root.iterdir():
538
- if category.is_dir():
539
- for py in category.glob("*.py"):
540
- key = f"{category.name}/{py.stem}"
541
- templates[key] = py
542
- return templates
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
- exporter.init_project(args.name, template=args.template)
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=None):
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.[/red]")
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
- """Adds a global style to the app"""
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"""