Files
system-docs/v4-archive/mind-link/docs/interfaz_mindlink v0.md
2025-12-24 17:28:34 +00:00

18 KiB

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)

<header id="header">Proyecto Demo HST</header>
#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

<div class="level1-node" data-color="orange" data-node-id="n1">
    <span class="node-text">Arquitectura</span>
</div>
.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

<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>
.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

.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

<svg id="lines-svg">
    <path d="M 150,50 C 200,50 200,120 250,120" stroke="#FF6B35" />
</svg>
#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

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

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

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

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

// Redibujar en resize
window.addEventListener('resize', drawLines);

// Detectar mobile
const isMobile = window.innerWidth < 768;

7. Estructura JSON

7.1 Formato completo

{
    "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

<!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

: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

const HST_BASE = 'http://72.62.2.84:8090/';

9.2 Construcción de URL

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

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

// 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

<!-- 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

// 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