505 lines
11 KiB
Markdown
505 lines
11 KiB
Markdown
|
|
# 📋 Resumen de Mejoras - EnvManager y Sistema de Archivos
|
||
|
|
|
||
|
|
## 🎯 Problemas Resueltos
|
||
|
|
|
||
|
|
### 1. ❌ Problema de Eliminación de Archivos
|
||
|
|
|
||
|
|
**Problema:** No se podían eliminar archivos, no estaba claro si era por permisos.
|
||
|
|
|
||
|
|
**Solución Implementada:**
|
||
|
|
- ✅ Mejorado el manejo de errores en `delete_file` y `delete_folder` (Rust)
|
||
|
|
- ✅ Ahora muestra mensajes específicos según el tipo de error:
|
||
|
|
- **Permiso denegado**: "Ejecuta el editor como administrador"
|
||
|
|
- **Archivo no encontrado**: "El archivo no existe"
|
||
|
|
- **No es un archivo**: "La ruta no es un archivo válido"
|
||
|
|
|
||
|
|
**Ubicación:** `AEditor/src-tauri/src/lib.rs` líneas 468-525
|
||
|
|
|
||
|
|
**Cómo usar:**
|
||
|
|
```bash
|
||
|
|
# Si ves "Permiso denegado", ejecuta como administrador:
|
||
|
|
# Windows: Click derecho → Ejecutar como administrador
|
||
|
|
# O usa PowerShell con privilegios elevados
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### 2. 🔐 Problema del EnvManager
|
||
|
|
|
||
|
|
**Problema:**
|
||
|
|
- No leía correctamente el .env
|
||
|
|
- No guardaba bien los cambios
|
||
|
|
- No mostraba las ubicaciones exactas donde se usan las variables
|
||
|
|
|
||
|
|
**Soluciones Implementadas:**
|
||
|
|
|
||
|
|
#### a) Lectura Mejorada
|
||
|
|
```typescript
|
||
|
|
// Ahora con logs detallados para debugging
|
||
|
|
console.log('📂 Cargando .env desde:', props.projectRoot);
|
||
|
|
console.log('✅ .env cargado, contenido:', content);
|
||
|
|
```
|
||
|
|
|
||
|
|
#### b) Parseo Mejorado
|
||
|
|
- ✅ Acepta valores vacíos: `KEY=`
|
||
|
|
- ✅ Maneja comillas: `KEY="value with spaces"`
|
||
|
|
- ✅ Ignora comentarios: `# This is a comment`
|
||
|
|
- ✅ Valida nombres de variables: Solo `A-Z`, `0-9`, `_`
|
||
|
|
|
||
|
|
#### c) Guardado Mejorado
|
||
|
|
```typescript
|
||
|
|
// Ahora sincroniza antes de guardar para evitar pérdida de datos
|
||
|
|
isUpdatingRaw.value = true;
|
||
|
|
parseEnvContent(rawEnvContent.value);
|
||
|
|
await nextTick();
|
||
|
|
isUpdatingRaw.value = false;
|
||
|
|
```
|
||
|
|
|
||
|
|
#### d) **NUEVO:** Ubicaciones Exactas
|
||
|
|
|
||
|
|
**Backend Rust - Nueva función:**
|
||
|
|
```rust
|
||
|
|
#[tauri::command]
|
||
|
|
fn scan_env_variables_with_locations(project_root: String) -> Result<Vec<VarLocation>, String>
|
||
|
|
```
|
||
|
|
|
||
|
|
**Devuelve:**
|
||
|
|
```json
|
||
|
|
[
|
||
|
|
{
|
||
|
|
"variable": "DATABASE_URL",
|
||
|
|
"file": "src/prisma.ts",
|
||
|
|
"line": 5,
|
||
|
|
"snippet": "const url = process.env.DATABASE_URL"
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"variable": "API_KEY",
|
||
|
|
"file": "src/server/api.ts",
|
||
|
|
"line": 12,
|
||
|
|
"snippet": "headers: { 'X-API-Key': process.env.API_KEY }"
|
||
|
|
}
|
||
|
|
]
|
||
|
|
```
|
||
|
|
|
||
|
|
**Vista en el UI:**
|
||
|
|
```
|
||
|
|
📍 Usada en 2 ubicación(es)
|
||
|
|
├─ src/prisma.ts:5 → const url = process.env.DATABASE_URL
|
||
|
|
└─ src/server/api.ts:12 → headers: { 'X-API-Key': process.env.API_KEY }
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 📚 Documentación de Funciones
|
||
|
|
|
||
|
|
### 🔍 `loadEnvFile()`
|
||
|
|
**¿Qué hace?**
|
||
|
|
1. Lee el archivo .env del proyecto
|
||
|
|
2. Muestra el contenido en el editor raw
|
||
|
|
3. Parsea las variables al formato de objetos
|
||
|
|
4. Si no existe, inicia vacío
|
||
|
|
|
||
|
|
**Errores comunes:**
|
||
|
|
- `No such file`: Normal en proyectos nuevos
|
||
|
|
- `Permission denied`: Necesitas permisos de admin
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### 🔍 `parseEnvContent(content, markAsChanged)`
|
||
|
|
**¿Qué hace?**
|
||
|
|
Convierte texto plano a objeto JavaScript.
|
||
|
|
|
||
|
|
**Ejemplo:**
|
||
|
|
```env
|
||
|
|
# Input (texto)
|
||
|
|
DATABASE_URL=postgres://localhost:5432
|
||
|
|
API_KEY="abc123"
|
||
|
|
DEBUG=true
|
||
|
|
|
||
|
|
# Output (objeto)
|
||
|
|
{
|
||
|
|
"DATABASE_URL": "postgres://localhost:5432",
|
||
|
|
"API_KEY": "abc123",
|
||
|
|
"DEBUG": "true"
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**Reglas:**
|
||
|
|
- Ignora líneas que empiezan con `#`
|
||
|
|
- Ignora líneas vacías
|
||
|
|
- Remueve comillas del valor
|
||
|
|
- Solo acepta claves válidas: `[A-Z_][A-Z0-9_]*`
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### 🔍 `scanProjectVars()`
|
||
|
|
**¿Qué hace?**
|
||
|
|
Busca en todo el código fuente las variables de entorno.
|
||
|
|
|
||
|
|
**Proceso:**
|
||
|
|
1. Escanea archivos `.ts`, `.js`, `.prisma`
|
||
|
|
2. Busca patrones: `process.env.VARIABLE_NAME`
|
||
|
|
3. Devuelve ubicaciones exactas (archivo, línea, código)
|
||
|
|
4. Auto-agrega variables faltantes
|
||
|
|
|
||
|
|
**Ejemplo de uso:**
|
||
|
|
```typescript
|
||
|
|
// En tu código:
|
||
|
|
const dbUrl = process.env.DATABASE_URL;
|
||
|
|
const apiKey = process.env.API_KEY;
|
||
|
|
|
||
|
|
// EnvManager detecta:
|
||
|
|
// ✅ DATABASE_URL (src/prisma.ts:5)
|
||
|
|
// ✅ API_KEY (src/server/api.ts:12)
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### 🔍 `updateRawContent()`
|
||
|
|
**¿Qué hace?**
|
||
|
|
Convierte el objeto de variables a texto plano.
|
||
|
|
|
||
|
|
**Reglas de formato:**
|
||
|
|
- Si el valor tiene espacios → agrega comillas: `KEY="value with spaces"`
|
||
|
|
- Si no tiene espacios → sin comillas: `KEY=value`
|
||
|
|
- Si tiene `#` o `=` → agrega comillas: `KEY="value#123"`
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### 🔍 `syncFromRaw()`
|
||
|
|
**¿Qué hace?**
|
||
|
|
Sincroniza del editor raw al formulario.
|
||
|
|
|
||
|
|
**¿Por qué el flag `isUpdatingRaw`?**
|
||
|
|
|
||
|
|
**Sin flag (loop infinito):**
|
||
|
|
```
|
||
|
|
1. Cambias raw → se parsea → actualiza envVariables
|
||
|
|
2. Watch de envVariables → actualiza raw
|
||
|
|
3. raw cambia → se parsea → actualiza envVariables
|
||
|
|
4. Watch de envVariables → actualiza raw
|
||
|
|
5. Loop infinito 💥
|
||
|
|
```
|
||
|
|
|
||
|
|
**Con flag (correcto):**
|
||
|
|
```
|
||
|
|
1. Flag ON
|
||
|
|
2. Cambias raw → se parsea → actualiza envVariables
|
||
|
|
3. Watch ve flag ON → NO actualiza raw
|
||
|
|
4. Flag OFF
|
||
|
|
5. Siguiente cambio funciona normal ✅
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### 💾 `saveEnvFile()`
|
||
|
|
**¿Qué hace?**
|
||
|
|
Escribe los cambios al disco.
|
||
|
|
|
||
|
|
**Proceso:**
|
||
|
|
1. Sincroniza del raw (por si editaste ahí)
|
||
|
|
2. Llama a Rust `write_env_file`
|
||
|
|
3. Escribe archivo en `{projectRoot}/.env`
|
||
|
|
4. Marca sin cambios
|
||
|
|
|
||
|
|
**Ubicación del archivo:**
|
||
|
|
```
|
||
|
|
Windows: C:/Users/Shnimlz/Documents/GitHub/amayo/.env
|
||
|
|
Linux/Mac: /home/user/projects/amayo/.env
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 🧩 Pregunta: ¿Sistema de Extensiones?
|
||
|
|
|
||
|
|
**TL;DR:** Sí, es posible pero avanzado. Aquí está el plan:
|
||
|
|
|
||
|
|
### Opción 1: Sistema Simple de Plugins (Más Fácil)
|
||
|
|
|
||
|
|
**Concepto:**
|
||
|
|
```
|
||
|
|
plugins/
|
||
|
|
├── theme-dracula/
|
||
|
|
│ ├── plugin.json
|
||
|
|
│ └── theme.css
|
||
|
|
├── snippets-react/
|
||
|
|
│ ├── plugin.json
|
||
|
|
│ └── snippets.json
|
||
|
|
└── formatter-prettier/
|
||
|
|
├── plugin.json
|
||
|
|
└── format.js
|
||
|
|
```
|
||
|
|
|
||
|
|
**Estructura de plugin:**
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"name": "theme-dracula",
|
||
|
|
"version": "1.0.0",
|
||
|
|
"type": "theme",
|
||
|
|
"main": "theme.css",
|
||
|
|
"author": "You",
|
||
|
|
"description": "Dracula theme for Monaco"
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**Implementación:**
|
||
|
|
1. **Manager de Plugins** (Vue component)
|
||
|
|
```typescript
|
||
|
|
class PluginManager {
|
||
|
|
plugins: Plugin[] = [];
|
||
|
|
|
||
|
|
async loadPlugin(path: string) {
|
||
|
|
const config = await readPluginConfig(path);
|
||
|
|
if (config.type === 'theme') {
|
||
|
|
await loadTheme(config.main);
|
||
|
|
} else if (config.type === 'snippets') {
|
||
|
|
await loadSnippets(config.main);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
2. **UI de Extensiones**
|
||
|
|
```vue
|
||
|
|
<div class="extensions-panel">
|
||
|
|
<h2>🧩 Extensiones Instaladas</h2>
|
||
|
|
<div v-for="plugin in plugins">
|
||
|
|
<h3>{{ plugin.name }}</h3>
|
||
|
|
<button @click="togglePlugin(plugin)">
|
||
|
|
{{ plugin.enabled ? 'Desactivar' : 'Activar' }}
|
||
|
|
</button>
|
||
|
|
</div>
|
||
|
|
<button @click="installFromFile">
|
||
|
|
📦 Instalar desde ZIP
|
||
|
|
</button>
|
||
|
|
</div>
|
||
|
|
```
|
||
|
|
|
||
|
|
3. **Backend Rust**
|
||
|
|
```rust
|
||
|
|
#[tauri::command]
|
||
|
|
fn list_plugins(app_data_dir: String) -> Vec<PluginInfo> {
|
||
|
|
// Escanear carpeta plugins/
|
||
|
|
}
|
||
|
|
|
||
|
|
#[tauri::command]
|
||
|
|
fn install_plugin(zip_path: String) -> Result<(), String> {
|
||
|
|
// Extraer ZIP a plugins/
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**Tipos de Extensiones:**
|
||
|
|
- 🎨 **Temas**: CSS para Monaco
|
||
|
|
- 📝 **Snippets**: JSON con snippets personalizados
|
||
|
|
- 🔧 **Formatters**: JS que formatea código
|
||
|
|
- 🌐 **Language Support**: Definiciones de TypeScript
|
||
|
|
- 🎯 **Commands**: Nuevos comandos personalizados
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### Opción 2: Sistema Avanzado tipo VS Code (Más Complejo)
|
||
|
|
|
||
|
|
**API de Extensiones:**
|
||
|
|
```typescript
|
||
|
|
// extension-api.d.ts
|
||
|
|
export interface Extension {
|
||
|
|
activate(context: ExtensionContext): void;
|
||
|
|
deactivate(): void;
|
||
|
|
}
|
||
|
|
|
||
|
|
export interface ExtensionContext {
|
||
|
|
subscriptions: Disposable[];
|
||
|
|
workspaceState: Memento;
|
||
|
|
globalState: Memento;
|
||
|
|
}
|
||
|
|
|
||
|
|
export namespace commands {
|
||
|
|
export function registerCommand(
|
||
|
|
command: string,
|
||
|
|
callback: (...args: any[]) => any
|
||
|
|
): Disposable;
|
||
|
|
}
|
||
|
|
|
||
|
|
export namespace languages {
|
||
|
|
export function registerCompletionItemProvider(
|
||
|
|
selector: DocumentSelector,
|
||
|
|
provider: CompletionItemProvider
|
||
|
|
): Disposable;
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**Ejemplo de extensión:**
|
||
|
|
```typescript
|
||
|
|
// my-extension/src/extension.ts
|
||
|
|
import * as vscode from '@editor/api';
|
||
|
|
|
||
|
|
export function activate(context: vscode.ExtensionContext) {
|
||
|
|
// Registrar comando
|
||
|
|
let disposable = vscode.commands.registerCommand(
|
||
|
|
'myExtension.helloWorld',
|
||
|
|
() => {
|
||
|
|
vscode.window.showInformationMessage('Hello from Extension!');
|
||
|
|
}
|
||
|
|
);
|
||
|
|
|
||
|
|
context.subscriptions.push(disposable);
|
||
|
|
|
||
|
|
// Registrar autocompletado
|
||
|
|
context.subscriptions.push(
|
||
|
|
vscode.languages.registerCompletionItemProvider('typescript', {
|
||
|
|
provideCompletionItems() {
|
||
|
|
return [
|
||
|
|
{
|
||
|
|
label: 'myFunction',
|
||
|
|
kind: vscode.CompletionItemKind.Function,
|
||
|
|
insertText: 'myFunction($1)',
|
||
|
|
}
|
||
|
|
];
|
||
|
|
}
|
||
|
|
})
|
||
|
|
);
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**Arquitectura:**
|
||
|
|
```
|
||
|
|
Extension Host (Worker)
|
||
|
|
├── Sandbox aislado para cada extensión
|
||
|
|
├── Comunicación vía postMessage
|
||
|
|
└── API restringida por seguridad
|
||
|
|
|
||
|
|
Main Thread (Editor)
|
||
|
|
├── Recibe comandos del Extension Host
|
||
|
|
├── Ejecuta cambios en Monaco
|
||
|
|
└── Maneja UI
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### 🚀 Plan de Implementación Recomendado
|
||
|
|
|
||
|
|
**Fase 1: Sistema Simple** (1-2 semanas)
|
||
|
|
1. ✅ Crear carpeta `plugins/` en appData
|
||
|
|
2. ✅ Componente `ExtensionManager.vue`
|
||
|
|
3. ✅ Backend Rust para leer/instalar
|
||
|
|
4. ✅ Soporte para temas CSS
|
||
|
|
5. ✅ Soporte para snippets JSON
|
||
|
|
|
||
|
|
**Fase 2: Marketplace Básico** (2-3 semanas)
|
||
|
|
1. ✅ UI de búsqueda de extensiones
|
||
|
|
2. ✅ Servidor simple con lista de extensiones
|
||
|
|
3. ✅ Instalación desde URL
|
||
|
|
4. ✅ Auto-actualización
|
||
|
|
|
||
|
|
**Fase 3: API Avanzada** (1-2 meses)
|
||
|
|
1. ✅ Extension Host con Workers
|
||
|
|
2. ✅ API tipo VS Code
|
||
|
|
3. ✅ Sandboxing de seguridad
|
||
|
|
4. ✅ Debugging de extensiones
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### 📦 Ejemplo Completo: Extensión de Tema
|
||
|
|
|
||
|
|
**Estructura:**
|
||
|
|
```
|
||
|
|
theme-dracula.zip
|
||
|
|
├── manifest.json
|
||
|
|
├── theme.json
|
||
|
|
└── README.md
|
||
|
|
```
|
||
|
|
|
||
|
|
**manifest.json:**
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"name": "Dracula Theme",
|
||
|
|
"id": "dracula-theme",
|
||
|
|
"version": "1.0.0",
|
||
|
|
"publisher": "dracula",
|
||
|
|
"description": "Dark theme for vampires",
|
||
|
|
"type": "theme",
|
||
|
|
"main": "theme.json",
|
||
|
|
"engines": {
|
||
|
|
"aeditor": "^1.0.0"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**theme.json:**
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"name": "Dracula",
|
||
|
|
"type": "dark",
|
||
|
|
"colors": {
|
||
|
|
"editor.background": "#282a36",
|
||
|
|
"editor.foreground": "#f8f8f2",
|
||
|
|
"editorCursor.foreground": "#f8f8f0",
|
||
|
|
"editor.lineHighlightBackground": "#44475a"
|
||
|
|
},
|
||
|
|
"tokenColors": [
|
||
|
|
{
|
||
|
|
"scope": ["comment"],
|
||
|
|
"settings": {
|
||
|
|
"foreground": "#6272a4",
|
||
|
|
"fontStyle": "italic"
|
||
|
|
}
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"scope": ["string"],
|
||
|
|
"settings": {
|
||
|
|
"foreground": "#f1fa8c"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
]
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**Instalación:**
|
||
|
|
1. Usuario descarga `theme-dracula.zip`
|
||
|
|
2. Click en "Instalar Extensión"
|
||
|
|
3. Selecciona el ZIP
|
||
|
|
4. Backend extrae a `plugins/dracula-theme/`
|
||
|
|
5. Frontend carga el tema
|
||
|
|
6. Usuario lo activa desde la UI
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 🎓 Resumen
|
||
|
|
|
||
|
|
### Mejoras Completadas ✅
|
||
|
|
1. **Sistema de eliminación** con errores detallados
|
||
|
|
2. **EnvManager mejorado:**
|
||
|
|
- Lectura robusta del .env
|
||
|
|
- Parseo mejorado con logs
|
||
|
|
- Guardado con sincronización
|
||
|
|
- **Ubicaciones exactas** de cada variable (archivo, línea, código)
|
||
|
|
- Documentación completa de cada función
|
||
|
|
|
||
|
|
### Sistema de Extensiones 🚀
|
||
|
|
- **Factible:** Sí, totalmente posible
|
||
|
|
- **Complejidad:** Media-Alta
|
||
|
|
- **Recomendación:** Empezar con sistema simple
|
||
|
|
- **Beneficios:**
|
||
|
|
- Comunidad puede crear extensiones
|
||
|
|
- Personalización infinita
|
||
|
|
- Marketplace propio
|
||
|
|
- Competencia con VS Code en features
|
||
|
|
|
||
|
|
### Siguiente Paso Sugerido
|
||
|
|
1. Crear prototipo del ExtensionManager
|
||
|
|
2. Implementar soporte básico de temas
|
||
|
|
3. Si funciona bien, expandir a otros tipos
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 📞 Necesitas Ayuda?
|
||
|
|
|
||
|
|
Si quieres implementar el sistema de extensiones, puedo ayudarte con:
|
||
|
|
1. Arquitectura detallada
|
||
|
|
2. Código del ExtensionManager
|
||
|
|
3. API de extensiones
|
||
|
|
4. Ejemplos de extensiones
|
||
|
|
5. Sistema de marketplace
|
||
|
|
|
||
|
|
¡Solo pregunta! 🚀
|