g360-cli 1.15.1 → 1.16.0
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.
- package/README.md +1547 -1424
- package/package.json +2 -1
- package/src/assets/pptx/layouts/base.js +239 -0
- package/src/assets/pptx/layouts/index.js +14 -0
- package/src/assets/pptx/scripts/analyze-app.js +194 -0
- package/src/assets/pptx/templates/app-manual.js +275 -0
- package/src/assets/pptx/themes/base.js +63 -0
- package/src/assets/pptx/themes/cipsa.js +46 -0
- package/src/assets/pptx/themes/g360.js +32 -0
- package/src/assets/pptx/themes/index.js +2 -0
- package/src/assets/templates/python-flet-polished/README.md +39 -0
- package/src/assets/templates/python-flet-polished/main.py +7 -0
- package/src/assets/templates/python-flet-polished/pyproject.toml +1 -0
- package/src/assets/templates/python-flet-polished/requirements.txt +1 -0
- package/src/assets/templates/python-flet-polished/skill.json +27 -1
- package/src/assets/templates/python-flet-polished/src/app.py +44 -1
- package/src/assets/templates/python-flet-polished/src/config/theme.py +153 -74
- package/src/assets/templates/python-flet-polished/src/core/g360_registry.py +562 -0
- package/src/assets/templates/python-flet-polished/src/ui/dashboard.py +2 -2
- package/src/assets/templates/python-flet-polished/src/ui/kpi_card.py +1 -1
- package/src/assets/templates/python-flet-polished/src/ui/search_overlay.py +4 -4
- package/src/cli.js +11 -0
- package/src/commands/addon.test.js +2 -2
- package/src/commands/pptx.js +89 -0
|
@@ -1,105 +1,171 @@
|
|
|
1
1
|
import json
|
|
2
2
|
from pathlib import Path
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
"
|
|
20
|
-
"
|
|
21
|
-
"
|
|
22
|
-
"
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
|
|
4
|
+
# Colores G360 estandar como referencia
|
|
5
|
+
G360_DEFAULTS = {
|
|
6
|
+
"dark": {
|
|
7
|
+
"accent": "#10B981",
|
|
8
|
+
"accent_dark": "#047857",
|
|
9
|
+
"success": "#34D399",
|
|
10
|
+
"warning": "#F59E0B",
|
|
11
|
+
"error": "#EF4444",
|
|
12
|
+
"info": "#3B82F6",
|
|
13
|
+
"violet": "#8B5CF6",
|
|
14
|
+
"pink": "#EC4899",
|
|
15
|
+
"cyan": "#06B6D4",
|
|
16
|
+
"orange": "#F97316",
|
|
17
|
+
"surface": "#141D33",
|
|
18
|
+
"surface_variant": "#1B2740",
|
|
19
|
+
"surface_sunken": "#0E1627",
|
|
20
|
+
"background": "#0A0F1E",
|
|
21
|
+
"border": "#FFFFFF17",
|
|
22
|
+
"text_muted": "#8FA0BA",
|
|
23
|
+
"text_primary": "#F1F5FB",
|
|
24
|
+
"text_secondary": "#C9D4E6",
|
|
25
|
+
"kpis": {
|
|
26
|
+
"primary": "#06B6D4",
|
|
27
|
+
"secondary": "#8B5CF6",
|
|
28
|
+
"tertiary": "#0EA5E9",
|
|
29
|
+
"quaternary": "#6366F1",
|
|
30
|
+
"quinary": "#EC4899",
|
|
31
|
+
"senary": "#F59E0B",
|
|
32
|
+
"septenary": "#EF4444",
|
|
33
|
+
"octonary": "#F97316",
|
|
34
|
+
},
|
|
27
35
|
},
|
|
28
|
-
"
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
"octonary": "#EA580C",
|
|
36
|
+
"light": {
|
|
37
|
+
"accent": "#047857",
|
|
38
|
+
"accent_dark": "#065F46",
|
|
39
|
+
"success": "#15803D",
|
|
40
|
+
"warning": "#B45309",
|
|
41
|
+
"error": "#DC2626",
|
|
42
|
+
"info": "#2563EB",
|
|
43
|
+
"violet": "#7C3AED",
|
|
44
|
+
"pink": "#DB2777",
|
|
45
|
+
"cyan": "#0891B2",
|
|
46
|
+
"orange": "#EA580C",
|
|
47
|
+
"surface": "#FFFFFF",
|
|
48
|
+
"surface_variant": "#F7F9FC",
|
|
49
|
+
"surface_sunken": "#EEF1F6",
|
|
50
|
+
"background": "#F3F5F9",
|
|
51
|
+
"border": "#E3E8F0",
|
|
52
|
+
"text_muted": "#64748B",
|
|
53
|
+
"text_primary": "#0F172A",
|
|
54
|
+
"text_secondary": "#334155",
|
|
55
|
+
"kpis": {
|
|
56
|
+
"primary": "#0891B2",
|
|
57
|
+
"secondary": "#7C3AED",
|
|
58
|
+
"tertiary": "#0284C7",
|
|
59
|
+
"quaternary": "#4F46E5",
|
|
60
|
+
"quinary": "#DB2777",
|
|
61
|
+
"senary": "#B45309",
|
|
62
|
+
"septenary": "#DC2626",
|
|
63
|
+
"octonary": "#EA580C",
|
|
64
|
+
},
|
|
58
65
|
},
|
|
59
|
-
"surface": "#FFFFFF",
|
|
60
|
-
"surface_variant": "#F7F9FC",
|
|
61
|
-
"surface_sunken": "#EEF1F6",
|
|
62
|
-
"background": "#F3F5F9",
|
|
63
|
-
"border": "#E3E8F0",
|
|
64
|
-
"text_muted": "#64748B",
|
|
65
|
-
"text_primary": "#0F172A",
|
|
66
|
-
"text_secondary": "#334155",
|
|
67
66
|
}
|
|
68
67
|
|
|
69
68
|
|
|
70
|
-
def get_colors(mode: str) -> dict:
|
|
71
|
-
|
|
69
|
+
def get_colors(mode: str = None, skill_path: Path = None) -> dict:
|
|
70
|
+
"""
|
|
71
|
+
Carga la paleta de colores para el modo especificado.
|
|
72
|
+
|
|
73
|
+
Prioridad:
|
|
74
|
+
1. skill.json del proyecto (si existe)
|
|
75
|
+
2. Configuracion guardada en ~/.g360/
|
|
76
|
+
3. Valores por defecto G360
|
|
77
|
+
|
|
78
|
+
Args:
|
|
79
|
+
mode: 'dark' o 'light'. Si es None, lee de la preferencia guardada.
|
|
80
|
+
skill_path: Ruta al skill.json. Si es None, busca automaticamente.
|
|
81
|
+
|
|
82
|
+
Returns:
|
|
83
|
+
dict con los colores aplicados
|
|
84
|
+
"""
|
|
85
|
+
# Determinar modo
|
|
86
|
+
if mode is None:
|
|
87
|
+
mode = load_theme_preference()
|
|
88
|
+
|
|
89
|
+
# Cargar desde skill.json si existe
|
|
90
|
+
skill_colors = _load_skill_colors(skill_path)
|
|
91
|
+
|
|
92
|
+
# Combinar: defaults -> skill overrides
|
|
93
|
+
defaults = G360_DEFAULTS.get(mode, G360_DEFAULTS["dark"])
|
|
94
|
+
|
|
95
|
+
if skill_colors:
|
|
96
|
+
# Skill tiene colores propios, usarlos
|
|
97
|
+
colors = {**defaults, **skill_colors}
|
|
98
|
+
# Asegurar que kpis se fusionen correctamente
|
|
99
|
+
if "kpis" in skill_colors:
|
|
100
|
+
colors["kpis"] = {**defaults.get("kpis", {}), **skill_colors["kpis"]}
|
|
101
|
+
else:
|
|
102
|
+
colors = defaults.copy()
|
|
103
|
+
if "kpis" not in colors:
|
|
104
|
+
colors["kpis"] = defaults.get("kpis", {}).copy()
|
|
105
|
+
|
|
106
|
+
return colors
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def _load_skill_colors(skill_path: Path = None) -> dict | None:
|
|
110
|
+
"""Carga colores desde skill.json del proyecto."""
|
|
111
|
+
try:
|
|
112
|
+
if skill_path is None:
|
|
113
|
+
skill_path = _find_skill_json()
|
|
114
|
+
|
|
115
|
+
if skill_path and skill_path.exists():
|
|
116
|
+
with open(skill_path, encoding="utf-8") as f:
|
|
117
|
+
data = json.load(f)
|
|
118
|
+
return data.get("colors", {})
|
|
119
|
+
except Exception:
|
|
120
|
+
pass
|
|
121
|
+
return None
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def _find_skill_json() -> Path | None:
|
|
125
|
+
"""Busca skill.json en las ubicaciones posibles."""
|
|
126
|
+
candidates = [
|
|
127
|
+
Path("skill.json"),
|
|
128
|
+
Path("src/core/skill.json"),
|
|
129
|
+
Path(__file__).resolve().parent.parent.parent / "skill.json",
|
|
130
|
+
]
|
|
131
|
+
for candidate in candidates:
|
|
132
|
+
if candidate.exists():
|
|
133
|
+
return candidate
|
|
134
|
+
return None
|
|
72
135
|
|
|
73
136
|
|
|
74
137
|
def rgba(color: str, opacity: float) -> str:
|
|
138
|
+
"""Convierte un color hex a rgba con la opacidad especificada."""
|
|
75
139
|
if color.startswith("#") and len(color) == 9:
|
|
76
|
-
return color
|
|
140
|
+
return color # Ya tiene alpha
|
|
77
141
|
if color.startswith("#") and len(color) == 7:
|
|
78
142
|
hex_color = color[1:]
|
|
79
143
|
alpha = int(max(0.0, min(1.0, opacity)) * 255)
|
|
80
144
|
return f"#{alpha:02x}{hex_color}"
|
|
81
|
-
return color
|
|
145
|
+
return color # Retornar como esta si no es hex valido
|
|
82
146
|
|
|
83
147
|
|
|
84
148
|
def get_theme_file() -> Path:
|
|
85
|
-
|
|
149
|
+
"""Retorna la ruta del archivo de preferencia de tema."""
|
|
150
|
+
app_name = _get_app_name()
|
|
151
|
+
return Path.home() / ".g360" / f"{app_name}_config.json"
|
|
86
152
|
|
|
87
153
|
|
|
88
154
|
def _get_app_name() -> str:
|
|
155
|
+
"""Obtiene el nombre de la app desde skill.json o el nombre del directorio."""
|
|
89
156
|
try:
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
_data = json.load(_f)
|
|
96
|
-
return _data.get("name", "g360-app").replace("-", "_")
|
|
157
|
+
skill_path = _find_skill_json()
|
|
158
|
+
if skill_path and skill_path.exists():
|
|
159
|
+
with open(skill_path, encoding="utf-8") as f:
|
|
160
|
+
data = json.load(f)
|
|
161
|
+
return data.get("name", Path(__file__).resolve().parent.parent.parent.name)
|
|
97
162
|
except Exception:
|
|
98
163
|
pass
|
|
99
|
-
return
|
|
164
|
+
return Path(__file__).resolve().parent.parent.parent.name
|
|
100
165
|
|
|
101
166
|
|
|
102
167
|
def load_theme_preference() -> str:
|
|
168
|
+
"""Carga la preferencia de tema guardada (dark/light)."""
|
|
103
169
|
try:
|
|
104
170
|
config_file = get_theme_file()
|
|
105
171
|
if config_file.exists():
|
|
@@ -112,6 +178,7 @@ def load_theme_preference() -> str:
|
|
|
112
178
|
|
|
113
179
|
|
|
114
180
|
def save_theme_preference(mode: str):
|
|
181
|
+
"""Guarda la preferencia de tema."""
|
|
115
182
|
try:
|
|
116
183
|
config_file = get_theme_file()
|
|
117
184
|
config_file.parent.mkdir(parents=True, exist_ok=True)
|
|
@@ -120,3 +187,15 @@ def save_theme_preference(mode: str):
|
|
|
120
187
|
json.dump(config, f)
|
|
121
188
|
except Exception:
|
|
122
189
|
pass
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def set_brand_colors(colors: dict):
|
|
193
|
+
"""
|
|
194
|
+
Permite sobrescribir los colores de la marca en runtime.
|
|
195
|
+
Useful para apps que necesitan colores dinamicos.
|
|
196
|
+
"""
|
|
197
|
+
global G360_DEFAULTS
|
|
198
|
+
# Actualizar solo los modos que existan
|
|
199
|
+
for mode in ["dark", "light"]:
|
|
200
|
+
if mode in G360_DEFAULTS and mode in colors:
|
|
201
|
+
G360_DEFAULTS[mode].update(colors[mode])
|