Archive: System v4 - Estado al 2024-12-24
This commit is contained in:
674
v4-archive/mind-link/docs/interfaz_mindlink v0.md
Normal file
674
v4-archive/mind-link/docs/interfaz_mindlink v0.md
Normal file
@@ -0,0 +1,674 @@
|
||||
# INTERFAZ MML
|
||||
## Mindmap Linking - Capa de Interfaz
|
||||
|
||||
---
|
||||
|
||||
## 1. Concepto Visual
|
||||
|
||||
### 1.1 Referencia
|
||||
|
||||
Estética tipo **MindNode**:
|
||||
- Diseño limpio y minimalista
|
||||
- Líneas curvas orgánicas (bezier)
|
||||
- Colores vibrantes por rama
|
||||
- Tipografía sans-serif moderna
|
||||
|
||||
### 1.2 Principios
|
||||
|
||||
| Principio | Aplicación |
|
||||
|-----------|------------|
|
||||
| Simplicidad | Sin elementos decorativos innecesarios |
|
||||
| Jerarquía | Nodos nivel 1 destacan, subnodos secundarios |
|
||||
| Color | Cada rama tiene identidad cromática |
|
||||
| Espacio | Generoso, respira |
|
||||
|
||||
---
|
||||
|
||||
## 2. Estructura Visual
|
||||
|
||||
### 2.1 Elementos
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ │
|
||||
│ [Header - Título del proyecto (opcional)] │
|
||||
│ │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ NODOS NIVEL 1 LÍNEAS SUBNODOS │
|
||||
│ (izquierda) (curvas) (derecha) │
|
||||
│ │
|
||||
│ Texto subrayado ────────────────► Imagen 16:9 │
|
||||
│ con color + título │
|
||||
│ + botones │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 2.2 Layout Desktop (>768px)
|
||||
|
||||
```
|
||||
╭─────────────────────────────────────────────────────────────────╮
|
||||
│ Proyecto Demo HST │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ╭─────────────╮ │
|
||||
│ ╭─────│ IMAGEN │ │
|
||||
│ Arquitectura ───────────────┤ ╰─────────────╯ │
|
||||
│ │ plano.pdf │
|
||||
│ │ │
|
||||
│ │ ╭─────────────╮ │
|
||||
│ ╰─────│ IMAGEN │ │
|
||||
│ ╰─────────────╯ │
|
||||
│ secciones.pdf │
|
||||
│ │
|
||||
│ ╭─────────────╮ │
|
||||
│ ╭─────│ IMAGEN │ │
|
||||
│ Renders ────────────────────┤ ╰─────────────╯ │
|
||||
│ │ exterior.jpg │
|
||||
│ │ │
|
||||
│ ╰─────╭─────────────╮ │
|
||||
│ │ IMAGEN │ │
|
||||
│ ╰─────────────╯ │
|
||||
│ interior.jpg │
|
||||
│ │
|
||||
╰─────────────────────────────────────────────────────────────────╯
|
||||
```
|
||||
|
||||
### 2.3 Layout Mobile (<768px)
|
||||
|
||||
```
|
||||
╭──────────────────────╮
|
||||
│ Proyecto Demo │
|
||||
├──────────────────────┤
|
||||
│ │
|
||||
│ Arquitectura │
|
||||
│ │ │
|
||||
│ ┌────┴────┐ │
|
||||
│ ▼ ▼ │
|
||||
│ ╭─────╮ ╭─────╮ │
|
||||
│ │ IMG │ │ IMG │ │
|
||||
│ ╰─────╯ ╰─────╯ │
|
||||
│ plano secciones │
|
||||
│ │
|
||||
│ Renders │
|
||||
│ │ │
|
||||
│ ┌────┴────┐ │
|
||||
│ ▼ ▼ │
|
||||
│ ╭─────╮ ╭─────╮ │
|
||||
│ │ IMG │ │ IMG │ │
|
||||
│ ╰─────╯ ╰─────╯ │
|
||||
│ exterior interior │
|
||||
│ │
|
||||
│ ↕ scroll vertical │
|
||||
╰──────────────────────╯
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. Componentes
|
||||
|
||||
### 3.1 Header (opcional)
|
||||
|
||||
```html
|
||||
<header id="header">Proyecto Demo HST</header>
|
||||
```
|
||||
|
||||
```css
|
||||
#header {
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
margin-bottom: 40px;
|
||||
padding-left: 20px;
|
||||
}
|
||||
```
|
||||
|
||||
**Visibilidad:** Controlada por `mostrar_titulo` en JSON.
|
||||
|
||||
### 3.2 Nodo Nivel 1
|
||||
|
||||
```html
|
||||
<div class="level1-node" data-color="orange" data-node-id="n1">
|
||||
<span class="node-text">Arquitectura</span>
|
||||
</div>
|
||||
```
|
||||
|
||||
```css
|
||||
.level1-node {
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
padding: 8px 0;
|
||||
}
|
||||
|
||||
.level1-node .node-text {
|
||||
font-size: 18px;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
border-bottom: 3px solid currentColor;
|
||||
padding-bottom: 4px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
/* Colores por data-color */
|
||||
.level1-node[data-color="orange"] .node-text {
|
||||
color: #FF6B35;
|
||||
border-color: #FF6B35;
|
||||
}
|
||||
```
|
||||
|
||||
**Características:**
|
||||
- Solo texto, sin caja ni fondo
|
||||
- Subrayado del color de la rama
|
||||
- Click para expandir/contraer
|
||||
|
||||
### 3.3 Subnodo
|
||||
|
||||
```html
|
||||
<div class="subnode" data-color="orange" data-parent-id="n1">
|
||||
<div class="subnode-image-container">
|
||||
<img src="http://72.62.2.84:8090/arc_arquitectura.png" alt="plano.pdf">
|
||||
<a href="#" download class="btn-download">↓</a>
|
||||
<a href="#" target="_blank" class="btn-preview">👁</a>
|
||||
</div>
|
||||
<div class="subnode-title">plano.pdf</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
```css
|
||||
.subnode {
|
||||
position: relative;
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
.subnode-image-container {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
aspect-ratio: 16/9;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
border: 3px solid #ddd;
|
||||
}
|
||||
|
||||
.subnode-image-container img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.subnode-title {
|
||||
font-size: 14px;
|
||||
color: #555;
|
||||
margin-top: 8px;
|
||||
text-align: center;
|
||||
}
|
||||
```
|
||||
|
||||
**Características:**
|
||||
- Imagen 16:9 con bordes redondeados
|
||||
- Borde del color de la rama padre
|
||||
- Título debajo, centrado
|
||||
- Botones siempre visibles
|
||||
|
||||
### 3.4 Botones de Acción
|
||||
|
||||
```css
|
||||
.btn-download, .btn-preview {
|
||||
position: absolute;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
border-radius: 6px;
|
||||
background: rgba(0,0,0,0.6);
|
||||
color: white;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.btn-download {
|
||||
bottom: 6px;
|
||||
left: 6px;
|
||||
}
|
||||
|
||||
.btn-preview {
|
||||
top: 6px;
|
||||
right: 6px;
|
||||
}
|
||||
|
||||
.btn-download:hover, .btn-preview:hover {
|
||||
background: rgba(0,0,0,0.8);
|
||||
}
|
||||
```
|
||||
|
||||
| Botón | Icono | Posición | Visible |
|
||||
|-------|-------|----------|---------|
|
||||
| Descarga | ↓ | Esquina inferior izquierda | Siempre |
|
||||
| Preview | 👁 | Esquina superior derecha | Solo PDF/imágenes |
|
||||
|
||||
**Archivos con preview:**
|
||||
- `.pdf`
|
||||
- `.png`
|
||||
- `.jpg` / `.jpeg`
|
||||
- `.gif`
|
||||
- `.webp`
|
||||
|
||||
### 3.5 Líneas SVG
|
||||
|
||||
```html
|
||||
<svg id="lines-svg">
|
||||
<path d="M 150,50 C 200,50 200,120 250,120" stroke="#FF6B35" />
|
||||
</svg>
|
||||
```
|
||||
|
||||
```css
|
||||
#lines-svg {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
pointer-events: none;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
#lines-svg path {
|
||||
fill: none;
|
||||
stroke-width: 3;
|
||||
stroke-linecap: round;
|
||||
}
|
||||
```
|
||||
|
||||
**Curva Bezier:**
|
||||
```
|
||||
M x1,y1 C cx1,cy1 cx2,cy2 x2,y2
|
||||
|
||||
Donde:
|
||||
- (x1,y1) = Punto inicio (borde derecho del nodo nivel 1)
|
||||
- (cx1,cy1) = Primer punto de control
|
||||
- (cx2,cy2) = Segundo punto de control
|
||||
- (x2,y2) = Punto final (borde izquierdo del subnodo)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Paleta de Colores
|
||||
|
||||
### 4.1 Colores de ramas
|
||||
|
||||
| Nombre | Hex | Muestra |
|
||||
|--------|-----|---------|
|
||||
| Orange | #FF6B35 | 🟠 |
|
||||
| Yellow | #E5A000 | 🟡 |
|
||||
| Green | #06D6A0 | 🟢 |
|
||||
| Cyan | #0891B2 | 🔵 |
|
||||
| Blue | #3B82F6 | 🔷 |
|
||||
| Purple | #8B5CF6 | 🟣 |
|
||||
| Pink | #EC4899 | 🩷 |
|
||||
|
||||
### 4.2 Colores base
|
||||
|
||||
| Uso | Hex |
|
||||
|-----|-----|
|
||||
| Fondo | #f5f5f7 |
|
||||
| Texto principal | #333333 |
|
||||
| Texto secundario | #555555 |
|
||||
| Borde default | #dddddd |
|
||||
|
||||
### 4.3 Asignación automática
|
||||
|
||||
```javascript
|
||||
const COLORS = ['orange', 'yellow', 'green', 'cyan', 'blue', 'purple', 'pink'];
|
||||
|
||||
// Cada nodo nivel 1 recibe un color según su índice
|
||||
const color = COLORS[index % COLORS.length];
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Tipografía
|
||||
|
||||
### 5.1 Familia
|
||||
|
||||
```css
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||
```
|
||||
|
||||
### 5.2 Tamaños
|
||||
|
||||
| Elemento | Tamaño | Peso |
|
||||
|----------|--------|------|
|
||||
| Header | 24px | 600 |
|
||||
| Nodo nivel 1 | 18px | 500 |
|
||||
| Título subnodo | 14px | 400 |
|
||||
|
||||
---
|
||||
|
||||
## 6. Interactividad
|
||||
|
||||
### 6.1 Click en nodo nivel 1
|
||||
|
||||
```javascript
|
||||
node.addEventListener('click', () => {
|
||||
const nodeId = node.dataset.nodeId;
|
||||
const subnodes = document.querySelectorAll(`[data-parent-id="${nodeId}"]`);
|
||||
|
||||
subnodes.forEach(sub => {
|
||||
sub.classList.toggle('hidden');
|
||||
});
|
||||
|
||||
// Redibujar líneas
|
||||
drawLines();
|
||||
});
|
||||
```
|
||||
|
||||
**Comportamiento:**
|
||||
- Toggle: muestra/oculta subnodos
|
||||
- Recalcula posiciones de líneas SVG
|
||||
- Animación suave (opcional)
|
||||
|
||||
### 6.2 Cálculo de líneas
|
||||
|
||||
```javascript
|
||||
function drawLines() {
|
||||
const svg = document.getElementById('lines-svg');
|
||||
svg.innerHTML = '';
|
||||
|
||||
const containerRect = document.getElementById('mindmap-container').getBoundingClientRect();
|
||||
|
||||
document.querySelectorAll('.level1-node').forEach(node => {
|
||||
const nodeId = node.dataset.nodeId;
|
||||
const color = node.dataset.color;
|
||||
const nodeRect = node.getBoundingClientRect();
|
||||
|
||||
// Punto inicio: borde derecho del subrayado
|
||||
const startX = nodeRect.right - containerRect.left;
|
||||
const startY = nodeRect.bottom - containerRect.top - 2;
|
||||
|
||||
// Encontrar subnodos visibles
|
||||
const subnodes = document.querySelectorAll(
|
||||
`.subnode[data-parent-id="${nodeId}"]:not(.hidden)`
|
||||
);
|
||||
|
||||
subnodes.forEach(subnode => {
|
||||
const subRect = subnode.querySelector('.subnode-image-container')
|
||||
.getBoundingClientRect();
|
||||
|
||||
// Punto final: borde izquierdo del subnodo
|
||||
const endX = subRect.left - containerRect.left;
|
||||
const endY = subRect.top + subRect.height/2 - containerRect.top;
|
||||
|
||||
// Puntos de control para curva suave
|
||||
const midX = startX + (endX - startX) / 2;
|
||||
|
||||
const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
|
||||
path.setAttribute('d',
|
||||
`M ${startX},${startY} C ${midX},${startY} ${midX},${endY} ${endX},${endY}`
|
||||
);
|
||||
path.setAttribute('stroke', getColorValue(color));
|
||||
svg.appendChild(path);
|
||||
});
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
### 6.3 Responsive
|
||||
|
||||
```javascript
|
||||
// Redibujar en resize
|
||||
window.addEventListener('resize', drawLines);
|
||||
|
||||
// Detectar mobile
|
||||
const isMobile = window.innerWidth < 768;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. Estructura JSON
|
||||
|
||||
### 7.1 Formato completo
|
||||
|
||||
```json
|
||||
{
|
||||
"slug": "proyecto-demo",
|
||||
"titulo": "Proyecto Demo HST",
|
||||
"mostrar_titulo": true,
|
||||
"tema": "light",
|
||||
"nodos": [
|
||||
{
|
||||
"id": "n1",
|
||||
"titulo": "Arquitectura",
|
||||
"color": "#FF6B35",
|
||||
"subnodos": [
|
||||
{
|
||||
"titulo": "plano_general.pdf",
|
||||
"icono_hst": "arc_arquitectura",
|
||||
"url_archivo": "https://git.tzzr.pro/.../plano.pdf",
|
||||
"url_corta": "https://tzzr.pro/abc123"
|
||||
},
|
||||
{
|
||||
"titulo": "secciones.pdf",
|
||||
"icono_hst": "pln_plano",
|
||||
"url_archivo": "https://git.tzzr.pro/.../secciones.pdf",
|
||||
"url_corta": "https://tzzr.pro/def456"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "n2",
|
||||
"titulo": "Renders",
|
||||
"color": "#E5A000",
|
||||
"subnodos": [
|
||||
{
|
||||
"titulo": "exterior.jpg",
|
||||
"icono_hst": "ren_render",
|
||||
"url_archivo": "https://git.tzzr.pro/.../exterior.jpg"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 7.2 Campos
|
||||
|
||||
| Campo | Tipo | Requerido | Descripción |
|
||||
|-------|------|-----------|-------------|
|
||||
| slug | string | ✅ | Identificador URL |
|
||||
| titulo | string | ✅ | Nombre del proyecto |
|
||||
| mostrar_titulo | boolean | ❌ | Si mostrar header (default: true) |
|
||||
| tema | string | ❌ | "light" o "dark" |
|
||||
| nodos | array | ✅ | Lista de nodos nivel 1 |
|
||||
|
||||
**Nodo:**
|
||||
| Campo | Tipo | Requerido | Descripción |
|
||||
|-------|------|-----------|-------------|
|
||||
| id | string | ✅ | Identificador único |
|
||||
| titulo | string | ✅ | Nombre de la carpeta |
|
||||
| color | string | ❌ | Color hex (si no, se asigna auto) |
|
||||
| subnodos | array | ✅ | Lista de subnodos |
|
||||
|
||||
**Subnodo:**
|
||||
| Campo | Tipo | Requerido | Descripción |
|
||||
|-------|------|-----------|-------------|
|
||||
| titulo | string | ✅ | Nombre del archivo |
|
||||
| icono_hst | string | ✅ | Referencia imagen HST |
|
||||
| url_archivo | string | ✅ | URL de descarga |
|
||||
| url_corta | string | ❌ | URL acortada |
|
||||
|
||||
---
|
||||
|
||||
## 8. Código Base
|
||||
|
||||
### 8.1 index.html
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="es">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>MML - Mindmap Linking</title>
|
||||
<link rel="stylesheet" href="css/main.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="app">
|
||||
<header id="header"></header>
|
||||
<div id="mindmap-container">
|
||||
<svg id="lines-svg"></svg>
|
||||
<div id="nodes-container"></div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="js/app.js" type="module"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
### 8.2 Variables CSS
|
||||
|
||||
```css
|
||||
:root {
|
||||
--bg-primary: #f5f5f7;
|
||||
--text-primary: #333;
|
||||
--text-secondary: #555;
|
||||
--font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||
--line-width: 3px;
|
||||
--image-radius: 8px;
|
||||
--transition-speed: 0.3s;
|
||||
|
||||
/* Colores de ramas */
|
||||
--color-orange: #FF6B35;
|
||||
--color-yellow: #E5A000;
|
||||
--color-green: #06D6A0;
|
||||
--color-cyan: #0891B2;
|
||||
--color-blue: #3B82F6;
|
||||
--color-purple: #8B5CF6;
|
||||
--color-pink: #EC4899;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. Imágenes HST
|
||||
|
||||
### 9.1 URL Base
|
||||
|
||||
```javascript
|
||||
const HST_BASE = 'http://72.62.2.84:8090/';
|
||||
```
|
||||
|
||||
### 9.2 Construcción de URL
|
||||
|
||||
```javascript
|
||||
function getImageUrl(icono_hst) {
|
||||
if (!icono_hst) return null;
|
||||
return `${HST_BASE}${icono_hst}.png`;
|
||||
}
|
||||
```
|
||||
|
||||
### 9.3 Formato de nombres
|
||||
|
||||
```
|
||||
{codigo}_{nombre}.png
|
||||
|
||||
Ejemplos:
|
||||
- arc_arquitectura.png
|
||||
- pln_plano.png
|
||||
- doc_documento.png
|
||||
- fto_foto.png
|
||||
```
|
||||
|
||||
### 9.4 Fallback si imagen no existe
|
||||
|
||||
```javascript
|
||||
img.onerror = function() {
|
||||
this.src = '/assets/placeholder.png';
|
||||
// O ocultar el contenedor de imagen
|
||||
this.parentElement.style.display = 'none';
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 10. Tests de Interfaz
|
||||
|
||||
### 10.1 Checklist visual
|
||||
|
||||
| Test | Criterio |
|
||||
|------|----------|
|
||||
| Carga | Página muestra contenido en <2s |
|
||||
| Header | Título visible si `mostrar_titulo: true` |
|
||||
| Nodos | Todos los nodos nivel 1 visibles |
|
||||
| Imágenes | Ninguna imagen rota |
|
||||
| Líneas | Curvas conectan correctamente |
|
||||
| Click | Expandir/contraer funciona |
|
||||
| Responsive | Layout adapta a mobile |
|
||||
|
||||
### 10.2 Tests Puppeteer
|
||||
|
||||
```javascript
|
||||
// Test de imágenes
|
||||
const images = await page.$$eval('img', imgs =>
|
||||
imgs.map(img => ({
|
||||
src: img.src,
|
||||
loaded: img.complete && img.naturalWidth > 0
|
||||
}))
|
||||
);
|
||||
const broken = images.filter(i => !i.loaded);
|
||||
if (broken.length > 0) {
|
||||
errors.push(`Imágenes rotas: ${broken.map(i => i.src).join(', ')}`);
|
||||
}
|
||||
|
||||
// Test de líneas
|
||||
const paths = await page.$$('svg path');
|
||||
if (paths.length === 0) {
|
||||
errors.push('No hay curvas SVG');
|
||||
}
|
||||
|
||||
// Test de interactividad
|
||||
await page.click('.level1-node');
|
||||
await page.waitForTimeout(300);
|
||||
const visibleSubnodes = await page.$$('.subnode:not(.hidden)');
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 11. Accesibilidad
|
||||
|
||||
### 11.1 Básico
|
||||
|
||||
```html
|
||||
<!-- Alt text en imágenes -->
|
||||
<img src="..." alt="plano_general.pdf">
|
||||
|
||||
<!-- Roles semánticos -->
|
||||
<nav role="navigation">
|
||||
<main role="main">
|
||||
|
||||
<!-- Focus visible -->
|
||||
.level1-node:focus {
|
||||
outline: 2px solid var(--color-blue);
|
||||
}
|
||||
```
|
||||
|
||||
### 11.2 Navegación por teclado
|
||||
|
||||
```javascript
|
||||
// Enter/Space para expandir
|
||||
node.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Enter' || e.key === ' ') {
|
||||
e.preventDefault();
|
||||
node.click();
|
||||
}
|
||||
});
|
||||
|
||||
// Tab order
|
||||
node.setAttribute('tabindex', '0');
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**Versión:** 1.0
|
||||
**Fecha:** 2025-12-11
|
||||
Reference in New Issue
Block a user