forked from lab/libre-marker
369 lines
13 KiB
Markdown
369 lines
13 KiB
Markdown
# Libre-marker Project
|
|
|
|
|
|
con Nginx + JavaScript. Podemos configurar Nginx para que, cuando alguien pida un `.md`, Nginx le sirva una pequeña plantilla HTML "invisible" que contenga la librería
|
|
[Marked](https://github.com/markedjs/marked/) y renderice el texto al instante.
|
|
|
|
|
|
## FASE1 - Libre-marke viewer
|
|
|
|
Aquí tienes cómo montar tu propio "Markdown Viewer" estático en 2 pasos:
|
|
|
|
### Paso 1: Crear el "Visor Mágico" (visor.html)
|
|
|
|
Crea un archivo llamado `visor.html` en la raíz de tu sitio web (junto a tus archivos .md). Copia este código exacto.
|
|
|
|
Este código usa la librería **Marked** (la que mostraste en la imagen) para convertir el texto a HTML en el navegador del visitante.
|
|
|
|
```HTML
|
|
<!DOCTYPE html>
|
|
<html lang="es">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<title>Visor Markdown</title>
|
|
<style>
|
|
body { font-family: sans-serif; max-width: 800px; margin: 0 auto; padding: 20px; line-height: 1.6; }
|
|
img { max-width: 100%; }
|
|
pre { background: #f4f4f4; padding: 10px; border-radius: 5px; overflow-x: auto; }
|
|
/* Puedes añadir aquí CSS para que se vea como GitHub o tu estilo preferido */
|
|
</style>
|
|
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
|
|
</head>
|
|
<body>
|
|
<div id="content">Cargando documento...</div>
|
|
|
|
<script>
|
|
// 1. Detectar qué archivo .md se pidió (viene en la URL)
|
|
const urlParams = new URLSearchParams(window.location.search);
|
|
const file = urlParams.get('file');
|
|
|
|
if (file) {
|
|
// 2. Descargar el archivo .md
|
|
fetch(file)
|
|
.then(response => {
|
|
if (!response.ok) throw new Error("No se encontró el archivo.");
|
|
return response.text();
|
|
})
|
|
.then(text => {
|
|
// 3. Convertir Markdown a HTML y mostrarlo
|
|
document.getElementById('content').innerHTML = marked.parse(text);
|
|
})
|
|
.catch(err => {
|
|
document.getElementById('content').innerHTML = "Error: " + err.message;
|
|
});
|
|
} else {
|
|
document.getElementById('content').innerHTML = "No se ha especificado ningún archivo.";
|
|
}
|
|
</script>
|
|
</body>
|
|
</html>
|
|
```
|
|
|
|
|
|
### Paso 2: El Truco de Nginx (La Redirección)
|
|
|
|
Ahora configuramos Nginx para que intercepte la petición. Cuando el usuario escriba `misitio.com/manual.md`, Nginx **reescribirá internamente** la petición para cargar el visor, pasándole el nombre del archivo.
|
|
|
|
Edita tu configuración del sitio (`/etc/nginx/sites-enabled/tustio`):
|
|
|
|
```Nginx
|
|
server {
|
|
# ... tu configuración actual ...
|
|
root /var/www/tusitio;
|
|
index index.html;
|
|
|
|
# REGLA MÁGICA PARA MARKDOWN
|
|
location ~ \.md$ {
|
|
# Si el archivo existe, redirige al visor
|
|
try_files $uri @markdown_viewer;
|
|
}
|
|
|
|
location @markdown_viewer {
|
|
# Reescribe la URL para usar el visor.html
|
|
# Ejemplo: manual.md -> /visor.html?file=manual.md
|
|
rewrite ^/(.*\.md)$ /visor.html?file=$1 last;
|
|
}
|
|
|
|
# ... resto de la config ...
|
|
}
|
|
```
|
|
|
|
|
|
### Paso 3: Reiniciar y Probar
|
|
|
|
1. Reinicia Nginx:
|
|
```bash
|
|
service nginx restart
|
|
```
|
|
|
|
2. Borra caché del navegador (Ctrl + Shift + R).
|
|
|
|
Entra a librebits.info/tu-archivo.md.
|
|
|
|
3. ¿Qué ocurrirá?
|
|
|
|
La URL en el navegador seguirá diciendo tu-archivo.md (elegante).
|
|
|
|
Pero lo que verás en pantalla será el HTML generado, con títulos, negritas y enlaces, tal cual querías.
|
|
|
|
¿Te animas a probar este enfoque? Es muy ligero y no requiere instalar PHP ni bases de datos.
|
|
|
|
|
|
## FASE 2 - Live Editor + Preview
|
|
|
|
Editor Markdown en tiempo real con split view, inspirado en la demo oficial de Marked.js (ver `Screenshots/markedJS-demo.png`).
|
|
|
|
### Arquitectura
|
|
|
|
**100% cliente-side**. Nginx solo sirve el HTML estático, toda la lógica ocurre en el navegador usando JavaScript vanilla + Marked.js.
|
|
|
|
### Componentes UI
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────────┐
|
|
│ Barra superior: [Dropdown Views] [Clear] [Version] [Theme] │
|
|
├──────────────────────────┬──────────────────────────────────┤
|
|
│ │ │
|
|
│ PANEL IZQUIERDO │ PANEL DERECHO │
|
|
│ (Editor) │ (Preview) │
|
|
│ │ │
|
|
│ <textarea> │ <div id="preview"> │
|
|
│ Markdown crudo │ HTML renderizado │
|
|
│ con sintaxis │ en tiempo real │
|
|
│ │ │
|
|
└──────────────────────────┴──────────────────────────────────┘
|
|
```
|
|
|
|
### Funcionamiento
|
|
|
|
1. **Textarea con evento `oninput`**:
|
|
```javascript
|
|
textarea.addEventListener('input', (e) => {
|
|
const markdown = e.target.value;
|
|
renderPreview(markdown);
|
|
});
|
|
```
|
|
|
|
2. **Renderizado instantáneo**:
|
|
```javascript
|
|
function renderPreview(text) {
|
|
const view = currentView; // Preview, HTML Source, Lexer Data
|
|
|
|
switch(view) {
|
|
case 'preview':
|
|
preview.innerHTML = marked.parse(text);
|
|
break;
|
|
case 'html':
|
|
preview.textContent = marked.parse(text);
|
|
break;
|
|
case 'lexer':
|
|
preview.textContent = JSON.stringify(marked.lexer(text), null, 2);
|
|
break;
|
|
}
|
|
}
|
|
```
|
|
|
|
3. **Layout CSS Grid/Flexbox**:
|
|
- Split 50/50 horizontal
|
|
- Responsive: stack vertical en móviles
|
|
- Dark mode toggle
|
|
|
|
### Características a Implementar
|
|
|
|
- ✅ Split view editor/preview
|
|
- ✅ Actualización en tiempo real (oninput)
|
|
- ✅ Dropdown con vistas: Preview, HTML Source, Lexer Data, Quick Reference
|
|
- ✅ Botón "Clear" para limpiar editor
|
|
- ✅ Dark mode
|
|
- ⏳ Cargar archivo .md existente para editar
|
|
- ⏳ Botón "Download" para guardar cambios localmente
|
|
- ⏳ LocalStorage para persistencia temporal
|
|
|
|
### Sin Backend Necesario
|
|
|
|
Todo funciona en el cliente con:
|
|
- HTML + CSS + JavaScript vanilla (ES6+)
|
|
- Marked.js (CDN: `https://cdn.jsdelivr.net/npm/marked/marked.min.js`)
|
|
- Nginx sirviendo archivos estáticos
|
|
|
|
---
|
|
|
|
## FASE 3 - Control de Versiones con Git
|
|
|
|
Añadir capacidad de **commit directo desde el navegador** para versionar cambios en archivos Markdown.
|
|
|
|
### Visión General
|
|
|
|
Botón "💾 Commit" en la interfaz que guarda el estado actual del archivo con mensaje de commit, creando un historial versionado.
|
|
|
|
### Desafío: Seguridad del Navegador
|
|
|
|
JavaScript en el navegador **NO puede ejecutar comandos git** directamente por restricciones de seguridad. Necesitamos un puente mínimo servidor-cliente.
|
|
|
|
### Opciones de Implementación (Out-of-the-box thinking)
|
|
|
|
#### Opción A: GitHub API (Sin Backend Custom)
|
|
|
|
**Pros**: Sin código servidor, usa API REST de GitHub
|
|
**Contras**: Requiere repo público/privado en GitHub, token de autenticación
|
|
|
|
```javascript
|
|
// Commit directo vía GitHub API
|
|
async function commitToGitHub(filename, content, message) {
|
|
const response = await fetch(`https://api.github.com/repos/user/repo/contents/${filename}`, {
|
|
method: 'PUT',
|
|
headers: {
|
|
'Authorization': `token ${GITHUB_TOKEN}`,
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify({
|
|
message: message,
|
|
content: btoa(content), // Base64
|
|
sha: currentFileSHA // Obtenido previamente
|
|
})
|
|
});
|
|
return response.json();
|
|
}
|
|
```
|
|
|
|
**Flujo**:
|
|
1. Usuario edita markdown
|
|
2. Click en "Commit" → prompt para mensaje
|
|
3. JavaScript hace PUT a GitHub API
|
|
4. Archivo actualizado en el repo remoto
|
|
5. Historial visible en GitHub
|
|
|
|
#### Opción B: Endpoint Mínimo PHP/Python + Git Local
|
|
|
|
**Pros**: Control total, commits locales, no depende de GitHub
|
|
**Contras**: Requiere script servidor (rompe filosofía "sin backend")
|
|
|
|
```php
|
|
// save.php (ejecutado vía Nginx FastCGI)
|
|
<?php
|
|
$file = $_POST['file'];
|
|
$content = $_POST['content'];
|
|
$message = $_POST['message'];
|
|
|
|
// Guardar archivo
|
|
file_put_contents("/var/www/docs/{$file}", $content);
|
|
|
|
// Git commit
|
|
exec("cd /var/www/docs && git add {$file}");
|
|
exec("git commit -m " . escapeshellarg($message));
|
|
|
|
echo json_encode(['status' => 'ok']);
|
|
?>
|
|
```
|
|
|
|
**Configuración Nginx**:
|
|
```nginx
|
|
location /api/save {
|
|
fastcgi_pass unix:/var/run/php/php-fpm.sock;
|
|
include fastcgi_params;
|
|
fastcgi_param SCRIPT_FILENAME /var/www/scripts/save.php;
|
|
}
|
|
```
|
|
|
|
#### Opción C: LocalStorage + Historial JSON (Sin Git Real)
|
|
|
|
**Pros**: 100% cliente, sin servidor, funciona offline
|
|
**Contras**: No es git real, historial solo en navegador local
|
|
|
|
```javascript
|
|
// Pseudo-versionado con LocalStorage
|
|
function saveVersion(filename, content, message) {
|
|
const history = JSON.parse(localStorage.getItem('mdHistory') || '[]');
|
|
|
|
history.push({
|
|
file: filename,
|
|
content: content,
|
|
message: message,
|
|
timestamp: new Date().toISOString(),
|
|
sha: simpleHash(content) // Hash simple como "commit ID"
|
|
});
|
|
|
|
localStorage.setItem('mdHistory', JSON.stringify(history));
|
|
}
|
|
|
|
// Exportar historial como .json
|
|
function exportHistory() {
|
|
const data = localStorage.getItem('mdHistory');
|
|
downloadFile('history.json', data);
|
|
}
|
|
```
|
|
|
|
#### Opción D: Git via WebAssembly (Experimental)
|
|
|
|
**Pros**: Git real en el navegador, sin backend
|
|
**Contras**: Tecnología emergente, compleja, tamaño de bundle grande
|
|
|
|
Usar [isomorphic-git](https://isomorphic-git.org/) compilado a WASM para ejecutar operaciones git directamente en el navegador.
|
|
|
|
### Recomendación por Fases
|
|
|
|
1. **FASE 3.1** (Más simple): Opción C - LocalStorage + exportar historial
|
|
2. **FASE 3.2** (Híbrido): Opción B - Endpoint PHP mínimo para git local
|
|
3. **FASE 3.3** (Cloud): Opción A - GitHub API para equipos colaborativos
|
|
4. **FASE 3.X** (Futuro): Opción D - Git WASM cuando madure la tecnología
|
|
|
|
### UI Propuesta
|
|
|
|
```
|
|
┌──────────────────────────────────────────────────────────┐
|
|
│ [💾 Commit] [📜 History] [⬇️ Export] [Theme] [Help] │
|
|
├─────────────────────────┬────────────────────────────────┤
|
|
│ Editor │ Preview │
|
|
│ │ │
|
|
└─────────────────────────┴────────────────────────────────┘
|
|
|
|
Al click en "💾 Commit":
|
|
┌─────────────────────────────────────┐
|
|
│ Commit Message: │
|
|
│ ┌───────────────────────────────┐ │
|
|
│ │ Updated documentation │ │
|
|
│ └───────────────────────────────┘ │
|
|
│ │
|
|
│ [Cancel] [💾 Save] │
|
|
└─────────────────────────────────────┘
|
|
```
|
|
|
|
---
|
|
|
|
### Reference: Marked - Markdown Parser
|
|
========================
|
|
|
|
[Marked] lets you convert [Markdown] into HTML. Markdown is a simple text format whose goal is to be very easy to read and write, even when not converted to HTML. This demo page will let you type anything you like and see how it gets converted. Live. No more waiting around.
|
|
|
|
How To Use The Demo
|
|
-------------------
|
|
|
|
1. Type in stuff on the left.
|
|
2. See the live updates on the right.
|
|
|
|
That's it. Pretty simple. There's also a drop-down option above to switch between various views:
|
|
|
|
- **Preview:** A live display of the generated HTML as it would render in a browser.
|
|
- **HTML Source:** The generated HTML before your browser makes it pretty.
|
|
- **Lexer Data:** What [marked] uses internally, in case you like gory stuff like this.
|
|
- **Quick Reference:** A brief run-down of how to format things using markdown.
|
|
|
|
Why Markdown?
|
|
-------------
|
|
|
|
It's easy. It's not overly bloated, unlike HTML. Also, as the creator of [markdown] says,
|
|
|
|
> The overriding design goal for Markdown's
|
|
> formatting syntax is to make it as readable
|
|
> as possible. The idea is that a
|
|
> Markdown-formatted document should be
|
|
> publishable as-is, as plain text, without
|
|
> looking like it's been marked up with tags
|
|
> or formatting instructions.
|
|
|
|
Ready to start writing? Either start changing stuff on the left or
|
|
[clear everything](/demo/?text=) with a simple click.
|
|
|
|
[Marked]: https://github.com/markedjs/marked/
|
|
[Markdown]: http://daringfireball.net/projects/markdown/
|