{ "cells": [ { "cell_type": "markdown", "metadata": { "toc": true }, "source": [ "

Tabla de Contenido

\n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Introducción \n", "\n", "[Nikola](https://getnikola.com/) es un buena forma de crear un sitio web/blog estático. Es versátil, potente y de código abierto. Existen otras alternativas como, [Hugo](https://gohugo.io/), [Pelican](https://docs.getpelican.com/en/stable/quickstart.html), y [Sphinx](https://github.com/sphinx-doc/sphinx). Después de revisar los pros y contras, decidí usar Nikola principalmente por su integración con Jupyter Notebook y el Matlab-kernel.\n", "\n", "Hay buen material en internet sobre la instalación y uso de Nikola, pero pensé que sería útil mostrar mi metodología desde la perspectiva de alguién que nunca usó Anaconda, Jupyter y Nikola.\n", "\n", "Sin embargo, acá la lista de la documentación oficial y algunos tutoriales (inglés) que recomiendo:\n", "\n", "- [Nikola Getting Started](https://getnikola.com/getting-started.html).\n", "- [Nikola Handbook](https://getnikola.com/handbook.html#).\n", "- [Complete and comprehensive tutorial](https://randlow.github.io/posts/python/create-nikola-coding-blog/).\n", "- [Compact tutorial](https://jiaweizhuang.github.io/blog/nikola-guide/).\n", "- [For extra customization](https://notes.mikejarrett.ca/optimizing-your-nikola-blog-for-jupyter-notebooks/)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Pre-requisitos\n", "\n", "- [Anaconda](https://www.anaconda.com/).\n", "- [Jupyter Notebook](https://jupyter.org/).\n", "- [Matlab-kernel (jmatlab)](https://github.com/Calysto/matlab_kernel).\n", "- [VSCode](https://code.visualstudio.com/), pero puedes usar el editor que quieras\n", "- [GitHub pages](https://pages.github.com/).\n", "- Conocimiento en HTML, CSS, JS, Matlab, Python, Git, Markdown y reStructedText." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Configurar VSCode\n", "\n", "- Instalar [Python extension](https://marketplace.visualstudio.com/items?itemName=ms-python.python).\n", "- Asegúrate que esté habilitado `Python › Data Science: Allow Import From Notebook` en configuración de la extensión." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Crea un nuevo entorno con Anaconda o Conda \n", "\n", "- Descarga e instala [Anaconda](https://www.anaconda.com/products/individual#Downloads) o [Miniconda](https://repo.anaconda.com/miniconda/). Es tu decisión.\n", "- Asegúrate que `C:\\ProgramData\\Anaconda3` y `C:\\ProgramData\\Anaconda3\\Scripts` estén listado `path variables` de Windows.\n", "- En el navegador de Anaconda, crea un entorno llamado `jmatlabNikola` con `Python 3.6`.\n", "- O abre la terminal y corre,\n", " ```\n", " conda create -n jmatlabNikola python=3.6\n", " ```\n", "- Una nueva carpeta se creará en `C:\\Users\\\\.conda\\envs\\`\n", "\n", "**¿Por qué Python 3.6?**\n", "\n", "Porque el [Matlab engine](https://www.mathworks.com/help/matlab/matlab-engine-for-python.html) es compatible solo con Python 2.7, 3.6, y 3.7. La versión 3.7 es para Matlab 2019+, y la versión 3.6 es para Matlab 2018+. También, Python 3.6 es conocido como la versión más estable.\n", "\n", "**¿Por qué un entorno?** \n", "\n", "Anaconda (conda) es una potente **plataforma de administración de paquetes** que te permite instalar espacio de trabajo **auto-contenidos** (entorno) con diferentes kernels y librerías. Por ejemplo, digamos que tengo dos entornos:\n", "\n", "- `jmatlabNikola`: compuesto por Python 3.6, Jupyter Notebook, jmatlab, y Nikola.\n", "- `openCV_TF`: compuesto por Python 3.7, OpenCV, y TensorFlow.\n", " \n", "Entonces, si quiero escribir un post: \n", "\n", "- Corro `activate jmatlabNikola` → cambia a Python 3.6 (puedes verificar con `python --version`). \n", " ```\n", " (jmatlabNikola) C:\\Users\\\\blog>python --version\n", " Python 3.6.10 :: Anaconda, Inc. \n", " ```\n", " Cualquier post que escriba será compilado solamente en este entorno. Ninguna de las librerías del otro entorno(s) estará disponible. \n", "\n", "Si quiero trabajar con OpenCV:\n", "\n", "- Corro `activate openCV_TF` → cambia a Python 3.7. El kernel y las librerías de `jmatlabNikola` no están disponibles y no se mezclarán con este entorno.\n", " ```\n", " (openCV_TF) C:\\Users\\\\ML_project>python --version\n", " Python 3.7.6 :: Anaconda, Inc.\n", " ```\n", "\n", "**Notas**\n", "\n", "- No uses el entorno `base` de Anaconda si quieres probar paquetes. Úsalo como referencia porque es el entorno por defecto para tu sistema operativo.\n", "- Si ya tienes instalado Python, puedes usar Anaconda sin problema. Solo asegúrate que el `Anaconda python path` esté configurado en VSCode. \n", "- Tus documentos y carpetas de trabajo pueden estar en cualquier ubicación de tu disco duro, pero no los crees dentro las carpetas de los entornos." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Instalación de Jupyter Notebook, Nikola y jmatlab\n", "\n", "- Abre el terminal en `cd \"C:\\Users\\\\.conda\\envs\\jmatlabNikola\"`\n", "- Instala Jupyter Notebook:\n", " - Jupyter Notebook es instaladon por defecto cuando creas un entorno usando el navegador de Anaconda.\n", " - Con conda.\n", " ```\n", " conda install -c conda-forge notebook\n", " ```\n", " - Con pip.\n", " ``` \n", " pip install notebook\n", " ```\n", " - Instala las [`nbextensions`](https://jupyter-contrib-nbextensions.readthedocs.io/en/latest/install.html#install-the-python-package).\n", " ```\n", " pip install jupyter_contrib_nbextensions\n", " jupyter contrib nbextension install --user\n", " ```\n", " - Instala el Jupyter nbextension configurator.\n", " ```\n", " pip install jupyter_nbextensions_configurator\n", " jupyter nbextensions_configurator enable --user\n", " ```\n", " - Corre Jupyter Notebook por línea de comando para verificar que está instalado correctamente junto con las nbextensions. \n", " ```\n", " jupyter notebook\n", " ```\n", " Abrirá tu navegador en http://localhost:8888\n", " - Recomiendo habilitar las siguientes nbextensions\n", " - (some) LaTex environments for Jupyter\n", " - Equation Auto Numbering\n", " - CodeMirror mode extensions -> Matlab/Octave highlighting\n", " - Table of Contents (2) -> auto ToC\n", "- Instala Nikola.\n", " ```\n", " pip install Nikola[extras]\n", " ```\n", "- Instala Matlab-kernel.\n", " ```\n", " pip install matlab_kernel\n", " ```\n", "- Verifica los kernels instalados. \n", " ```\n", " jupyter kernelspec list\n", " ```\n", "- Verás algo parecido.\n", " ```\n", " Available kernels:\n", " matlab C:\\Users\\\\.conda\\envs\\jmatlabNikola\\share\\jupyter\\kernels\\matlab\n", " python3 C:\\Users\\\\.conda\\envs\\jmatlabNikola\\share\\jupyter\\kernels\\python3\n", " ```\n", "\n", "## Configurar Matlab\n", "\n", "- Abre la terminal como **administrador**\n", " ```\n", " activate jmatlabNikola\n", " ```\n", "- Ve a `cd \"C:\\Program Files\\MATLAB\\R2018b\\extern\\engines\\python\"`\n", "- Instala el Python engine.\n", " ```\n", " python setup.py install\n", " ```\n", "\n", "\n", "Listo!, ahora ya tienes todo lo que necesita tu entorno. \n", "\n", "**Notas**\n", "\n", "- Asegúrate que activaste el entorno `jmatlabNikola` como administrador solo para instalar el `matlab_kernel`.\n", "- Si tienes dudas sobre el kernel de Python, verifica con `python --version` cuando actives un entorno.\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Crea un sitio demo de Nikola\n", "\n", "- Crea un nuevo folder llamado `blog` donde quieras excepto dentro la carpeta del entorno `jmatlabNikola`.\n", "- Abre la terminal dentro tu carpeta `blog`.\n", "- Activa el entorno. \n", " ```\n", " activate jmatlabNikola\n", " ```\n", "- Inicializa el demo y llena su contenido.\n", " ```\n", " nikola init --demo\n", " ```\n", "- Construye (genera) los documentos HTML del sitio.\n", " ```\n", " nikola build\n", " ```\n", "- Revísalo en tu navegador. Abrirá el localhost en `http://127.0.0.1:8000`.\n", " ```\n", " nikola serve -b\n", " ```\n", "- Automaticamente `build` y `serve` tu sitio si deseas ver los cambios de tu sitio constantemente (**recomendado**). \n", " ```\n", " nikola auto\n", " ```\n", "- Limpia el contenido de la carpeta `/output/` cada vez que remuevas documentos manualmente de la carpeta `blog`. Úsalo antes del comando `build`.\n", " ```\n", " nikola check -f --clean-files\n", " ```\n", "- `ctrl+c` para detener el servidor.\n", "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Crear un nuevo post y una nueva página\n", "\n", "- Por defecto, ambos son creados en formato `.rst`.\n", " ```bash\n", " nikola new_post\n", " nikola new_page\n", " ``` \n", "- Otros formatos diferentes (`-f ipynb` necesita configuración extra, lo veremos más adelante).\n", " ```bash\n", " nikola new_post -f md\n", " nikola new_post -f rst\n", " nikola new_post -f html\n", " nikola new_post -f ipynb\n", " ```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Crear un sitio limpio de Nikola\n", "\n", "Lo mismo que en la anterior sección pero corre `nikola init` en vez de `nikola init --demo`.\n", "\n", "Listo!. Navega a través de los documentos y archivos para ver que hizo Nikola en el demo y la versión limpia." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Directorios de Nikola\n", "\n", "Aunque muchos de ellos se explican solos, pueden categorizarse en tres tipos:\n", "\n", "- Contenido: `files`, `galleries`, `images`, `listings`, `pages`, `posts`.\n", "- Personalización: `plugins`, `templates`, `themes`.\n", "- Salida visible: `output`. Acá, todo el contenido es convertido a `.html` para ser visible en cualquier servicio de alojamiento de páginas web como GitHub pages.\n", " ```bash\n", " ../__pycache__\n", " ../cache \n", " ../files # all the files of your blog, e.g. pdf, videos, etc.\n", " ../galleries # galleries of images.\n", " ../images # images of your website.\n", " ../listings # code files, e.g. .py, .tex, .m, .cpp, etc.\n", " ../output # the root directory of your website. \n", " ../pages # stores index.html, blog.html, home.html, about-me.html. \n", " ../plugins # additional plugins.\n", " ../posts # stores all your posts, .md, .html, .ipynb, and .rst\n", " ../templates # additional templates. \n", " ../themes # additional themes.\n", " ```\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Configurar un website Nikola (no un blog)\n", "\n", "Desde esta sección, configuraremos Nikola usando `conf.py` que está localizado en la raíz de tu directorio.\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Crear las páginas index y bio\n", "\n", "- Primero, creamos ambas páginas usando la termina.\n", " ```bash\n", " nikola new_page --title=\"index\"\n", " nikola new_page --title=\"bio\"\n", " ```\n", "- Los agregamos a la lista de navegación.\n", " ```python\n", " NAVIGATION_LINKS = {\n", " DEFAULT_LANG: (\n", " (\"/index.html\", \"Home\"),\n", " (\"/bio/index.html\", \"Bio\"),\n", " ...\n", " ),\n", " ```\n", "- Agrega otra página que desees, solo asegúrate añadirlos a la lista, e.g. about-me, contact, etc.\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Usar Jupyter Notebooks\n", "\n", "- Por defecto, Nikola usa la configuración **blog**.\n", " ```python\n", " POSTS = (\n", " (\"posts/*.rst\", \"posts\", \"post.tmpl\"),\n", " (\"posts/*.md\", \"posts\", \"post.tmpl\"),\n", " (\"posts/*.txt\", \"posts\", \"post.tmpl\"),\n", " (\"posts/*.html\", \"posts\", \"post.tmpl\"),\n", " (\"posts/*.ipynb\", \"posts\", \"post.tmpl\"),\n", " )\n", " PAGES = (\n", " (\"pages/*.rst\", \"pages\", \"page.tmpl\"),\n", " (\"pages/*.md\", \"pages\", \"page.tmpl\"),\n", " (\"pages/*.txt\", \"pages\", \"page.tmpl\"),\n", " (\"pages/*.html\", \"pages\", \"page.tmpl\"),\n", " (\"pages/*.ipynb\", \"pages\", \"page.tmpl\"), \n", " )\n", " ```\n", "- Cámbialo a la [**configuración no blog**](https://getnikola.com/creating-a-site-not-a-blog-with-nikola.html).\n", " ```python\n", " POSTS = (\n", " (\"posts/*.rst\", \"blog\", \"post.tmpl\"),\n", " (\"posts/*.md\", \"blog\", \"post.tmpl\"),\n", " (\"posts/*.txt\", \"blog\", \"post.tmpl\"),\n", " (\"posts/*.html\", \"blog\", \"post.tmpl\"),\n", " (\"posts/*.ipynb\", \"blog\", \"post_ipynb.tmpl\"),\n", " )\n", " PAGES = (\n", " (\"pages/*.rst\", \"\", \"page.tmpl\"),\n", " (\"pages/*.md\", \"\", \"page.tmpl\"),\n", " (\"pages/*.txt\", \"\", \"page.tmpl\"),\n", " (\"pages/*.html\", \"\", \"page.tmpl\"),\n", " (\"pages/*.ipynb\", \"\", \"post_ipynb.tmpl\"),\n", " )\n", " # And to avoid a conflict because blogs try to generate /index.html\n", " INDEX_PATH = \"blog\"\n", " ```\n", "- Verifica si `.ipynb` está listado como compilador.\n", " ```python\n", " COMPILERS = {\n", " \"rest\": ['.rst', '.txt'],\n", " \"markdown\": ['.md', '.mdown', '.markdown'],\n", " \"textile\": ['.textile'],\n", " \"txt2tags\": ['.t2t'],\n", " \"bbcode\": ['.bb'],\n", " \"wiki\": ['.wiki'],\n", " \"ipynb\": ['.ipynb'],\n", " \"html\": ['.html', '.htm'],\n", " ...\n", " ```\n", "- Si usas `build` y `serve` en el sitio, este te dará un error de `\"missing post_ipynb.tmpl\"`, lo crearemos en la siguiente sección.\n", "- Cambia `post_ipynb.tmpl` a `post.tmpl`, y corre `nikola auto` para ver el estílo por defecto de Jupyter Notebook." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Crear el template `post_ipynb.tmpl`\n", "\n", "- Crea un nuevo tema.\n", " ```\n", " nikola theme --new=themeBlog --parent=bootstrap4 --engine=mako\n", " ```\n", "- Copia `post.tmpl` del directorio del original de bootstrap4.\n", " ```\n", " nikola theme --copy-template=post.tmpl\n", " ```\n", "- Muevelo al folder `/themes/themeBlog/templates/post_ipynb.tmpl` y renombralo como `post_ipynb.tmpl`\n", "- Agrega lo siguiente a la sección `extra head` del `.tmpl`\n", " ```html\n", " <%block name=\"extra_head\">\n", " ... \n", " \n", " \n", " ```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Crea el `post_ipynb.css` \n", "\n", "Crea el documento en `/themes/themeBlog/assets/css/post_ipynb.css` y copia lo siguiente en su contenido.\n", "\n", "```css\n", "div.prompt {\n", " padding: 0.6em;\n", " font-size: 13px;\n", " background-color: #ffffff;\n", " margin-right: 0em;\n", " -webkit-border-radius: 3px;\n", " -moz-border-radius: 3px;\n", " border-radius: 3px;\n", "}\n", " \n", "div.output_prompt {\n", " /* 5px right shift to account for margin in parent container */\n", " margin: 0 5px 0 0px;\n", "}\n", " \n", " div.output_area pre {\n", " font-size: 13px;\n", "}\n", " \n", "div.text_cell_render {\n", " padding: 0px;\n", " color: #333333;\n", "}\n", " \n", ".rendered_html p {\n", " text-align: left;\n", "}\n", " \n", ".rendered_html ul {\n", " margin: 0 0 12px 25px;\n", "}\n", " \n", ".rendered_html :visited {\n", " text-decoration: none;\n", "}\n", " \n", ".rendered_html :link {\n", " text-decoration: none;\n", " }\n", " \n", ".rendered_html pre, .rendered_html code {\n", " background-color: #eeeeee;\n", " margin: 1em 0em;\n", " font-size: 14px;\n", "}\n", " \n", ".rendered_html pre {\n", " padding-left: 0.5em;\n", " padding-right: 0.5em;\n", " padding-top: 0.05em;\n", " padding-bottom: 0.05em;\n", "}\n", " \n", ".page-content > .content p {\n", " margin: 0 0 0px;\n", "}\n", "\n", "\n", "div.input {\n", " border: none;\n", " background-color: none;\n", "}\n", "\n", "/* set a max-width for horizontal fluid layout and make it centered */\n", ".body-content {\n", " margin-right: auto;\n", " margin-left: auto;\n", " max-width: 100%; /* or 950px */\n", "}\n", " \n", "/* Widens image-containing divs so that image is full body width */\n", "div.output_subarea { \n", " max-width: 100%;\n", "}\n", "```\n", "\n", "Personaliza a placer. En mi caso, entre otros cambios, puse `background-color: #ffffff` para eliminar la barra lateral gris de las celdas de Jupyter Notebook." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Versión limpia de `post_ipynb.css`\n", "\n", "```css\n", "div.prompt {\n", " display: none;\n", "}\n", "\n", "div.input {\n", " border: none;\n", " background-color: none;\n", "}\n", "\n", "div.input * {\n", " background-color: none;\n", "}\n", "\n", "/* set a max-width for horizontal fluid layout and make it centered */\n", ".body-content {\n", " margin-right: auto;\n", " margin-left: auto;\n", " max-width: 100%; /* or 950px */\n", "}\n", "\n", "/* Widens image-containing divs so that image is full body width */\n", "div.output_subarea {\n", " max-width: 100%;\n", "}\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Usa tu tema personalizado\n", "\n", "Una ves estés conforme con los cambios.\n", "\n", "- Habilita el tema.\n", " ```python\n", " THEME = \"themeBlog\"\n", " ```\n", "- Crea un post de Jupyter Notebook y empieza a editar con VSCode.\n", " ```\n", " nikola new_post -f ipynb --title=\"Jupyter-notebook-test\"\n", " ```\n", "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Configuraciones extras" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Ubicación del folder de salida `files` \n", "\n", "Tus documentos guardados en el folder `files` serán copiados a `/output/files` en vez de la raíz, que es su ubicación por defecto.\n", "```python\n", "FILES_FOLDERS = {'files': 'files'} \n", "```\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Evita publicar borrardores de los posts\n", "\n", "Si no deseas publicarlos.\n", "\n", "- Agrega los siguiente al metadata del post.\n", " ```\n", " .. status: draft\n", " ```\n", "- Configura lo siguiente en `conf.py`\n", " ```python\n", " DEPLOY_DRAFTS = False\n", " ```\n", "Será compilado y visto en tu `localhost` pero no será publicado en GitHub. También, cuando corras `nikola github_deploy`, el post borrador es removido del folder `output`.\n", "\n", "Por defecto la metadata no tiene la linea `status`, agrégalo.\n", "```\n", ".. status: published \n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Habilitar una sola lista de los posts\n", "\n", "Crea una sola lista grande de los posts en vez de una por año.\n", "```python\n", "CREATE_SINGLE_ARCHIVE = True\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Favicon\n", "\n", "Agrega tu favicon al sitio\n", "```python\n", "FAVICONS = (\n", " (\"icon\", \"/files/blogFavicon.ico\", \"128x128\"),\n", ")\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Licencia\n", "\n", "Agrega una licencia a tu contenido.\n", "```html\n", "LICENSE = \"\"\"\n", "\n", "\"Creative\"\"\"\n", "```\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Header (Encabezado)\n", "\n", "Agrega [FontAwesone](fontawesome.com/) y [Academic Icons](https://jpswalsh.github.io/academicons/) para usarlos en el contenido del pie de los posts.\n", "```html\n", "EXTRA_HEAD_DATA = '''\n", " \n", " \n", "'''\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Footer (Pié de página)\n", "\n", "Personaliza el footer como desees. Una buena forma es mostrar tus perfiles de redes sociales.\n", "\n", "```html\n", "CONTENT_FOOTER = '''\n", "
\n", "

\n", " \n", " \">\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "

\n", "

\n", " {license} Contents © {date} {author} - Powered by Nikola\n", "

\n", "
\n", "'''\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Barra de búsqueda de Google\n", "\n", "Habilitalo según lo siguiente.\n", "```html\n", "SEARCH_FORM = \"\"\"\n", "\n", "
\n", "
\n", " \n", " \n", " \n", "
\n", "
\n", "\n", "\"\"\" % SITE_URL\n", "```\n", "\n", "**Notas**\n", "- Puede que encuentres que `conf.py` usa [Glyphicon](https://www.glyphicons.com), pero recientemente Bootstrap 4+ cambió a [FontAwesome](https://stackoverflow.com/questions/32612690/bootstrap-4-glyphicons-migration), así que Glyphicon ya está disponible.\n", "- Cambia el `form-group` a `input-group`.\n", "- Más configuraciones en [Bootstrap 4 demo Icon inputs](https://www.codeply.com/go/ioPsDfyCBc).\n", "- El buscador de [Duckduckgo](https://duckduckgo.com/) también está disponbile." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Comentarios Disqus\n", "\n", "- Crea una cuenta en [Disqus](https://disqus.com/).\n", " - Crea un nuevo sitio.\n", " - Elige un `disqus_shortname`, e.g. `github_user_name`. Yo recomiendo no usar `.github.io`\n", " - Agrega tu blog `.github.io`.\n", " - Escoge el *'Plan Básico'* \n", " - Salta las instrucciones en la sección *'Escoge Plataforma'*.\n", "- Finalmente, habilita lo siguiente en `conf.py`\n", " ```python\n", " COMMENT_SYSTEM = \"disqus\"\n", " COMMENT_SYSTEM_ID = \"\"\n", " ```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Configura la barra del menu\n", "\n", "Puedes configurar que la barra del menu se mantenga pegado en la parte superior mientras te deslizas en la página.\n", "\n", "- Copia el template base.\n", " ```bash\n", " nikola theme --copy-template=base.tmpl\n", " ```\n", "- Muévelo a `/themes/themeBlog/templates/base.tmpl` y agrega lo siguiente.\n", " ```html\n", " \n", "