The full scope across this batch: POST /sessions key→token exchange, SessionStore dual-index with revoke_by_id, CLI Bearer injection (ONTOREF_TOKEN), ontoref setup --gen-keys, install scripts, daemon config form roundtrip, ADR-004/005, on+re self-description update (fully-self-described), and landing page refresh.
383 lines
9.8 KiB
Markdown
383 lines
9.8 KiB
Markdown
# Componentes Personalizados para Slidev
|
||
|
||
## ProjectionToggle - Toggle para Modo Proyector
|
||
|
||
### 📍 Ubicación
|
||
`slides/components/ProjectionToggle.vue`
|
||
|
||
### 🎯 Descripción
|
||
Botón flotante para cambiar entre modo Monitor (oscuro) y modo Proyector (más claro) durante la presentación.
|
||
|
||
### ✨ Características
|
||
|
||
1. **Toggle visual**: Botón flotante con iconos 🖥️ / 📽️
|
||
2. **Persistencia**: Guarda la preferencia en localStorage
|
||
3. **Feedback visual**: Cambia de color según el modo activo
|
||
4. **No invasivo**: Se oculta automáticamente en modo presentador y al imprimir
|
||
5. **CSS Variables**: Usa variables CSS para cambios instantáneos
|
||
|
||
### 📝 Uso
|
||
|
||
Agrégalo a tu primera slide o a todas las slides principales:
|
||
|
||
```vue
|
||
<ProjectionToggle />
|
||
```
|
||
|
||
O en markdown:
|
||
|
||
```markdown
|
||
---
|
||
# Primera Slide
|
||
---
|
||
|
||
<ProjectionToggle />
|
||
|
||
# Tu contenido aquí...
|
||
```
|
||
|
||
### 🎨 Estados Visuales
|
||
|
||
**Monitor Mode** (por defecto)
|
||
- Icono: 🖥️ Monitor
|
||
- Color: Naranja (gradiente Rust)
|
||
- Fondo: #1c1c1c (muy oscuro)
|
||
|
||
**Projection Mode**
|
||
- Icono: 📽️ Projection
|
||
- Color: Verde (#48bb78)
|
||
- Fondo: #2d3748 (dark pero más visible)
|
||
|
||
### 💡 ¿Por qué dos modos?
|
||
|
||
Los proyectores tienen menor contraste que los monitores. El modo Projection:
|
||
- Fondo más claro (#2d3748 vs #1c1c1c)
|
||
- Mayor contraste de texto
|
||
- Bordes más gruesos en componentes
|
||
- Mejor visibilidad en salas con luz ambiente
|
||
|
||
### 🔧 Cómo Funciona
|
||
|
||
1. Click en el botón
|
||
2. Agrega/quita la clase `projection-mode` al `<html>`
|
||
3. Las CSS variables cambian automáticamente:
|
||
```css
|
||
:root {
|
||
--bg-primary: #1c1c1c; /* Monitor */
|
||
}
|
||
|
||
.projection-mode {
|
||
--bg-primary: #2d3748; /* Projection */
|
||
}
|
||
```
|
||
4. Guarda la preferencia en `localStorage`
|
||
|
||
### 📖 Documentación Completa
|
||
|
||
Ver: `slides/PROJECTION_MODE.md` para guía detallada sobre:
|
||
- Todos los métodos de activación
|
||
- Comparación visual
|
||
- Personalización de colores
|
||
- Troubleshooting
|
||
|
||
### 🎯 Recomendación de Uso
|
||
|
||
Antes de tu presentación:
|
||
1. Agrega `<ProjectionToggle />` a la primera slide
|
||
2. Durante el soundcheck con el proyector, prueba ambos modos
|
||
3. El modo elegido se guardará automáticamente
|
||
|
||
---
|
||
|
||
## YearsCounter - Contador Animado de Años
|
||
|
||
### 📍 Ubicación
|
||
`slides/components/YearsCounter.vue`
|
||
|
||
### 🎯 Descripción
|
||
Componente SVG animado que muestra un contador de años pasando como hojas de calendario, desde 1 hasta el año actual.
|
||
|
||
### ✨ Características
|
||
|
||
1. **Animación de contador**: Los números pasan de 1 hasta 40 (en 2025)
|
||
2. **Efecto de hoja**: Animación de scale que simula una página de calendario volteándose
|
||
3. **Badge animado**: Fondo con gradiente verde y efecto de brillo pulsante
|
||
4. **SVG puro**: Más ligero y eficiente que componentes complejos
|
||
5. **Totalmente personalizable**: Props para ajustar año inicial y velocidad
|
||
|
||
### 📝 Uso
|
||
|
||
En cualquier slide de Slidev:
|
||
|
||
```vue
|
||
<YearsCounter :startYear="1985" :speed="80" />
|
||
```
|
||
|
||
### 🎛️ Props
|
||
|
||
| Prop | Tipo | Default | Descripción |
|
||
|------|------|---------|-------------|
|
||
| `startYear` | Number | 1985 | Año desde el que se cuenta |
|
||
| `speed` | Number | 80 | Velocidad en milisegundos por año (menor = más rápido) |
|
||
| `delay` | Number | 3000 | Delay en ms antes de iniciar la animación (después de que la slide sea visible) |
|
||
|
||
### 🔧 Ejemplos de Configuración
|
||
|
||
#### Más Rápido (50ms por año)
|
||
```vue
|
||
<YearsCounter :startYear="1985" :speed="50" />
|
||
```
|
||
|
||
#### Más Lento (150ms por año)
|
||
```vue
|
||
<YearsCounter :startYear="1985" :speed="150" />
|
||
```
|
||
|
||
#### Desde otro año (ej: experiencia con Rust)
|
||
```vue
|
||
<YearsCounter :startYear="2019" :speed="100" />
|
||
```
|
||
|
||
#### Con delay personalizado (iniciar 5 segundos después)
|
||
```vue
|
||
<YearsCounter :startYear="1985" :speed="80" :delay="5000" />
|
||
```
|
||
|
||
#### Sin delay (iniciar inmediatamente cuando sea visible)
|
||
```vue
|
||
<YearsCounter :startYear="1985" :speed="80" :delay="0" />
|
||
```
|
||
|
||
### 🎨 Personalización del Estilo
|
||
|
||
El componente usa estas características visuales:
|
||
|
||
1. **Badge Verde**:
|
||
- Gradiente: `rgba(72,187,120,0.2)` → `rgba(72,187,120,0.4)`
|
||
- Borde: `#48bb78` (verde Rust accent)
|
||
- Bordes redondeados: `15px`
|
||
|
||
2. **Número**:
|
||
- Color: `#4ade80` (verde brillante)
|
||
- Tamaño: `48px`
|
||
- Fondo oscuro con opacidad
|
||
|
||
3. **Animaciones**:
|
||
- **Pulso del badge**: 3 segundos, brillo entre 0.3 y 0.6 opacidad
|
||
- **Flip del número**: 0.3 segundos, scale de `1,1` → `1,0.3` → `1,1`
|
||
|
||
### 🖼️ Estructura del SVG
|
||
|
||
```svg
|
||
<svg width="300" height="120">
|
||
<!-- Badge con gradiente y animación -->
|
||
<rect with gradient + glow filter + pulse animation />
|
||
|
||
<!-- Emoji -->
|
||
<text>💻</text>
|
||
|
||
<!-- Contenedor del número -->
|
||
<g>
|
||
<rect (fondo oscuro) />
|
||
<text (número con animación de flip) />
|
||
</g>
|
||
|
||
<!-- Texto descriptivo -->
|
||
<text>años como desarrollador</text>
|
||
</svg>
|
||
```
|
||
|
||
### 🔄 Cómo Funciona la Animación
|
||
|
||
#### Detección de Visibilidad (Nuevo)
|
||
|
||
1. **IntersectionObserver**: Monitorea cuando la slide es visible (30% threshold)
|
||
2. **Activación única**: Solo se inicia la primera vez que aparece
|
||
3. **Delay configurable**: Espera 3 segundos (por defecto) antes de iniciar
|
||
4. **Compatible**: Funciona tanto en Slidev como en navegación normal
|
||
|
||
#### Secuencia de la Animación
|
||
|
||
1. **Slide visible**: IntersectionObserver detecta que el componente es visible
|
||
2. **Delay**: Espera el tiempo configurado (por defecto 3000ms)
|
||
3. **Inicio**: Comienza el contador desde 1
|
||
4. **Incremento**: Cada `speed` milisegundos, incrementa el contador
|
||
5. **Efecto flip**: Cada cambio de número aplica una animación de scale en el eje Y
|
||
6. **Fin**: Cuando alcanza el año objetivo (2025-1985=40), detiene el intervalo
|
||
|
||
**Ejemplo de timing completo**:
|
||
- Llegas a la slide 2 → espera 3 segundos → cuenta de 1 a 40 en 3.2 segundos = **6.2 segundos total**
|
||
|
||
### 💻 Código JavaScript
|
||
|
||
```javascript
|
||
const counterElement = ref(null) // Referencia al elemento DOM
|
||
const displayYear = ref(1)
|
||
const hasStarted = ref(false) // Prevenir múltiples inicios
|
||
const targetYear = new Date().getFullYear() - props.startYear // 2025 - 1985 = 40
|
||
|
||
const startAnimation = () => {
|
||
if (hasStarted.value) return // Solo iniciar una vez
|
||
|
||
hasStarted.value = true
|
||
|
||
// Esperar el delay configurado antes de iniciar
|
||
setTimeout(() => {
|
||
let year = 1
|
||
const interval = setInterval(() => {
|
||
if (year <= targetYear) {
|
||
displayYear.value = year
|
||
year++
|
||
} else {
|
||
clearInterval(interval)
|
||
}
|
||
}, props.speed)
|
||
}, props.delay)
|
||
}
|
||
|
||
onMounted(async () => {
|
||
await nextTick()
|
||
|
||
// Observar cuando el componente es visible
|
||
const observer = new IntersectionObserver(
|
||
(entries) => {
|
||
entries.forEach((entry) => {
|
||
if (entry.isIntersecting && !hasStarted.value) {
|
||
startAnimation()
|
||
observer.disconnect() // Dejar de observar después de iniciar
|
||
}
|
||
})
|
||
},
|
||
{ threshold: 0.3 } // 30% visible
|
||
)
|
||
|
||
observer.observe(counterElement.value)
|
||
})
|
||
```
|
||
|
||
### 🎬 Resultado
|
||
|
||
Cuando navegas a la slide:
|
||
1. **Espera 3 segundos** (delay configurable)
|
||
2. **Inicia en 1**
|
||
3. **Va incrementando** cada 80ms
|
||
4. **Animación de flip** en cada cambio
|
||
5. **Termina en 40** (años desde 1985 hasta 2025)
|
||
|
||
**Duración total**:
|
||
- Delay: 3 segundos
|
||
- Animación: 40 años × 80ms = 3.2 segundos
|
||
- **Total: 6.2 segundos**
|
||
|
||
### ✨ Comportamiento Inteligente
|
||
|
||
- ✅ **Solo se inicia cuando la slide es visible** (no al cargar la presentación)
|
||
- ✅ **Delay de 3 segundos** para dar tiempo al usuario a leer el título
|
||
- ✅ **Se inicia solo una vez** (aunque vuelvas a la slide, no se reinicia)
|
||
- ✅ **Compatible con navegación** hacia adelante y atrás en Slidev
|
||
|
||
### 📱 Responsive
|
||
|
||
El SVG se adapta automáticamente al contenedor y mantiene su aspecto.
|
||
|
||
### 🔄 Actualización Automática
|
||
|
||
El cálculo `new Date().getFullYear() - props.startYear` se hace al montar el componente, por lo que:
|
||
- En **2025**: muestra de 1 a 40
|
||
- En **2026**: mostrará de 1 a 41
|
||
- Y así sucesivamente
|
||
|
||
### 🎯 Implementación en el Slide
|
||
|
||
**Slide 2** - Introducción Personal:
|
||
|
||
```markdown
|
||
---
|
||
layout: intro
|
||
---
|
||
|
||
# Jesús Pérez Lorenzo
|
||
|
||
<div class="flex items-center gap-11 mt-8">
|
||
<div class="leading-8 opacity-80">
|
||
|
||
<YearsCounter :startYear="1985" :speed="80" />
|
||
|
||
<span class="orange">Rust</span> Developer & Cloud Architect<br>
|
||
...
|
||
</div>
|
||
</div>
|
||
```
|
||
|
||
### 🛠️ Modificar la Animación
|
||
|
||
Para cambiar el tipo de animación, edita la sección `<animateTransform>` en el componente:
|
||
|
||
```xml
|
||
<!-- Flip horizontal en lugar de vertical -->
|
||
<animateTransform
|
||
attributeName="transform"
|
||
type="scale"
|
||
values="1,1; 0.3,1; 1,1"
|
||
dur="0.3s"/>
|
||
|
||
<!-- Rotación -->
|
||
<animateTransform
|
||
attributeName="transform"
|
||
type="rotate"
|
||
values="0 120 75; 360 120 75"
|
||
dur="0.5s"/>
|
||
```
|
||
|
||
### 🎨 Cambiar Colores
|
||
|
||
Para cambiar el esquema de colores, modifica:
|
||
|
||
```xml
|
||
<!-- Verde → Naranja Rust -->
|
||
<stop offset="0%" style="stop-color:rgba(247,76,0,0.2)" />
|
||
<stop offset="100%" style="stop-color:rgba(247,76,0,0.4)" />
|
||
|
||
<!-- Color del número -->
|
||
<text fill="#ff6b4a">
|
||
|
||
<!-- Color del borde -->
|
||
<rect stroke="#f74c00" />
|
||
```
|
||
|
||
## 🚀 Crear Otros Contadores
|
||
|
||
Puedes reutilizar el componente para otras métricas:
|
||
|
||
```vue
|
||
<!-- Años con Rust -->
|
||
<YearsCounter :startYear="2019" :speed="100" />
|
||
|
||
<!-- Años en Cloud -->
|
||
<YearsCounter :startYear="2013" :speed="100" />
|
||
```
|
||
|
||
## 📦 Archivos del Componente
|
||
|
||
```
|
||
slides/
|
||
├── components/
|
||
│ ├── YearsCounter.vue ← El componente
|
||
│ └── README.md ← Esta documentación
|
||
└── slides.md ← Uso del componente
|
||
```
|
||
|
||
## 🔍 Troubleshooting
|
||
|
||
### El componente no se muestra
|
||
- Asegúrate de que el archivo esté en `slides/components/`
|
||
- Slidev detecta automáticamente componentes en esta carpeta
|
||
|
||
### La animación es muy rápida/lenta
|
||
- Ajusta el prop `:speed` (en milisegundos)
|
||
- Valores recomendados: 50-150ms
|
||
|
||
### El número no llega al año correcto
|
||
- Verifica que `startYear` sea correcto
|
||
- El cálculo es: `new Date().getFullYear() - startYear`
|