g360-cli 1.3.0 → 1.4.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 +87 -5
- package/dist/g360.exe +0 -0
- package/package.json +6 -6
- package/src/assets/config/g360-skills.json +52 -0
- package/src/assets/config/project-types.json +16 -0
- package/src/assets/config/skills.json +12 -0
- package/src/assets/snippets/snippets.json +77 -0
- package/src/assets/templates/python-cli/build-portable.bat +33 -0
- package/src/assets/templates/python-cli/src/main.py +107 -3
- package/src/assets/templates/python-customtkinter/README.md +47 -0
- package/src/assets/templates/python-customtkinter/build-portable.bat +28 -0
- package/src/assets/templates/python-customtkinter/requirements.txt +1 -0
- package/src/assets/templates/python-customtkinter/run.bat +29 -0
- package/src/assets/templates/python-customtkinter/src/core/skill.json +22 -0
- package/src/assets/templates/python-customtkinter/src/main.py +123 -0
- package/src/assets/templates/python-flet/README.md +130 -0
- package/src/assets/templates/python-flet/build-portable.bat +42 -0
- package/src/assets/templates/python-flet/create_shortcut.vbs +35 -0
- package/src/assets/templates/python-flet/requirements.txt +3 -0
- package/src/assets/templates/python-flet/run.bat +81 -0
- package/src/assets/templates/python-flet/src/core/skill.json +21 -0
- package/src/assets/templates/python-flet/src/main.py +101 -0
- package/src/assets/templates/python-flet/src/test_app.py +103 -0
- package/src/assets/templates/python-flet-migrate/src/main.py +68 -0
- package/src/assets/templates/python-flet-migrate/src/migrate_tkinter.py +56 -0
- package/src/cli.js +5 -1
- package/src/commands/clean.js +51 -4
- package/src/commands/init.js +60 -8
- package/src/commands/signature.js +12 -8
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import flet as ft
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class MigratedApp:
|
|
5
|
+
def __init__(self, page: ft.Page):
|
|
6
|
+
self.page = page
|
|
7
|
+
self.setup_theme()
|
|
8
|
+
self.create_ui()
|
|
9
|
+
|
|
10
|
+
def setup_theme(self):
|
|
11
|
+
self.page.title = "G360 - Migrated App"
|
|
12
|
+
self.page.theme_mode = ft.ThemeMode.DARK
|
|
13
|
+
self.page.padding = 20
|
|
14
|
+
self.page.bgcolor = "#0b1220"
|
|
15
|
+
|
|
16
|
+
def create_ui(self):
|
|
17
|
+
self.page.add(
|
|
18
|
+
ft.Column([
|
|
19
|
+
self.create_header(),
|
|
20
|
+
self.create_main_content(),
|
|
21
|
+
], spacing=20, expand=True)
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
def create_header(self):
|
|
25
|
+
return ft.Container(
|
|
26
|
+
content=ft.Row([
|
|
27
|
+
ft.Text("G360 Migrated App", size=28, weight=ft.FontWeight.BOLD, color="#00d084"),
|
|
28
|
+
ft.Container(expand=True),
|
|
29
|
+
ft.Text("from tkinter/ctkinter", size=14, color="#94a3b8"),
|
|
30
|
+
]),
|
|
31
|
+
padding=20,
|
|
32
|
+
bgcolor="#151e2e",
|
|
33
|
+
border_radius=12
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
def create_main_content(self):
|
|
37
|
+
return ft.Container(
|
|
38
|
+
content=ft.Column([
|
|
39
|
+
ft.Text("Panel de Controles (antes tk.Button)", size=20, color="#f0f4f8"),
|
|
40
|
+
ft.Row([
|
|
41
|
+
ft.ElevatedButton("Aceptar", bgcolor="#00d084", color="#0b1220"),
|
|
42
|
+
ft.ElevatedButton("Cancelar", bgcolor="#ef4444", color="#ffffff"),
|
|
43
|
+
ft.ElevatedButton("Guardar", bgcolor="#00796B", color="#ffffff"),
|
|
44
|
+
], spacing=15),
|
|
45
|
+
ft.Container(height=30),
|
|
46
|
+
ft.Text("Campos de Entrada (antes tk.Entry)", size=20, color="#f0f4f8"),
|
|
47
|
+
ft.Column([
|
|
48
|
+
ft.TextField(label="Usuario", hint_text="Ingrese usuario", width=300),
|
|
49
|
+
ft.TextField(label="Contraseña", hint_text="Ingrese contraseña", password=True, width=300),
|
|
50
|
+
], spacing=15),
|
|
51
|
+
ft.Container(height=30),
|
|
52
|
+
ft.Text("Casillas y Opciones (antes tk.Checkbutton/tk.Radiobutton)", size=20, color="#f0f4f8"),
|
|
53
|
+
ft.Column([
|
|
54
|
+
ft.Checkbox(label="Recordarme"),
|
|
55
|
+
ft.Checkbox(label="Aceptar términos"),
|
|
56
|
+
], spacing=10),
|
|
57
|
+
], scroll=ft.ScrollMode.AUTO),
|
|
58
|
+
expand=True,
|
|
59
|
+
padding=20
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def main(page: ft.Page):
|
|
64
|
+
MigratedApp(page)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
if __name__ == "__main__":
|
|
68
|
+
ft.app(target=main)
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Migración de tkinter/ctkinter a Flet
|
|
3
|
+
Script de ayuda para convertir código existente
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
MAPPING = {
|
|
7
|
+
'tk.Label': 'ft.Text',
|
|
8
|
+
'tk.Button': 'ft.ElevatedButton',
|
|
9
|
+
'tk.Entry': 'ft.TextField',
|
|
10
|
+
'tk.Checkbutton': 'ft.Checkbox',
|
|
11
|
+
'tk.Radiobutton': 'ft.Radio',
|
|
12
|
+
'tk.Listbox': 'ft.ListView',
|
|
13
|
+
'tk.Frame': 'ft.Container',
|
|
14
|
+
'tk.LabelFrame': 'ft.Container with border',
|
|
15
|
+
'tk.Canvas': 'ft.Canvas',
|
|
16
|
+
'tk.Menu': 'ft.AppBar with actions',
|
|
17
|
+
'ctk.CTkButton': 'ft.ElevatedButton',
|
|
18
|
+
'ctk.CTkLabel': 'ft.Text',
|
|
19
|
+
'ctk.CTkEntry': 'ft.TextField',
|
|
20
|
+
'ctk.CTkFrame': 'ft.Container',
|
|
21
|
+
'ctk.CTkCheckBox': 'ft.Checkbox',
|
|
22
|
+
'ctk.CTkRadioButton': 'ft.Radio',
|
|
23
|
+
'ctk.CTkProgressBar': 'ft.ProgressBar',
|
|
24
|
+
'ctk.CTkSlider': 'ft.Slider',
|
|
25
|
+
'ctk.CTkSwitch': 'ft.Switch',
|
|
26
|
+
'ctk.CTkComboBox': 'ft.Dropdown',
|
|
27
|
+
'ctk.CTkTextbox': 'ft.TextField multiline',
|
|
28
|
+
'ctk.CTkTabview': 'ft.Tabs',
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
LAYOUT_MAPPING = {
|
|
32
|
+
'pack()': 'page.add()',
|
|
33
|
+
'pack(fill=..., expand=...)': 'expand=True, alignment=...',
|
|
34
|
+
'grid(row=..., column=...)': 'use Column with Row for grid-like',
|
|
35
|
+
'place(x=..., y=...)': 'position in Container',
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
THEME_MAPPING = {
|
|
39
|
+
'bg': 'bgcolor',
|
|
40
|
+
'fg': 'color',
|
|
41
|
+
'font': 'size + weight',
|
|
42
|
+
'relief': 'border_style',
|
|
43
|
+
'padx/pady': 'padding',
|
|
44
|
+
'width': 'width',
|
|
45
|
+
'height': 'height',
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
def convert_tkinter_to_flet(code: str) -> str:
|
|
49
|
+
result = code
|
|
50
|
+
for tk_widget, flet_widget in MAPPING.items():
|
|
51
|
+
result = result.replace(tk_widget, flet_widget)
|
|
52
|
+
return result
|
|
53
|
+
|
|
54
|
+
print("Mapeo de tkinter/ctkinter a Flet cargado")
|
|
55
|
+
print("Usa: from migrate_tkinter import convert_tkinter_to_flet")
|
|
56
|
+
print("Para convertir código existente a Flet")
|
package/src/cli.js
CHANGED
|
@@ -31,10 +31,12 @@ program
|
|
|
31
31
|
.command('init')
|
|
32
32
|
.argument('<name>', 'Project name')
|
|
33
33
|
.option('-t, --template <type>', 'Project template type', 'web-pwa')
|
|
34
|
-
.option('-s, --skill <skill>', 'Skill to use (corporativo, corporativo-movil, moderno, moderno-movil, minimalista, custom)', 'corporativo-movil')
|
|
34
|
+
.option('-s, --skill <skill>', 'Skill to use (corporativo, corporativo-movil, moderno, moderno-movil, minimalista, custom, flet-desktop)', 'corporativo-movil')
|
|
35
35
|
.option('-d, --dir <path>', 'Target directory', '.')
|
|
36
36
|
.option('--dry-run', 'Preview without creating files')
|
|
37
37
|
.option('--force', 'Overwrite existing files')
|
|
38
|
+
.option('--portable', 'Create portable version (for python-flet, python-cli)')
|
|
39
|
+
.option('--no-portable', 'Skip portable version creation')
|
|
38
40
|
.action(init);
|
|
39
41
|
|
|
40
42
|
program
|
|
@@ -81,6 +83,8 @@ program
|
|
|
81
83
|
.option('--orphans', 'Remove orphan files (unreferenced)')
|
|
82
84
|
.option('--organize', 'Show misplaced files suggestions')
|
|
83
85
|
.option('--all', 'Run all cleanup tasks')
|
|
86
|
+
.option('--github', 'Prepare repo for GitHub (update .gitignore)')
|
|
87
|
+
.option('--pre-push', 'Alias for --github (prepare before push)')
|
|
84
88
|
.action(clean);
|
|
85
89
|
|
|
86
90
|
program
|
package/src/commands/clean.js
CHANGED
|
@@ -21,14 +21,16 @@ const DEAD_PATTERNS = [
|
|
|
21
21
|
const DUPLICATE_EXTENSIONS = ['.bak', '.orig', '.swp', '.swo', '~'];
|
|
22
22
|
|
|
23
23
|
export async function clean(projectPath, options) {
|
|
24
|
-
const {
|
|
25
|
-
dryRun = false,
|
|
24
|
+
const {
|
|
25
|
+
dryRun = false,
|
|
26
26
|
force = false,
|
|
27
27
|
dead = false,
|
|
28
28
|
duplicates = false,
|
|
29
29
|
orphans = false,
|
|
30
30
|
organize = false,
|
|
31
|
-
all = false
|
|
31
|
+
all = false,
|
|
32
|
+
github = false,
|
|
33
|
+
prePush = false
|
|
32
34
|
} = options;
|
|
33
35
|
|
|
34
36
|
const targetDir = path.join(process.cwd(), projectPath);
|
|
@@ -123,8 +125,53 @@ export async function clean(projectPath, options) {
|
|
|
123
125
|
return;
|
|
124
126
|
}
|
|
125
127
|
|
|
128
|
+
if (github || prePush) {
|
|
129
|
+
console.log(chalk.bold.cyan('\n📦 Modo Pre-Push GitHub\n'));
|
|
130
|
+
console.log(chalk.gray('Limpiando para subir a repositorio remoto...\n'));
|
|
131
|
+
|
|
132
|
+
const gitIgnore = path.join(targetDir, '.gitignore');
|
|
133
|
+
const requiredIgnores = [
|
|
134
|
+
'node_modules/',
|
|
135
|
+
'__pycache__/',
|
|
136
|
+
'*.pyc',
|
|
137
|
+
'.env',
|
|
138
|
+
'dist/',
|
|
139
|
+
'build/',
|
|
140
|
+
'*.log',
|
|
141
|
+
'.cache/',
|
|
142
|
+
'coverage/',
|
|
143
|
+
'*.egg-info/',
|
|
144
|
+
'.pytest_cache/'
|
|
145
|
+
];
|
|
146
|
+
|
|
147
|
+
if (fs.existsSync(gitIgnore)) {
|
|
148
|
+
const currentIgnore = await fs.readFile(gitIgnore, 'utf-8');
|
|
149
|
+
let updatedIgnore = currentIgnore;
|
|
150
|
+
|
|
151
|
+
for (const pattern of requiredIgnores) {
|
|
152
|
+
if (!currentIgnore.includes(pattern)) {
|
|
153
|
+
updatedIgnore += `\n${pattern}`;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
await fs.writeFile(gitIgnore, updatedIgnore);
|
|
158
|
+
console.log(chalk.green(' ✅ .gitignore actualizado'));
|
|
159
|
+
} else {
|
|
160
|
+
await fs.writeFile(gitIgnore, requiredIgnores.join('\n') + '\n');
|
|
161
|
+
console.log(chalk.green(' ✅ .gitignore creado'));
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
console.log(chalk.green('\n✅ Repo listo para GitHub!\n'));
|
|
165
|
+
console.log(chalk.gray('Para subir:'));
|
|
166
|
+
console.log(` ${chalk.cyan('git add .')}`);
|
|
167
|
+
console.log(` ${chalk.cyan('git commit -m "clean: preparing for push"')}`);
|
|
168
|
+
console.log(` ${chalk.cyan('git push')}\n`);
|
|
169
|
+
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
|
|
126
173
|
console.log(chalk.cyan('\n🚀 Aplicando limpieza...'));
|
|
127
|
-
|
|
174
|
+
|
|
128
175
|
let cleaned = 0;
|
|
129
176
|
|
|
130
177
|
for (const file of issues.dead) {
|
package/src/commands/init.js
CHANGED
|
@@ -5,19 +5,61 @@ import { fileURLToPath } from 'url';
|
|
|
5
5
|
import { manifest } from '../lib/manifest.js';
|
|
6
6
|
import { progress } from '../lib/progress.js';
|
|
7
7
|
import { setSkill } from './set-skill.js';
|
|
8
|
+
import inquirer from 'inquirer';
|
|
8
9
|
|
|
9
10
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
10
11
|
|
|
12
|
+
const PORTABLE_TEMPLATES = ['python-flet', 'python-flet-migrate', 'python-cli', 'python-customtkinter'];
|
|
13
|
+
|
|
14
|
+
async function askPortableOption(template) {
|
|
15
|
+
if (!PORTABLE_TEMPLATES.includes(template)) {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const answers = await inquirer.prompt([
|
|
20
|
+
{
|
|
21
|
+
type: 'confirm',
|
|
22
|
+
name: 'portable',
|
|
23
|
+
message: '¿Deseas crear una versión portable del proyecto? (ejecutable standalone)',
|
|
24
|
+
default: false
|
|
25
|
+
}
|
|
26
|
+
]);
|
|
27
|
+
|
|
28
|
+
return answers.portable;
|
|
29
|
+
}
|
|
30
|
+
|
|
11
31
|
export async function init(name, options) {
|
|
12
|
-
const {
|
|
32
|
+
const {
|
|
33
|
+
template = 'web-pwa',
|
|
34
|
+
skill = 'corporativo-movil',
|
|
35
|
+
dir = '.',
|
|
36
|
+
dryRun = false,
|
|
37
|
+
force = false,
|
|
38
|
+
portable = null
|
|
39
|
+
} = options;
|
|
40
|
+
|
|
13
41
|
const targetDir = path.join(process.cwd(), dir, name);
|
|
14
|
-
|
|
42
|
+
|
|
15
43
|
console.log(chalk.bold.cyan('\n🚀 G360 Project Initialization\n'));
|
|
16
44
|
console.log(`Project: ${chalk.yellow(name)}`);
|
|
17
45
|
console.log(`Template: ${chalk.blue(template)}`);
|
|
18
46
|
console.log(`Skill: ${chalk.magenta(skill)}`);
|
|
19
47
|
console.log(`Target: ${chalk.gray(targetDir)}\n`);
|
|
20
48
|
|
|
49
|
+
let wantPortable = false;
|
|
50
|
+
|
|
51
|
+
if (portable === true) {
|
|
52
|
+
wantPortable = true;
|
|
53
|
+
console.log(chalk.yellow('📦 Versión portable: SÍ (flag)\n'));
|
|
54
|
+
} else if (portable === false) {
|
|
55
|
+
console.log(chalk.gray('📦 Versión portable: NO\n'));
|
|
56
|
+
} else {
|
|
57
|
+
wantPortable = await askPortableOption(template);
|
|
58
|
+
if (wantPortable) {
|
|
59
|
+
console.log(chalk.yellow('📦 Versión portable: SÍ\n'));
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
21
63
|
const templatesPath = path.join(__dirname, '../assets/templates');
|
|
22
64
|
|
|
23
65
|
if (!fs.existsSync(templatesPath)) {
|
|
@@ -49,21 +91,31 @@ export async function init(name, options) {
|
|
|
49
91
|
const progressBar = progress('Creating project...');
|
|
50
92
|
|
|
51
93
|
try {
|
|
52
|
-
// Asegurar que el directorio base existe
|
|
53
94
|
await fs.ensureDir(path.dirname(targetDir));
|
|
54
95
|
|
|
55
96
|
await fs.copy(templateDir, targetDir);
|
|
56
|
-
await manifest.init(targetDir, { name, template, version: '1.0.0' });
|
|
57
|
-
|
|
58
|
-
|
|
97
|
+
await manifest.init(targetDir, { name, template, version: '1.0.0', portable: wantPortable });
|
|
98
|
+
|
|
99
|
+
if (wantPortable) {
|
|
100
|
+
const portableDir = path.join(targetDir, 'portable');
|
|
101
|
+
await fs.ensureDir(portableDir);
|
|
102
|
+
const buildScript = path.join(targetDir, 'build-portable.bat');
|
|
103
|
+
if (fs.existsSync(buildScript)) {
|
|
104
|
+
await fs.copy(buildScript, path.join(portableDir, 'build.bat'));
|
|
105
|
+
}
|
|
106
|
+
console.log(chalk.gray(' 📦 Carpeta portable/ creada para builds\n'));
|
|
107
|
+
}
|
|
108
|
+
|
|
59
109
|
await setSkill(skill, { force: true, verbose: options.verbose, cwd: targetDir });
|
|
60
110
|
|
|
61
|
-
// Crear estructura estándar G360 para auditoría y desarrollo guiado
|
|
62
111
|
await createG360Structure(targetDir, templatesPath);
|
|
63
112
|
|
|
64
113
|
progressBar.stop();
|
|
65
|
-
|
|
114
|
+
|
|
66
115
|
console.log(chalk.green('\n✅ Project created successfully!\n'));
|
|
116
|
+
if (wantPortable) {
|
|
117
|
+
console.log(chalk.yellow('📦 Versión portable habilitada\n'));
|
|
118
|
+
}
|
|
67
119
|
console.log(chalk.gray('Next steps:'));
|
|
68
120
|
console.log(` ${chalk.cyan('cd')} ${name}`);
|
|
69
121
|
console.log(` ${chalk.cyan('g360 bring')}`);
|
|
@@ -21,20 +21,24 @@ export async function signature(command, options) {
|
|
|
21
21
|
|
|
22
22
|
let htmlContent = fs.readFileSync(indexHtmlPath, 'utf8');
|
|
23
23
|
|
|
24
|
-
// Verificar si ya esta instalado
|
|
25
|
-
if (htmlContent.includes('g360-signature') && !force) {
|
|
26
|
-
console.log(chalk.yellow('⚠️ g360-signature ya se encuentra instalado'));
|
|
27
|
-
console.log(chalk.gray('Usa --force para reinstalar'));
|
|
28
|
-
return;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
// Agregar script CDN antes del cierre del body
|
|
32
24
|
const scriptTag = ' <script type="module" src="https://unpkg.com/g360-signature@latest/index.js"></script>';
|
|
33
25
|
const signatureComponent = `
|
|
34
26
|
<!-- Firma Oficial G360 -->
|
|
35
27
|
<g360-signature mode="powered" style="position: fixed; bottom: 16px; right: 16px; z-index: 99999;"></g360-signature>
|
|
36
28
|
`;
|
|
37
29
|
|
|
30
|
+
// Si --force es true, eliminamos cualquier instancia existente para una reinstalación limpia.
|
|
31
|
+
// De lo contrario, si ya está instalado y no se fuerza, salimos.
|
|
32
|
+
if (force) {
|
|
33
|
+
htmlContent = htmlContent.replace(new RegExp(scriptTag.trim().replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g'), '');
|
|
34
|
+
htmlContent = htmlContent.replace(new RegExp(signatureComponent.trim().replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g'), '');
|
|
35
|
+
htmlContent = htmlContent.replace(/\n\s*\n/g, '\n'); // Limpiar posibles líneas vacías extra
|
|
36
|
+
} else if (htmlContent.includes('g360-signature')) {
|
|
37
|
+
console.log(chalk.yellow('⚠️ g360-signature ya se encuentra instalado'));
|
|
38
|
+
console.log(chalk.gray('Usa --force para reinstalar'));
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
|
|
38
42
|
if (!htmlContent.includes(scriptTag)) {
|
|
39
43
|
htmlContent = htmlContent.replace('</body>', `${scriptTag}\n </body>`);
|
|
40
44
|
}
|