Compare commits

...

70 Commits

Author SHA1 Message Date
1fdd75cec6 merge: working branch(presentations) into main branch 2025-02-15 20:32:39 +01:00
a03f31e237 build(make): add translation generation 2025-02-15 19:43:14 +01:00
65dce1b060 docs(readme): follow last update about submodules 2025-02-15 19:18:12 +01:00
5b2683b3a4 feat(maintaining): Add plugins as submodules 2025-02-15 19:13:05 +01:00
43de32edd2 docs(readme): minor typo 2025-02-15 13:28:03 +01:00
263e1c5f20 docs(readme): typo in command 2025-02-15 12:35:30 +01:00
f1eba8b28a fix(theme): submodule address to https 2025-02-15 12:15:16 +01:00
b9c070b6c8 docs(readme): some info about building the blog 2025-02-15 12:12:41 +01:00
957c8e2ed1 fix(typst article): update the syntax to be consistent with the repository 2025-02-14 12:47:17 +01:00
1eed4d7ca3 fix(wget article/i11n): wrong category (English name) 2025-02-12 13:07:57 +01:00
ee6946373c fix(nvim article): link 2025-02-12 13:07:57 +01:00
30b1ce923f fix(nvim article): wrongly formated url + rephrasing 2025-02-12 10:04:23 +01:00
886221e821 fix(a11y): add alt text 2025-01-29 14:19:44 +01:00
665efa6b77 feat(presentations): add example code 2025-01-29 14:14:10 +01:00
9806dbd95b feat(presentations): continue the blog post
- introduction for reveal.js
- draft for customisation
- rewrite some other parts
2025-01-29 13:40:26 +01:00
19dcc79a98 feat(presenting): start reveal.js section 2025-01-29 10:16:24 +01:00
6e08f67ea4 feat(presenting): add a warning about authenticate-and-encrypt 2025-01-29 10:16:24 +01:00
87a2de2a07 feat(presentation): add typst section 2025-01-29 10:16:15 +01:00
5f07ecae00 merge: main into presentations
- Add `uv` support
2025-01-29 10:09:19 +01:00
1f2d9a9b3d feat(article): headings for latex and drawback subsection 2025-01-29 10:04:28 +01:00
2f1f346735 chores(cover images): compress the cover image 2025-01-29 10:04:28 +01:00
34f2980123 feat!(build): replace poetry with uv
- BREAKING CHANGE: poetry won’t work anymore
2025-01-28 22:16:36 +01:00
5ba13f48b7 Continue on latex presentations
- customisations
2024-11-03 20:52:07 +01:00
4da2ad6e6e fix(presentations): add images 2024-11-03 18:42:17 +01:00
6f0881d984 Start an article about making presentations 2024-11-03 18:35:24 +01:00
330a3d6b4e fix(typst): timeline issue 2024-10-19 23:09:23 +02:00
110c31099a chore(typst): clean unnecessary line 2024-10-19 21:39:01 +02:00
5364463d1f fix(typst): add a link to typst changelog 2024-10-19 21:26:41 +02:00
57e445a02d add(typst): add an example
+ proofreading
2024-10-19 21:12:20 +02:00
0eec47dc72 fix(typst): change the summary to be more accurate 2024-10-19 18:12:26 +02:00
f3eade6ef6 feat(typst): add a see also section 2024-10-19 18:12:11 +02:00
198c798f2c fix(typst): error in tags 2024-10-19 17:50:07 +02:00
85c5fcf3a2 perf(images): run jpgcrush on images 2024-10-19 17:48:01 +02:00
b35823a0a3 add(image): For future use 2024-10-19 17:47:14 +02:00
9aa3e1cb4d fix(typst): typo 2024-10-19 17:46:17 +02:00
c68e027c01 fix(invoke): publishing with simpler options 2024-10-19 17:45:42 +02:00
59fe511c25 Add an article about typst 2024-10-19 17:42:53 +02:00
7f1d2b274e fix(configuration): LOCALE variable 2024-10-19 14:19:08 +02:00
ddb749e247 Makefile is back 2024-10-19 14:06:13 +02:00
8fc7b7346f Deployement: Use invoke
- https://docs.getpelican.com/en/latest/publish.html#invoke
2024-10-19 14:04:02 +02:00
b13c2c7ff3 Use poetry to manage dependencies 2024-10-19 13:28:32 +02:00
2bd00b4b46 pass-fr: phrasing
🇫🇷
2024-02-24 20:11:27 +01:00
120df63984 pass: add paragraph about key management 2024-02-24 20:03:27 +01:00
9d086e6436 pass: polishing the article (typo and stuff) 2024-02-24 19:02:59 +01:00
39a90c8245 Add PASSWORD_STORE_CHARACTER_SET in pass description 2024-02-24 19:01:21 +01:00
eb653d1196 Missing NVIM_APPNAME typo 2024-02-06 14:36:44 +01:00
62d90435c3 Typo: NVIM_APP → NVIM_APPNAME 2024-02-06 11:35:23 +01:00
492165db60 Another typo (m) 2024-02-04 22:11:47 +01:00
ac87a6080d Fix a typo (m) 2024-02-04 22:09:57 +01:00
fa03ca647c Add RSS+Atom Feeds 2023-12-25 18:16:41 +01:00
bbf3f8163a Fix date 2023-12-25 18:14:41 +01:00
80a277b062 Add article about vim configuration. 2023-12-25 17:46:56 +01:00
04f8994933 xidel: forgot to put a description 2023-10-29 23:01:13 +01:00
6a2670c94d Add cli tag 2023-10-29 22:51:03 +01:00
7e05fb1432 Some uniformization 2023-10-29 22:36:26 +01:00
3912a2a74c Add cover image for xidel 2023-10-29 22:32:41 +01:00
1aff1e7336 Orgue cover image 2023-10-29 22:27:28 +01:00
ecd50343a4 Duplicate date 2023-10-29 22:15:05 +01:00
d79c9e9d47 + CLI tag 2023-10-29 22:13:57 +01:00
6cf9edd955 New article about xidel 2023-10-29 22:11:40 +01:00
d9e2205553 Suggestion 2023-10-14 19:14:45 +02:00
41b4bba16b Add lua remap in emails-md 2023-10-14 19:07:34 +02:00
03987fea67 Proofreading 2023-10-14 18:11:44 +02:00
2e703e3785 Error in url 2023-10-14 17:04:11 +02:00
d8c06e9f49 nvim-latex: add see also section + a caveat about bépo bindings 2023-10-14 16:41:34 +02:00
480e6a38e2 Proofreading 2023-10-14 13:11:57 +02:00
6505b22d90 Add nvim-latex article (in English) 2023-10-14 12:30:07 +02:00
d2c5efb43a t2svg: fix mistaken link 2023-07-10 12:02:35 +02:00
efa6fd31c5 fix date 2023-07-10 11:58:56 +02:00
9dd21a7b40 Backward fixes thanks to the translation 2023-07-10 11:57:56 +02:00
53 changed files with 2336 additions and 37 deletions

3
.gitignore vendored
View File

@ -1,5 +1,4 @@
output
__pycache__
Makefile
*.pid
plugins
cache

8
.gitmodules vendored
View File

@ -1,3 +1,9 @@
[submodule "themes/clean-blog"]
path = themes/clean-blog
url = ssh://gitea@git.epheme.re:2222/fmouhart/pelican-clean-blog.git
url = https://git.epheme.re/fmouhart/pelican-clean-blog.git
[submodule "plugins/autopages"]
path = plugins/autopages
url = https://git.epheme.re/fmouhart/pelican-autopages.git
[submodule "plugins/i18n_subsites"]
path = plugins/i18n_subsites
url = https://git.epheme.re/fmouhart/pelican-i18n_subsites.git

21
Makefile Normal file
View File

@ -0,0 +1,21 @@
all: build
build:
uv run pelican -s publishconf.py
dev:
uv run invoke livereload
publish:
uv run invoke publish
clean:
uv run invoke clean
%.mo: %.po
msgfmt "$^" -o "$@"
init: themes/clean-blog/translations/fr/LC_MESSAGES/messages.mo
uv sync
.PHONY: clean build publish dev init

View File

@ -4,7 +4,7 @@ Date: 2019-04-22 17:00
Modified: 2023-05-14 20:00+02:00
Author: Fabrice
Category: antisèches
Tags: git, termtosvg
Tags: git, termtosvg, cli
Slug: git-tricks
Header_Cover: ../images/covers/water.jpg
Summary: Une compilation de commandes git que jutilise ponctuellement

View File

@ -4,7 +4,7 @@ Date: 2019-04-22 17:00
Modified: 2023-05-14 20:00+2:00
Author: Fabrice
Category: cheat sheets
Tags: git, termtosvg
Tags: git, termtosvg, cli
Slug: git-tricks
Header_Cover: images/covers/water.jpg
Summary: A compilation of some `git` tricks I keep forgetting.

View File

@ -2,8 +2,8 @@
Title: wget/curl
Date: 2022-07-25 13:45 CEST
Author: Fabrice
Category: cheat sheets
Tags: wget, curl
Category: antisèches
Tags: wget, curl, cli
Slug: wget-curl
Header_Cover: ../images/covers/speedboat.jpg
Summary: Quelques commandes wget et curl utiles dans la vie de tous les jours.

View File

@ -3,7 +3,7 @@ Title: wget/curl
Date: 2022-07-25 13:45 CEST
Author: Fabrice
Category: cheat sheets
Tags: wget, curl
Tags: wget, curl, cli
Slug: wget-curl
Header_Cover: images/covers/speedboat.jpg
Summary: Some useful wget and curl commands, such as downloading a repository.

Binary file not shown.

After

Width:  |  Height:  |  Size: 381 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 548 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 94 KiB

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 227 KiB

After

Width:  |  Height:  |  Size: 224 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 98 KiB

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 224 KiB

After

Width:  |  Height:  |  Size: 220 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 204 KiB

After

Width:  |  Height:  |  Size: 199 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 319 KiB

After

Width:  |  Height:  |  Size: 309 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 481 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 469 KiB

After

Width:  |  Height:  |  Size: 465 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 291 KiB

After

Width:  |  Height:  |  Size: 281 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 473 KiB

After

Width:  |  Height:  |  Size: 451 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 487 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 280 KiB

After

Width:  |  Height:  |  Size: 267 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 470 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 219 KiB

After

Width:  |  Height:  |  Size: 213 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 185 KiB

After

Width:  |  Height:  |  Size: 182 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 320 KiB

After

Width:  |  Height:  |  Size: 308 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

View File

@ -1,7 +1,8 @@
---
title: Bienvenue
pagetitle: le blog de fmouhart: bienvenue
date: 2019-04-22
Date: 2019-04-22
Modified: 2019-04-22
author: Fabrice Mouhartem
Header_Cover: ../images/covers/home.jpg
slug: home
@ -9,7 +10,7 @@ save_as: ./index.html
URL:
og_image: ../images/miniature.png
article_list: true
Lang: fr
lang: fr
---
Bienvenue sur mon *blog*, je suis Fabrice Mouhartem.
@ -23,3 +24,7 @@ Si vous cherchez une version anglophone de ce blog, c'est [par là]({filename}in
Pour toutes remarques ou commentaires, nhésitez pas à me contacter par e-mail à ladresse suivante : <img style="height:2em" src="/images/mel.png" alt="courriel"/>.
Si vous voulez être mis au courant des nouveaux articles (en anglais et en français), des flux de syndication sont disponibles:
* [Atom](/feeds/all.atom.xml)
* [RSS](/feeds/all.rss.xml)

View File

@ -1,7 +1,8 @@
---
title: Welcome
pagetitle: fmouhart's blog: welcome!
date: 2019-04-22
Date: 2019-04-22
Modified: 2023-12-25
author: Fabrice Mouhartem
Header_Cover: images/covers/home.jpg
slug: home
@ -13,9 +14,14 @@ Lang: en
---
I'm Fabrice Mouhartem, and I welcome you to this _blog_, which aims at relieving my brain from the hard task of remembering all sorts of weird stuffs (thus, don't expect me to provide you some fresh original contents). I hope it'll help some of you as well.
Those stuffs will be mainly about my everyday use of _linux_ but not only.
Those stuffs will be mainly about my everyday use of _Linux_ but not only.
If you are lost and are looking for my professional page, it's available [**here**](https://fmouhart.epheme.re) where you can find a list of my [annex activities](https://fmouhart.epheme.re/pages/etc.html) as well as a list of [free and open source software I everyday use](https://fmouhart.epheme.re/pages/etc.html#free-and-open-source-software).
If you are lost and are looking for my professional page, it's available [**here**](https://fmouhart.epheme.re) where you can find a list of my [annex activities](https://fmouhart.epheme.re/pages/etc.html) as well as a list of [free and open source software I use every day](https://fmouhart.epheme.re/pages/etc.html#free-and-open-source-software).
If you are looking for a French version of this blog, you take a look over [there]({filename}index-fr.md).
If you want to send me any questions or comments about this blog, feel free to do so at <img style="height:2em" src="/images/mel.png" alt="courriel"/>.
For those interested to follow the blog, there are the following feeds:
- [Atom](/feeds/all-en.atom.xml) and [Atom (fr + en)](/feeds/all.atom.xml)
- [RSS](/feeds/all-en.rss.xml) and [RSS (fr+en)](/feeds/all.rss.xml)

View File

@ -0,0 +1,329 @@
---
Title: Setup Neovim with kickstart.nvim
Date: 2023-12-25 17:15
Modified: 2025-02-12 13:00
Lang: en
Author: Fabrice
Category: software
Tags: vim, neovim, setup
Slug: nvim-kickstart
table-of-contents: true
Header_Cover: ../images/covers/antennae.jpg
Summary: How to create a sane Neovim configuration with kickstart.nvim as a
starting point.
---
# Introduction
## How I managed my configuration
When I started using Vim, I started editing my `.vimrc` bit by bit and
incrementally before it starts getting too big for me to find anything inside
it and not using even half of the plugins I installed. That goes without saying,
there were quite a bit on conflicting keymaps as well as I'm using
[bépo](http://bepo.fr/) as my keyboard layout with [partial remaps
(fr)](https://cdn.bepo.fr/images/Vim-bepo-066.png).
Obviously, it slowly became quite a mess. To address this issue, I decided to
reorganise my `$HOME/.config/vim` directory using the [vim directory
structure](https://www.panozzaj.com/blog/2011/09/09/vim-directory-structure/)
and did some cleanup at this point of time. I think it was also around this
period that I discovered that Vim8 added a native package manager that I started
to use. Thus, at this point, I started organising my configuration with semantic
files, such as `$VIMHOME/plugin/spelling.vim` to manage my spelling
configuration for instance. This approach makes debugging easier, and also
checking custom keyboard shortcuts easier, as I just have to check
`$VIMHOME/plugin/omnicomplete.vim` for instance to know which shortcuts I set up
when I'm still getting the habits of using them.
At some point of time, I moved to Neovim, and simply moved my configuration from
Vim to Neovim. All the while continuing adding more and more plugins on top of
each other depending on my hype, especially because the world of Neovim plugins
opened up to me. Needless to say that less than half of these plugins were put
into good use. Which leads to my first configuration big cleanup.
Six months ago, I wiped my _frankenconfig_, and started back from scratch in
[lua](https://lua.org/about.html), with the same structural approach as
previously, but now wondering if the plugin would be useful or not. Since my
first time using Vim, there were some big changes in the vim ecosystem,
especially in language management with
[tree-sitter](https://tree-sitter.github.io/tree-sitter/) and
[lsp](https://en.wikipedia.org/wiki/Language_Server_Protocol). These two bring
into the environment a unified way to manage languages without depending on
language-specific plugins. Henceforth, I didn't need specific plugins to have
nice syntax coloration for obscure languages anymore, or get frustrated with
[omnicomplete](https://vim.fandom.com/wiki/Omni_completion) which decided not to
work only for some languages… While it's not an absolute rule (for instance, I'm
using [vimtex]({filename}./nvim-latex.md) for latex, which includes a more
accurate syntax coloring than tree-sitter). I also moved from the native vim way
of managing plugins to use [`Lazy`](https://github.com/folke/lazy.nvim) as a
proper plugin manager, which helped me synchronize my configuration between my
different computers. It was working nice and well, with some weird bugs (see
below) on first install, but as it was punctual… I just ignored it.
```plain
E5113: Error while calling lua chunk:
$VIMHOME/nvim/init.lua:13: E21: Cannot make changes, 'modifiable' is off
```
However, I was unhappy with some of my configurations, and if I managed to have
something functional, there were many details that annoy me that stemmed for
some configuration I wrote some times ago and of course didn't document. This
leads us to today, where I just decided to use
[`kickstart.nvim`](https://github.com/nvim-lua/kickstart.nvim). It is a well
documented vim starting configuration (it's not a distribution, it still
requires your inputs to obtain something that fits your needs), which was
exactly what I needed to start anew… but not fully from scratch.
## The migration
To move my configuration from `kickstart.nvim`, I wanted to get the best of both
world. For instance, I didn't want to have an
[`init.lua`](https://github.com/nvim-lua/kickstart.nvim/blob/master/init.lua)
that is over 600 lines long. I thus decided to split it into short files that
manages a specific part of the configuration: completion, lsps, treesitter,
mappings after reading the different configuration default from nvim-kickstart
and changing what I disliked. To do that, I started with using the
[`NVIM_APPNAME`](https://practical.li/neovim/configuration/) environment option
in order to make the move in a non-destructive way.
After installing the bare minimum to make it usable for me (as a bépo user), I
exported the `NVIM_APPNAME` variable to start using my configuration to help me
debug it on the fly.
I also decided to write this blog post to remember the process and maybe helped
some people who want to configure their text editor.
Note that in the following, we will assume that the reader is already familiar
with Neovim and lua.
# Configuring Neovim
My (in use) configuration for Neovim is available [here](https://git.epheme.re/fmouhart/nvim-config-kickstart).
I started using git from the start in order to remember what I did by using its
[history](https://git.epheme.re/fmouhart/nvim-config-kickstart/commits/branch/master).
As it's not the easier thing to read however, here follows my rationals, and
thought during this process in a chronological order.
## kickstart.nvim
`kickstart.nvim` is a starting Neovim configuration file which was created by
[TJ DeVries](https://github.com/tjdevries), a core developer of Neovim, author
of [telescope.nvim](https://github.com/nvim-lua/telescope.nvim) and content
creator about Neovim. You can see a quick presentation on his YouTube channel
[[here]](https://www.youtube.com/watch?v=stqUbv-5u2s).
To summarise, it is a starting configuration including a minimal set of plugins
that helps to have a modern editor, that has a working and customizable LSP
configuration with [Mason](https://github.com/williamboman/mason.nvim) to help
install LSP servers, git helpers, completion, telescope, and a choice of
shortcuts that are quite natural to learn (unlike what I used previously because
I was simply adding shortcuts one after another without thinking of the
compatibility between them).
Note that it is made for educational purpose, and thus is not modularised as is
(hence the single self-contained `init.lua` file). Which leads us to our first
step after simply pasting the content of `init.lua` into the `$VIMHOME/init.lua`
file.
## Modular configuration
When in doubt about some shortcuts, I'm under the habit of going to read the
corresponding configuration file in my `$VIMHOME/plugin` directory.
`kickstart.nvim` includes `which-key`, that is a plugin that pops a helper when
waiting for a command as shown hereunder.
[![which-key plugin
illustration](../images/nvim-which-keys.png)](../images/nvim-which-keys.png)
Thanks to that, I start getting rid of this habit. However, having a modular
configuration helps debugging it. Usually, when an error spawns, the filename
and location of what has triggered it appears in the stack trace, and it's
easier to search in a short file than a thousand-line long one.
After a first read of the configuration file, I decided to split it into smaller
files. Note that if you want to start directly from there a project that does
exactly that exists:
* [kickstart-modular.nvim](https://github.com/dam9000/kickstart-modular.nvim)
However, I find it easier for educational purpose to have everything in one
place to linearly read it first.
The process was quite simple, as the file was already divided logically into
components that make sense, I just had to take the content of those sections and
move them into the `$VIMHOME/lua` folder before including them in `init.lua`. I
hesitated to continue using the `$VIMHOME/plugin` directory for that, but I then
realised that having it inside init.lua allows having a structure that allows
using `init.lua` as an index, and I can start
[jumping](https://vimhelp.org/motion.txt.html) from there to access my
specific configurations.
To illustrate it, let's take the example of completion. In `kickstart.nvim`,
there is a section called "Configure nvim-cmp", that deleted and pasted into a
file `$VIMHOME/lua/complete.lua` before adding the line `require('complete')` to
load it. You can see the result in this
[commit](https://git.epheme.re/fmouhart/nvim-config-kickstart/commit/7ce423fade9d6877b2e9174dd9b9dea36254ac19).
## Survival
Now that it's done, I need my basic keymaps for using Vim/Neovim in bépo. I
added the file `$VIMHOME/lua/bepo.lua` and simply load it with a line
`require('bepo')` in my `$VIMHOME/init.lua` file.
I also merged my personal remap inside the `$VIMHOME/lua/mappings.lua` file
which already contained the ones imported from `kickstart.nvim` from the
previous step. These mappings are convenient ones such as the following one to
easily open folds to a given level.
```lua
local keymap = vim.keymap.set
-- z0…z9 to open folds to a certain level
for i=0,9 do
keymap('n', 'z' .. i , ':set fdl=' .. i .. '<CR>', {noremap = true, silent = false})
end
```
I also merged and cleaned redundant general options in the
`$VIMHOME/lua/general-options.lua` file. For instance to ignore folded content
when jumping between paragraphs for instance, there is the following line in the
aforementioned file:
```lua
-- folds
vim.g.ip_skipfold=true
```
Once all of that is done, at this point of time, I started moving to use the
configuration by exporting `NVIM_APPNAME=new_nvim` inside my `.zshrc`.
The idea is that now I bootstrapped my Neovim config to proficiently edit the
configuration, which in lua also uses LSP and many of `kickstart.nvim` features.
During this process I notice what I liked and disliked to know how to edit the
configuration while editing the configuration.
## Importing my former configuration
After having the minimal setup top edit a configuration, I now need to make
things work for other things as well.
That is dependent of your workflow. For reference, I personally use Neovim to:
* Write text in [markdown](https://daringfireball.net/projects/markdown/) (such as what I'm currently doing)
for different purposes;
* blog post, emails or more generally text blocks that will be exported in
html.
* Read text written in Markdown;
* Try to organise my life using [neorg](https://github.com/nvim-neorg/neorg);
* [LaTeX](https://en.wikipedia.org/wiki/LaTeX) documents and slideshows;
* Write code in a variety of languages.
To do that I went to my previous configuration (which fortunately was already
using [`Lazy`](https://github.com/folke/lazy.nvim) as a plugin manager). To do
that, I picked my specific plugins, for instance `Neorg`, to import the
configuration. For the example of `Neorg` it has multiple steps as I had a
`$VIMHOME/plugin/neorg.lua` that contained my general configuration and
`$VIMHOME/ftplugin/norg.lua` which has specific configuration when editing a
note in `Neorg`.
After asking `Lazy` to install `Neorg`, I first imported the general
configuration from my previous `plugin` folder in my new `lua` folder, as
depicted by this
[commit](https://git.epheme.re/fmouhart/nvim-config-kickstart/commit/0e8587b461b67082c1e34ef9cb07180ccfee9f8b).
After wondering if I should create a `ftplugin` directory, I finally decided to
move to autocommands to manage different filetypes. The reason behind that was
that I had a limited amount of lines in total in my former `ftplugin` directory,
and the subdivision with `augroup` and `autocmd` in Vim makes it reasonably
readable. Which brought me to this
[commit](https://git.epheme.re/fmouhart/nvim-config-kickstart/commit/c506a7ea7868ba80dc053a59cbb66c5efa666e2c).
Then some sanity and cleanup have been made. For example, adding a description
field to my key maps so that `which-key` nicely prints them. See this
[commit](https://git.epheme.re/fmouhart/nvim-config-kickstart/commit/689bb2024d97fdc9e5e656517f00e446e95ae198).
Now, rinse and repeat for each plugin/specific configuration set: `vimtex`,
`vim-pandoc-syntax` (which I mainly use for the
[concealer](https://vimhelp.org/syntax.txt.html#conceal)), spelling
configuration, etc.
All the while keeping a critical eye on what I'm moving. For instance, I have a
mapping on the following command to fix typo on a misspelled word under the
cursor when writing text: <code>mz[s1z=\`z</code>. However, the key was bound at
anytime, even when spelling was not enabled. To fix that, we changed the way the
binding was called: instead of being called everytime, we embedded it inside an
[`autocmd`](https://vimhelp.org/autocmd.txt.html#autocommands) which [triggers
when the option](https://vimhelp.org/autocmd.txt.html#OptionSet) `spell` is set.
To see the resulting configuration, see these two files:
[`autocommands.lua`](https://git.epheme.re/fmouhart/nvim-config-kickstart/src/commit/d0afa1066cabab3d2b0aa7e2e84a267ce0532c61/lua/autocommands.lua#L32-L37)
for the autocommand setting, and
[`spelling.lua`](https://git.epheme.re/fmouhart/nvim-config-kickstart/src/commit/d0afa1066cabab3d2b0aa7e2e84a267ce0532c61/lua/spelling.lua)
for the utils module.
Just for the record, this is what it does:
1. Store the current cursor location: `mz` [sets a
mark](https://vimhelp.org/motion.txt.html#mark) z on current position;
2. [Go to the previous spelling error](https://vimhelp.org/spell.txt.html#%5Bs): `[s`;
3. [Pick the first spelling suggestion](https://vimhelp.org/spell.txt.html#z%3D): `1z=`;
4. Go back to the stored location: <code>\`z</code> [jumps to
mark](https://vimhelp.org/motion.txt.html#%60) z at the right column.
## Adjusting the configuration
While testing the default behaviour of `kickstart.nvim`, which changes quite a
few things for me, I realised that some of them were quite smart, such as
<Space> as the leader key, which was on `,` before because of its accessibility
in bépo, disabling quite a [useful motion
binding](https://vimhelp.org/motion.txt.html#%2C), some are more personal
tastes, such as the default register to be the system one or not.
I thus tried some of these options and decided while editing the configuration
and writing this blog post which one where working nicely for me (with the help
of `which-key` to help me during this whole process), and which one were not
quite working (`unnamedplus` as default clipboard, I really use both separately,
and I don't want to have my system clipboard polluted)
Moreover, `:healthcheck which-keys` was really helpful to debug some colliding
key bindings, especially because of bépo, which has been incrementally fixed
while editing the configuration. I already wrote a blog post about how I handle
those in [vimtex]({filename}./nvim-latex.md).
My former configuration also featured LSP, but Mason was not used, which made me
install my LSP server from my system package manager. However, I still prefer
using the system one for some languages that features some changes between
versions, which I [also
configured](https://git.epheme.re/fmouhart/nvim-config-kickstart/src/commit/d0afa1066cabab3d2b0aa7e2e84a267ce0532c61/lua/lsp.lua#L120-L149)
in the `$VIMHOME/lua/lsp-configure.lua` file.
# Final thoughts
To conclude, I would like to say thanks to the French
[tuppervim](https://tuppervim.org) community, which regularly organises meetings
where we can show our latest configuration file, or just exchange nice tips. I
discovered both [TJ DeVries](https://www.youtube.com/c/TJDeVries) and
[`kickstart.nvim`](https://github.com/nvim-lua/kickstart.nvim) there.
I still have to get rid of some habits (such as the comma as a leader key) and
get used to it, but I'm happy with the change so far, beside knowing exactly
what is in my configuration, it also helped me fix some weird key conflicts
while editing markdown, making writing this blog post quite pleasant.
While trying the Git-related key bindings utilities bundled with
`kickstart.nvim`, while being quite minimal, it still filled my needs
(especially adding a hunk from visual selection, which is helpful to split
commits into sub-blocks when `git add -p` would have been quite tedious to use).
The [status bar](https://github.com/nvim-lualine/lualine.nvim) was also a
pleasant surprise for me. It is exactly the kind of things I find useful but a
pain to configure. I may tweak it a bit in the future, but so far it's fine as
it is.
The configuration will continue to evolve from this point, as my use of computer
and Neovim will change as well. And to finish, I think what TJ Devries said in
[this video](https://www.youtube.com/watch?v=QMVIJhC9Veg) about text editor is
quite on point: you don't have to spend time in your configuration if it is not
fun for you, just take something that works for you. I actually took the time to
do it because I find it interesting and fun 🙂
If you also find it fun, and want to try it, I strongly encourage you to take a
cup of hot cocoa, put on some relaxing music, and just dive head first!

View File

@ -0,0 +1,579 @@
---
Title: Neovim as a LaTex Development Environment
Date: 2023-10-14 12:00:00+0200
Modified: 2023-10-14 17:00:00+0200
Lang: en
Author: Fabrice
Category: software
Tags: vim, neovim, latex, zathura
Slug: nvim-latex
table-of-contents: true
Header_Cover: ../images/covers/fern-forest.jpg
Summary: How to turn Neovim into a full-fledged latex development environment
---
# Introduction
[LaTeX](https://en.wikipedia.org/wiki/LaTeX) is a typesetting software for
producing typographically sound printable documents that is mostly used by the
scientific community (but [not
only](https://www.ctan.org/pkg/latex-sciences-humaines)) as it allows writing
mathematics formulae in a somewhat *not-that-much painful* way, is shipped with
[bibliography engines](https://www.ctan.org/pkg/biblatex), enables easy
cross-referencing and automatically generates table of contents.
It is based on a markup language that allows writers to focus on the content of
the document and leaves the typesetting to the software (at least most of the
time).
It moreover enjoys [many](https://ctan.org/) libraries that span from enabling
[new features](https://ctan.org/pkg/algorithm2e) to [simpler
version](https://www.ctan.org/pkg/a4wide) of more [complete
tools](https://ctan.org/pkg/geometry).
In this blog post we will see how to setup [Neovim](https://neovim.io/) to
manipulate LaTeX document while enabling modern features such as
[language server
protocol](https://en.wikipedia.org/wiki/Language_Server_Protocol) and what you
want from any LaTeX IDEs: forward and backward
searches (respectively going from the source code to the resulting document and
vice-versa, thanks to
[synctex](https://tug.org/tugboat/tb29-3/tb93laurens.pdf)).
As a PDF reader, we will use [zathura](https://pwmt.org/projects/zathura/) to
show how to setup backward search (search from the document toward the source).
It is a highly configurable, lightweight document viewer which natively enjoys
vim-like shortcuts.
# Ingredients
Before starting we will need several components to achieve this lofty goal of
painlessly writing LaTeX documents with the best text editor. Let us start by
listing them; we will shortly see the installation and configuration procedure:
* A configurable text editor to be able to write the document:
[Neovim](https://neovim.io). For that we will also need some plugins to
unleash its full capability:
* [nvim-lspconfig](https://github.com/neovim/nvim-lspconfig): a plugin to
facilitate the configuration
[LSP](https://en.wikipedia.org/wiki/Language_Server_Protocol) for `Neovim`.
* [nvim-cmp](https://github.com/hrsh7th/nvim-cmp): a completion engine for
`Neovim`.
* [vimtex](https://github.com/lervag/vimtex): a language specific plugin for
LaTeX files that supports many features such as accurate syntactic
coloration, support of multi-files, add LaTeX-specific [text
objects](https://vimhelp.org/motion.txt.html#text-objects), improved
foldings and so on.
* [texlab](https://github.com/latex-lsp/texlab): to enable LSP features, you
also a LSP server for vim to communicate with, which is exactly what
`texlab` is.
* [zathura](https://pwmt.org/projects/zathura/): finally a PDF viewer, we will
use `zathura` here, but `vimtex` supports many others with predefined setups.
However you will have to look for the specific documentation of your pdf
reader to enable reverse search if it is possible.
# Setting Neovim up
Now that we have prepared everything, we need to setup `Neovim`.
We will assume a blank configuration and start from scratch.
I got inspired by a [blogpost about snippets in
Neovim](https://pcoves.gitlab.io/en/blog/nvim-snippets/#installation) and used
`NVIM_APPNAME` environment variables for testing this configuration. Please let
me know if anything is not working as intended.
## Being Lazy
Anyhow, we first need to install the different plugins that we need. For this
purpose, I used the [lazy](https://github.com/folke/lazy.nvim) plugin manager,
but you can use whichever you see fit for the task.
```lua
-- Lazy Package Manager
local lazypath = vim.fn.stdpath("data") .. "/lazy/lazy.nvim"
if not vim.loop.fs_stat(lazypath) then
vim.fn.system({
"git",
"clone",
"--filter=blob:none",
"https://github.com/folke/lazy.nvim.git",
"--branch=stable", -- latest stable release
lazypath,
})
end
vim.opt.rtp:prepend(lazypath)
-- Packages
require("lazy").setup({
"lervag/vimtex",
"neovim/nvim-lspconfig",
"hrsh7th/cmp-nvim-lsp",
"hrsh7th/nvim-cmp",
})
```
In the code block above —in `$NVIM_CONFIG/init.lua`— the first part is to bootstrap lazy (so it can install
itself if not already there) and the last block describe the installation of the
following plugins : `vimtex`, `nvim-lspconfig`, `nvim-cmp` and finally
`cmp-nvim-lsp` to glue the completion engine and `lspconfig`.
Now it is all good and done, but nothing is configured yet, and if you open a
LaTeX file in this state, you will only enjoy the benefits of an unconfigured
`vimtex`, which is already nice as is it, but not enough to achieve our goal.
And it's a bit sad to have installed three other plugins for nothing.
# vimtex
It will be a bit anti-climatic after the previous teasing, but we will use
`vimtex` as vanilla as possible…
We still need to tell it to use `zathura` as a pdf viewer:
```lua
vim.g.vimtex_view_method = "zathura"
```
This will allow `vimtex` to automatically open `zathura` upon compilation,
which is bound to `<LocalLeader>ll` by default. Meaning that we have to define
[`<LocalLeader>`](https://neovim.io/doc/user/map.html#%3CLocalLeader%3E), which
I usually set to be a comma: “`,`”:
```lua
vim.g.maplocalleader = ","
```
Now, you can use `,lv` to view the current line in `zathura`, and `,ll` to
compile your document. Yay!
More can be then done, such as using vimtex folds, which are disabled by
default (contrary to what [vim-latex](https://github.com/vim-latex/vim-latex)
was doing, which is the former plugin I used):
```lua
-- From: https://github.com/lervag/vimtex/blob/master/doc/vimtex.txt#L4671-L4713
vim.o.foldmethod = "expr"
vim.o.foldexpr="vimtex#fold#level(v:lnum)"
vim.o.foldtext="vimtex#fold#text()"
-- I like to see at least the content of the sections upon opening
vim.o.foldlevel=2
```
Now the sky is your limit, but to start with, here follows a quick list of what
is possible now:
- Compile the document: `,ll`
- This also automatically generates a [quickfix
buffer](https://vimhelp.org/quickfix.txt.html) which is quite complete… even
a tad bit too much sometimes.
I used it as is to hunt for over/underfull hboxes, but you can filter them
out by setting the
[`vim.g.vimtex_quickfix_ignore_filters`](https://github.com/lervag/vimtex/blob/master/doc/vimtex.txt#L2365-L2378)
variable.
- View the current location in the document: `,lv`.
- Show table of content navigation: `,lt`.
* Using latex-specific text objects such as `$` for math or `e` for environment
(defined by `\begin{…}` and `\end{…}`).
- Insert command/environment : `<F6>/<F7>` (in normal and visual mode; these are
not very accessible, but can be remapped).
- Support for [TeX
directives](https://github.com/lervag/vimtex/blob/master/doc/vimtex.txt#L481-L504)
(which are common with others LaTeX' IDEs), such as
`%! TeX program = xelatex` to specify a latex compiler.
* For machine-aided proofreading, you can also enable [grammar checking
tools](https://github.com/lervag/vimtex/blob/master/doc/vimtex.txt#L5577-L5610),
such as [languagetool](https://languagetool.org/). I didn't check for
[grammalecte](https://grammalecte.net/) support for French yet, but it may
prove to be an [interesting
endeavour](https://upload.wikimedia.org/wikipedia/commons/9/9f/Rabbit_Hole.jpg).
**Remark.** vimtex
[claims](https://github.com/lervag/vimtex/blob/master/doc/vimtex.txt#L6549-L6624)
that their coloration is more accurate than what
[tree-sitter](https://tree-sitter.github.io/tree-sitter/), then if you are using
[nvim-treesitter](https://github.com/nvim-treesitter/nvim-treesitter), you may
want to disable it for vimtex (it raises a warning otherwise):
```lua
require("nvim-treesitter.configs").setup({
highlight = {
enable = true,
disable = { "latex", },
},
})
```
Okay, that's all and good, but to quote [wikipedia](https://en.wikipedia.org):
> The goal of the [language server] protocol is to allow programming language
> support to be implemented and distributed independently of any given editor or
> IDE. In the early 2020s LSP quickly became a "norm" for language intelligence
> tools providers.
Source: <https://en.wikipedia.org/wiki/Language_Server_Protocol>
We are not early 2020s-ready for LaTeX yet, and even if we can send our current
location to `zathura`, the contrary is not possible yet.
Let us now address these two issues.
# Language Server Protocol
Setting up language server protocol with Vim is a big morsel, and have been the
topic of [some tuppervim's
sessions](https://tuppervim.org/archives/pads/grenoble-2212.txt) at some point.
Here follows a minimal configuration that should work with `texlab`:
```lua
-- Minimal lsp config
local lspconfig = require("lspconfig")
lspconfig.texlab.setup {}
```
Easy, innit? Well, that's well and good, we can now see errors and warnings
decorating the file like a Christmas tree, but we can not use any of the LSP
tools such as obtaining information on a bibliography key, or rename a macro.
However, let us just remark that texlab is a pretty minimal LSP server, and
does not implement the myriads of possible functionalities.
Henceforth, I simply copy-pasted the default example from the [nvim-lspconfig
Readme](https://github.com/neovim/nvim-lspconfig), tried the shortcuts one by
one, and removed those which raised an error for “not implemented
functionality” 🤡:
```lua
-- Use LspAttach autocommand to only map the following keys
-- after the language server attaches to the current buffer
vim.api.nvim_create_autocmd("LspAttach", {
group = vim.api.nvim_create_augroup("UserLspConfig", {}),
callback = function(ev)
-- Enable completion triggered by <c-x><c-o>
vim.bo[ev.buf].omnifunc = "v:lua.vim.lsp.omnifunc"
-- Buffer local mappings.
-- See `:help vim.lsp.*` for documentation on any of the below functions
local opts = { buffer = ev.buf }
vim.keymap.set("n", "gd", vim.lsp.buf.definition, opts)
vim.keymap.set("n", "K", vim.lsp.buf.hover, opts)
vim.keymap.set("n", "gR", vim.lsp.buf.rename, opts)
vim.keymap.set("n", "gr", vim.lsp.buf.references, opts)
end,
})
```
Which thus enables:
* Omnicompletion using LSP (I won't elaborate on this point, either you use it
or not, but if you're using it, it may be useful to leave. I personally
don't).
* Go to a definition, with `gd`, which can be a macro, a reference, or even a
bibliography reference.
* Show the information about the element under the cursor using `K`, it can be
useful to quickly check a reference. Note that pressing `K` twice jumps into
the floating window. That can prove useful to copy an article title to search
for it somewhere else for instance.
* Rename a macro/variable among **all** files in the current working document
using `gR`.
It's a lifesaver when renaming macros as it avoids writing [regular
expressions](https://xkcd.com/1171/).
* Show each place where a reference appears with `gr` in a
[quickfix](https://vimhelp.org/quickfix.txt.html) window.
It allows checking where a formula is referenced or verifying if you cited
yourself enough.
I personally use
[telescope.nvim](https://github.com/nvim-telescope/telescope.nvim) for that
purpose as it is more readable, but it goes beyond the scope of this blogpost.
And that is about it.
We now follow the same steps as before: enable the completion engine by fetching
the configuration from the [nvim-cmp](https://github.com/hrsh7th/nvim-cmp)
readme file and the [vimtex
documentation](https://github.com/lervag/vimtex/blob/master/doc/vimtex.txt#L4586-L4625),
then prune it.
```lua
-- nvim-cmp
local cmp = require("cmp")
cmp.setup({
sources = cmp.config.sources({
{ name = "nvim_lsp" },
{ name = "buffer" },
}),
mapping = cmp.mapping.preset.insert({
["<C-Space>"] = cmp.mapping.complete(),
["<C-u>"] = cmp.mapping.scroll_docs(-4),
["<C-d>"] = cmp.mapping.scroll_docs(4),
["<C-l>"] = cmp.mapping.confirm({ select = true }),
}),
})
```
And we're all good from Neovim's side. You can of course start fine-tuning it
but it's not the purpose of this blogpost.
# Plug it into zathura
Now that you tweaked your Neovim configuration so much that it now consumes 10GB
of memory and takes 12s to launch using all your 24 CPU cores, we can move onto
configuring zathura.
One of the reasons I moved from
[vim-latex](https://github.com/vim-latex/vim-latex) to
[vimtex](https://github.com/lervag/vimtex) is reverse search: to enable it with
`vim-latex`, I was using [nvim-remote](https://github.com/mhinz/neovim-remote)
which is a wrapper for `nvim --listen` with a lot of constraints, while the most
annoying one is that if I used reverse search from a detached[^1] zathura window
without starting `nvr` first… then it spawns the process which I cannot recover.
Which usually happens when I'm in a rush to fix something quickly.
Fortunately, this is a thing of the past as it is possible to directly send a
directive to `vimtex` upon which it will look for the corresponding buffer and
open the file at the corresponding location while following its state
(which can be viewed with `,li`).
To do so, the
[documentation](https://github.com/lervag/vimtex/blob/master/doc/vimtex.txt#L5985-L6033)
states that you have to launch the following command, where `%l` is the line in
the file and `%f` is the name of the file:
```bash
nvim --headless -c "VimtexInverseSearch %l '%f'"
```
That's all and good, we just have to tell Zathura which command to launch when
doing backward search, which by default is done with `Ctrl` + `left mouse
button` on the portion of the text you want to view in the code.
To do that, the following configuration that you can put in
`$HOME/.config/zathura/zathurarc` should do the trick:
```
set synctex true
set synctex-editor-command "nvim --headless -c \"VimtexInverseSearch %{line} '%{input}'\""
```
And… that's it! You can now go to the location you want in your source file,
compile it on the fly and scrutinise the warnings to look for overfull hboxes!
[^1]: Meaning that it is not owned by any terminal I have opened, I
can otherwise still recover it somehow.
# Conclusion
In this blogpost, we saw how to minimally set up Neovim to work with latex using
modern toolchains. You can use it as a base to then improve your workflow and
write your documents in a breeze with neovim.
To summarise the configuration we used, it can be done in an `init.lua` file in
your vim configuration directory:
```lua
-- Lazy Package Manager
local lazypath = vim.fn.stdpath("data") .. "/lazy/lazy.nvim"
if not vim.loop.fs_stat(lazypath) then
vim.fn.system({
"git",
"clone",
"--filter=blob:none",
"https://github.com/folke/lazy.nvim.git",
"--branch=stable", -- latest stable release
lazypath,
})
end
vim.opt.rtp:prepend(lazypath)
require("lazy").setup({
"lervag/vimtex",
"neovim/nvim-lspconfig",
"hrsh7th/cmp-nvim-lsp",
"hrsh7th/nvim-cmp",
})
-- vimtex
vim.g.vimtex_view_method = "zathura"
vim.g.maplocalleader = ","
vim.o.foldmethod = "expr"
vim.o.foldexpr="vimtex#fold#level(v:lnum)"
vim.o.foldtext="vimtex#fold#text()"
vim.o.foldlevel=2
-- Minimal lsp config
local lspconfig = require("lspconfig")
lspconfig.texlab.setup {}
-- Use LspAttach autocommand to only map the following keys
-- after the language server attaches to the current buffer
vim.api.nvim_create_autocmd("LspAttach", {
group = vim.api.nvim_create_augroup("UserLspConfig", {}),
callback = function(ev)
-- Enable completion triggered by <c-x><c-o>
vim.bo[ev.buf].omnifunc = "v:lua.vim.lsp.omnifunc"
-- Buffer local mappings.
-- See `:help vim.lsp.*` for documentation on any of the below functions
local opts = { buffer = ev.buf }
vim.keymap.set("n", "gd", vim.lsp.buf.definition, opts)
vim.keymap.set("n", "K", vim.lsp.buf.hover, opts)
vim.keymap.set("n", "gR", vim.lsp.buf.rename, opts)
vim.keymap.set("n", "gr", vim.lsp.buf.references, opts)
end,
})
-- nvim-cmp
local cmp = require("cmp")
cmp.setup({
sources = cmp.config.sources({
{ name = "buffer" },
{ name = "nvim_lsp" },
}),
mapping = cmp.mapping.preset.insert({
["<C-Space>"] = cmp.mapping.complete(),
["<C-u>"] = cmp.mapping.scroll_docs(-4),
["<C-d>"] = cmp.mapping.scroll_docs(4),
["<C-l>"] = cmp.mapping.confirm({ select = true }),
}),
})
```
and the following in your `zathurarc` file:
```
set synctex true
set synctex-editor-command "nvim --headless -c \"VimtexInverseSearch %{line} '%{input}'\""
```
Note that due to some technical limitations, it's not fully perfect.
For instance, synctex is not fully accurate with beamer slides, and just select
the whole slide instead of the selected text. It is still better than nothing
in my opinion, and it's a drawback that every LaTeX IDE is subject to.
Now that everything is set up, you can skim the [vimtex
documentation](https://github.com/lervag/vimtex/blob/master/doc/vimtex.txt) to
look for things you want to activate and learn more about its features.
Keep in mind though that you should not be too greedy, just pick some habits one
at a time in order to ingrain them into your workflow.
You can also expand directly `Neovim` with
[snippets](https://github.com/L3MON4D3/LuaSnip) support for instance to automate
some tasks as LaTeX can be quite verbose from time to time.
I leave you now with some further reading about the topic.
## See Also
* jdhao. [A Complete Guide on Writing LaTeX with Vimtex in
Neovim](https://jdhao.github.io/2019/03/26/nvim_latex_write_preview/). June 2019.
A blogpost that serves the same purpose as this one, eventhough it's not fully
up to date, for instance regarding backward search.
* Gilles Castel. [How I'm able to take notes in mathematics lectures using LaTeX
and Vim](https://castel.dev/post/lecture-notes-1/).
An example of how to streamline writing maths with Neovim, vimtex and
snippets. The goal may not be for everyone (as writing new maths and following
a lecture are not one and the same), but it's still an interesting read. For
instance it presents the `concealment` feature of [vim](https://www.vim.org/)
that makes previewing the result easier.
I also recommend the rest of the blog, as it contains information about
[inkscape](https://inkscape.org/) and how to integrate it with LaTeX.
# Bonus: Key bindings for bépo users
As a [bépo](https://fr.wikipedia.org/wiki/B%C3%A9po) user, I have some remapping
done in Neovim, and especially [direction
keys](https://vimhelp.org/usr_02.txt.html#02.3):
```lua
-- Some shortcuts
local keymap = vim.keymap.set
local opts = {noremap = true, silent = true}
-- [HJKL] <-> {CTSR}
local map_list = {
['c'] = 'h', ['r'] = 'l', ['t'] = 'j', ['s'] = 'k', ['C'] = 'H', ['R'] = 'L', ['T'] = 'J', ['S'] = 'K', -- [HJKL] -> [CTSR]
['j'] = 't', ['J'] = 'T', ['l'] = 'c', ['L'] = 'C', ['h'] = 'r', ['H'] = 'R', ['k'] = 's', ['K'] = 'S', -- [CTSR] -> [HJKL]: J = until, L = change, h = replace, k = substitute
}
for key, binding in pairs(map_list) do
keymap({'n', 'x'}, key, binding, opts)
end
```
That's nice and all but… it conflicts with the [vimtex default
mappings](https://github.com/lervag/vimtex/blob/master/doc/vimtex.txt#L800-L912)
such as `cse` to rename an environment which can be useful to replace an `align`
with `align*` for instance. Meaning that going back one character would trigger
vim to wait for the next key input, which is kind of annoying.
Hence the need to remap the vimtex default shortcuts starting with `c`, `t`, `s`
or `r`.
Fortunately, it's only the case for `c` and `t`. I first just add the remapping
to `$NVIMDIR/after/ftplugin/tex.lua`, however I soon noticed that it's not
sufficient as vimtex is also used for `.tikz`, `.cls` and `.bib` files,[^2] thus we
will use
[autocommand](https://neovim.io/doc/user/lua-guide.html#lua-guide-autocommands)
for that:
```lua
-- Some BÉPO mappings for vimtex
vim.api.nvim_create_autocmd({"BufEnter", "BufWinEnter"}, {
pattern = {"*.tex", "*.bib", "*.cls", "*.tikz",},
group = vim.api.nvim_create_augroup("latex", { clear = true }),
callback = function()
local vimtex_remaps = {
-- c <-> t
{ mode = "n", source = "csd", target = "tsd", command = "<Plug>(vimtex-delim-change-math)"},
{ mode = "n", source = "csc", target = "lsc", command = "<Plug>(vimtex-cmd-change)"},
{ mode = "n", source = "cse", target = "lse", command = "<Plug>(vimtex-env-change)"},
{ mode = "n", source = "cs$", target = "ls$", command = "<Plug>(vimtex-env-change-math))"},
-- t <-> j
{ mode = {"x", "n"}, source = "tsD", target = "jsD", command = "<Plug>(vimtex-delim-toggle-modifier-reverse)"},
{ mode = {"x", "n"}, source = "tsd", target = "jsd", command = "<Plug>(vimtex-delim-toggle-modifier)"},
{ mode = {"x", "n"}, source = "tsf", target = "jsf", command = "<Plug>(vimtex-cmd-toggle-frac)"},
{ mode = "n", source = "tsc", target = "jsc", command = "<Plug>(vimtex-cmd-toggle-star)"},
{ mode = "n", source = "ts$", target = "js$", command = "<Plug>(vimtex-env-toggle-math)"},
{ mode = "n", source = "tse", target = "jse", command = "<Plug>(vimtex-env-toggle-star)"},
}
for _,remap in pairs(vimtex_remaps) do
if vim.fn.maparg(remap.source) ~= "" then
vim.keymap.del(remap.mode, remap.source, { buffer = true })
vim.keymap.set(remap.mode, remap.target, remap.command, { silent = true, noremap = true, buffer = true})
end
end
end,
})
```
The sanity check with
[`maparg(·)`](https://vimhelp.org/builtin.txt.html#maparg%28%29) is done to
avoid unmapping a mapping that already doesn't exist, which will raise an error
(as I have the (bad?) habit to type `:e` to reload the current file when
thinking, that what triggered this behaviour in my case).
**Remark.** Please also note that it is not fully remapped yet, for instance in the table of
content navigation there are still collisions, as `t` for instance toggles
showing TODOs or `s` toggles the section numbering.
To finish and for the sake of completeness, here follows the bépo-bindings for
zathura, to put in your `zathurarc` file:
```text
## BEPO
# hjkl → ctsr
map t scroll down
map s scroll up
map c scroll left
map r scroll right
# JK → TS
map T navigate next
map S navigate previous
# r → p
map p rotate rotate-cw
# R → u
map u reload
# Mode Index
map [index] t navigate_index down
map [index] s navigate_index up
map [index] r navigate_index expand
map [index] c navigate_index collapse
map [index] R navigate_index expand-all
map [index] C navigate_index collapse-all
```
[^2]: Actually `.cls` and `.tikz` are detected as tex files, so the `ftplugin`
approach works but `.bib` is detected as a bibtex file and enjoys its own
filetype.

View File

@ -1,11 +1,11 @@
---
Title: Gérer vos mots de passe avec pass
Date: 2019-04-22 19:00
Modified: 2019-04-24 11:12
Modified: 2024-02-24 18:00
Lang: fr
Author: Fabrice
Category: programmes
Tags: pass, git
Tags: pass, git, cli
Slug: password-store
Header_Cover: ../images/covers/clovers.jpg
Summary: Un gestionnaire de mots de passe simple qui repose sur gpg, et synchronisé via git.
@ -14,12 +14,12 @@ Summary: Un gestionnaire de mots de passe simple qui repose sur gpg, et synchron
Comme nous vivons dans un monde dangereux où les failles de sécurité sont découvertes tous les jours et où les fuites de données sont devenues monnaies courantes, il est recommandé dutiliser un mot de passe différent pour chacune de nos identités numériques.
Cependant, cela devient vite une horreur à maintenir manuellement, javais essayé dutiliser un bloc note en 2003 que jai perdu au bout dun mois (et les mots de passe avec).
Heureusement, les gestionnaires de mots de passes ont fleurit depuis, et plusieurs proposent des fonctionnalités de base similaires: multiplateforme (en particulier sur les téléphones intelligents), génération de mots de passe «sécurisés», intégration navigateur…
Heureusement, les gestionnaires de mots de passes ont fleuri depuis, et plusieurs proposent des fonctionnalités de base similaires: mufti plateforme (en particulier sur les téléphones intelligents), génération de mots de passe «sécurisés», intégration navigateur…
Je ne vais pas pour faire une comparaison détaillée, mais si vous souhaitez jeter un coup dœil, [wikipedia](https://en.wikipedia.org) propose une table de comparaison détaillée [**par ici** (en)](https://en.wikipedia.org/wiki/List_of_password_managers).
Je ne vais pas pour faire une comparaison détaillée, mais si vous souhaitez jeter un coup dœil, [Wikipédia](https://en.wikipedia.org) propose une table de comparaison détaillée [**par ici** (en)](https://en.wikipedia.org/wiki/List_of_password_managers).
Dans cette recherche, grâce à [moviuro](https://try.popho.be), mon choix sest porté sur [pass](https://www.passwordstore.org/) avec [pass-otp](https://github.com/tadfisher/pass-otp#readme) (et [passmenu](https://git.zx2c4.com/password-store/tree/contrib/dmenu)).
Je nai pas non plus la prétention de faire un guide détaillé sur pass, comme ceux-ci sont déjà faciles à trouver sur internet, par exemple [**par ici** (en)](https://medium.com/@chasinglogic/the-definitive-guide-to-password-store-c337a8f023a1).
Je nai pas non plus la prétention de faire un guide détaillé sur pass, comme ceux-ci sont déjà faciles à trouver sur internet, par exemple [**par ici** (en)](https://medium.com/@chasinglogic/the-definitive-guide-to-password-store-c337a8f023a1).
En résumé, pass est un script bash qui fait appel à des outils de base comme [git](https://git-scm.com/), [gpg](https://www.gnupg.org/), et est écrit par [zx2c4](https://www.zx2c4.com/).
@ -32,6 +32,34 @@ Pour **re**générer un mot de passe, loption `-i` ici est **importante** pui
Cela évite donc de devoir utiliser du [git sale]({filename}/cheat-sheets/git-fr.md) pour retirer lerreur de larbre des commits vu que `pass <cmd>` fait automatiquement un commit atomique à la fin de la commande.
Je loublie parfois, cest pourquoi je laisse ça ici en guise de rappel.
Sur certains services, il arrive que lon ait besoin de spécifier un ensemble de caractères admissibles pour les mots de passe.
Cela peut être fait via la variable denvironnement `PASSWORD_STORE_CHARACTER_SET`.
Le contenu de cette variable est transmis à la [commande `tr`](https://fr.wikipedia.org/wiki/Tr_(Unix)).
Ainsi, pour créer un code PIN, on peut spécifier la valeur suivante: `PASSWORD_STORE_CHARACTER_SET='[:digit:]'` et indiquer la longueur désirée du mot de passe en dernier argument de la commande.
Par exemple, pour générer un code PIN de 6 chiffres:
```sh
PASSWORD_STORE_CHARACTER_SET='[:digit:]' pass generate <pass-name> 6
```
Je nai en revanche pas trouvé comment forcer la présence de caractères spéciaux… je lance donc la commande plusieurs fois dans ces cas avec loption `-i` pour écrire en place après la première tentative.
Ce nest pas la meilleure solution, qui en plus pollue lhistorique git mais bon… ça fonctionne.
Par exemple, pour générer un mot de passe sur un service _fictif_ qui supporterait les caractères spéciaux suivants: `-_@$<>` pour des mots de passe dau plus 20 caractères, on peut utiliser la commande suivante:
```sh
PASSWORD_STORE_CHARACTER_SET='[:alnum:]-_@$<>' pass generate <pass-name> 20
```
Si on souhaite faire de la rotation de clefs ou simplement mettre à jour ses identités, il est possible de relancer la commande `pass init`, où on peut y spécifier lidentifiant de la nouvelle clef… ou plusieurs identifiants.
Cela peut être utile pour avoir plusieurs clefs sur des périphériques différents pour mieux contrôler les risques de fuite de clef.
On peut également avoir des sous dossiers de son dossier `$HOME/.password-store/` chiffrés sous des clefs spécifiques (cela se contrôle avec loption `-p/--path=` de `pass init`).
Une application possible étant de séparer les clefs personnelles des clefs professionnelles (qui peuvent alors être chiffré sous une clef partagée entre les personnes ayant accès au jeu de mots-de-passe).
On pourrait imaginer coupler ça avec les [sous-modules git](https://git-scm.com/book/en/v2/Git-Tools-Submodules), mais je ne my suis pas aventuré…
Pour finir:
```sh
pass git <ce que vous voulez>
```

View File

@ -1,10 +1,10 @@
---
Title: Manage your passwords with pass
Date: 2019-04-22 19:00
Modified: 2019-04-23 14:24
Modified: 2024-02-24 18:00
Author: Fabrice
Category: software
Tags: pass, git
Tags: pass, git, cli
Slug: password-store
Header_Cover: images/covers/clovers.jpg
Summary: A simple password manager that relies on gpg, and synchronized with git.
@ -14,9 +14,9 @@ Lang: en
As security breaches are discovered regularly, and so leakage happens, it is recommended to have a different password on each account.
However, this task is obviously a pain to maintain by hand. I did use a notebook back in 2003, which I lost within a month, given that I'm a very organized person.
Hopefully, many password managers exist, with similar features: cross-platform (especially smartphone support), password generation,browser integration…
Hopefully, many password managers exist, with similar features: cross-platform (especially smartphone support), password generation, browser integration…
I'm not here to compare them, if you want to give a look, [wikipedia](https://en.wikipedia.org) provides a nice comparison table [**there**](https://en.wikipedia.org/wiki/List_of_password_managers).
I'm not here to compare them, if you want to give a look, [Wikipedia](https://en.wikipedia.org) provides a nice comparison table [**there**](https://en.wikipedia.org/wiki/List_of_password_managers).
However, thanks to [moviuro](https://try.popho.be), my choice is [pass](https://www.passwordstore.org/) along with [pass-otp](https://github.com/tadfisher/pass-otp#readme) (and [passmenu](https://git.zx2c4.com/password-store/tree/contrib/dmenu)).
I don't intend either to make a comprehensive guide, as those already populate the internet, for example [**here**](https://medium.com/@chasinglogic/the-definitive-guide-to-password-store-c337a8f023a1).
@ -28,7 +28,32 @@ Here are just some commands I often use.
```sh
pass generate -i <pass-name>
```
To regenerate a password, the `-i` is important to avoid overwritting the whole file and having to rely on [dirty git]({filename}/cheat-sheets/git.md) to withdraw your mistake (`pass <cmd>` will automatically commit your change)… I sometimes forget it, so let's put it here as a reminder.
To regenerate a password, the `-i` is important to avoid overwriting the whole file and having to rely on [dirty git]({filename}/cheat-sheets/git.md) to withdraw your mistake (`pass <cmd>` will automatically commit your change)… I sometimes forget it, so let's put it here as a reminder.
Sometimes it can be useful to specify the accepted special chars, this can be done using the `PASSWORD_STORE_CHARACTER_SET` environment variable.
This value is interpreted by the [`tr` command](https://en.wikipedia.org/wiki/Tr_(Unix)),
hence to create a PIN, you can use the following value: `PASSWORD_STORE_CHARACTER_SET='[:digit:]'`, then specify the length with the last argument.
For instance, to generate a 6 digit PIN:
```sh
PASSWORD_STORE_CHARACTER_SET='[:digit:]' pass generate <pass-name> 6
```
I didnt manage to specify how to have at least one of them, so I run the command multiple times (with the `-i` option to change the file in place after the first one)…
It pollutes a bit the git history but, well… it works.
For instance, for a service supporting only the following characters: `-_@$<>` of at most 20 char long (fictive example), you can use the following command:
```sh
PASSWORD_STORE_CHARACTER_SET='[:alnum:]-_@$<>' pass generate <pass-name> 20
```
If for some reasons you want to rotate your keys, you can rerun the `pass init` command by indicating the new gpg ID (or multiple keys to have it available under multiple devices that dont share the same key to limit the risks of key leakage).
Note that you can also have a subfolder encrypted under a specific key (it can be specified using the `-p/--path=` option for `pass init`) if you want to share it to some other devices, or to separate work from personal passwords.
It should be possible to use [`git submodule`](https://git-scm.com/book/en/v2/Git-Tools-Submodules) as well, but I didnt try.
To finish:
```sh
pass git <whatever you want>

View File

@ -1,6 +1,7 @@
---
Title: Use termtosvg to screencast your terminal
Date: 2019-04-24 17:01+5:30
Modified: 2023-07-10 12:00
Author: Fabrice
Category: software
Tags: termtosvg
@ -15,7 +16,7 @@ Therefore, it is common to just paste those term information when it comes to sh
However, once the Pandora box of terminals is opened, yet another world reveals itself: [ncurse](https://www.gnu.org/software/ncurses/).
It is common to represent static elements with it using a screenshot, however as I already complained about it in [another post]({filename}/tips/latex-adblock.md).
It is not a fully satisfactory solution as we also loose all the text information, and it is thus impossible to copy a part of the screen content without typing it again (or [OCR](https://www.gnu.org/software/ncurses/)ise the screenshot?).
It is not a fully satisfactory solution as we also loose all the text information, and it is thus impossible to copy a part of the screen content without typing it again (or [OCR](https://en.wikipedia.org/wiki/Optical_character_recognition)ise the screenshot?).
[Nicolas Bedos](https://github.com/nbedos) comes with a solution for this named [termtosvg](https://nbedos.github.io/termtosvg/).
This small python program is available on `pip` as well as [github](https://nbedos.github.io/termtosvg/).

252
content/software/typst.md Normal file
View File

@ -0,0 +1,252 @@
---
Title: Typesetting with Typst
Date: 2024-10-19 18:00
Modified: 2025-02-14 12:45
Lang: en
Author: Fabrice
Category: software
Tags: vim, neovim, typst
Slug: typst-intro
table-of-contents: true
Summary: An overview of Typst for simple usage.
Header_Cover: ../images/covers/printing-press.jpg
---
For about ten years now, Ive been using _LaTeX_ to typeset any document which
aims to be printed. The main reason is that Im quite familiar with the syntax
to adjust the page setting to what I imagine. Other solutions exist to ease this
workflow, such as [Pandoc](https://pandoc.org/) to convert easier to write syntax to
other formats. However, this solution was not palatable to me as the universal
aspect of Pandoc, makes it a pain to specify pagination properties. And, to
produce a PDF in the end, Pandoc calls _LaTeX_, and for the sake of simplicity I
prefer to directly write in _LaTeX_ (see the conclusion for clarification).
However, for some kind of documents that I dont write very often, such as
letters, its sometimes a pain to find the syntax of the [LaTeX class for
letters](https://texlive.mycozy.space/macros/latex/contrib/lettre/lettre.pdf)<sup>(fr)</sup>
I use. Recently, I had to write small documents (approx 3 pages), with a
bibliography, and that just needed to have a nice and neat presentation. Before
going farther, I decided to give a try to [typst](https://typst.app/). It is an
open-source typesetting engine that is written in
[rust](https://rust-lang.org/), that promotes modern features such as
_incremental compilation_, and a scripting language that is actually readable by
humans. Moreover, the markup language is clean, and as in
[markdown] or [asciidoc], it can be read from the source (without a
[concealer](https://alok.github.io/2018/04/26/using-vim-s-conceal-to-make-languages-more-tolerable/))
to have an idea of the typeset text before compiling it.
The project is still in development and some features may break before the first
major version is out. However, to quickly generate a document, it can be a good
idea to consider. Also, please note that there is no native output from Typst to
HTML. However, Pandoc can read and write `.typ` files, so its an alternative
worth considering given that native LaTeX is **not** easy to write for that
purpose.
In the following, it will be a quick overview of the features that I find nice
in Typst. Please dont expect an in-depth review, I only used it for a couple of
opportunities.
# Syntax
To illustrate Typst, here follows a simple document, with its corresponding [compiled
version]({static}/examples/typst-example.pdf).
```typst
/* Set some variables for the authors and the title */
#let title = [How to type in Typst]
#let authors = ("Fabrice Mouhartem", )
/* Set some document properties:
* - Numbering of sections (default: none)
* - PDF properties (it does not print anything) */
#set heading(numbering: "I.")
#set document(title: title, author: authors)
/* Now we can print the title and the authors */
#align(center, text(size: 17pt, strong(title)))
#for author in authors { // TODO: would be better in a grid
align(center, text(author))
}
/* Table of contents */
#outline()
/* To define a section */
= Introduction
/* And to write the content: */
To _type_ in Typst, you just have to… *type*.
= Conclusion
See, it was #strike[hard] easy.
```
Besides the programmatic part at the start, we can see that the syntax is close
to what we may know in other simple markup languages such as [markdown] or
[asciidoc]. However, it is its own and Typst [doesnt
plan](https://github.com/typst/typst/discussions/2498) to converge toward
another markup language, especially knowing that the markdown syntax is
[ambiguous](https://roopc.net/posts/2014/markdown-cfg/).
For the preamble of the file, it can be deferred to another file to accentuate
the separation between content and typesetting. We wont go into details, but it
was mostly there to illustrate some parts of the language, such as variable
declaration (`#let …`) or loops over arrays (`#for … in …`). We also set some
values about the document using either static values (for the numbering) or the
defined variables (authors and title options).
Let us now check that these options are indeed well set:
```bash
pdfinfo "typst-example.pdf" | head -n 3
Title: How to type in Typst
Author: Fabrice Mouhartem
Creator: Typst 0.12.0
```
The generated document is a bit bare, but we can easily change it, and as easily
create a template to automate this work for us.
# Example: a letter (the French way)
For a simple example, Ill take the letter one, for which I wrote a simple
template to generate them. The template is available in [this
repository](https://git.epheme.re/fmouhart/typst-lettre). The main issue I had
is that the French way of doing letters revert the order of the addresses of the
sender and recipient are… reverted compared to the English style.
Let us dig in what it does by looking at the example file.
First thing first, the file is loading the template from `lettre.typ`. This file
defines the typesetting and the paginations properties, and provide an
initialisation function to define the different headers, which you can look into
if you feel curious. If you find it a bit unbearable, you can create a new file
`preamble.typ` to hide everything, and include it with `#include "preamble.typ"`
at the outset of the file.
```typst
#import "lettre.typ": *
#show: doc => lettre.with(
de: [
Sender\
Address
],
pour: [
Recipient\
Address
],
objet: "subject of the letter", // optional
date: "date of sending", // optional
lieu: "location",
introduction: "opening",
cloture: "closing",
signature: "signature",
post: [
post-letter (e.g., post-scriptum)
],
)
```
This will generate the template with the elements where they should be, in a
very simple fashion. Now, we just have to write the content of the letter and
have a `pdf` file pretty easily with `typst compile` without all the files that
_LaTeX_ generates for us. As a bonus, it also set some PDFs metadata from the
input variables.
# Bibliography
Another nice feature is that Typst embeds a bibliography engine, that is
compatible with the bibtex format. Meaning that I dont have to change the way I
manage my bibliography or directly use [community-managed
bibliography](https://cryptobib.di.ens.fr/).
For that you simply have to call the bibliography function where you want it to be generated:
```typst
#bibliography("bibliography.bib", title: "References", style: "springer-basic")
```
And cite the references you want with the `@bibkey` syntax (note that its the
same syntax for cross-referencing) or the `#cite` function. Note that if you
have a `+` in your bibliography key, the label wont be recognized. It is my
case as my bibkeys are in the _alpha_ style with the initials of the authors +
year up to 3 names, and a `+` afterward… To be able to cite the reference
anyway, just do `#cite(label("bib+key"))`.
The full list of natively available style is given in the [Typst
documentation](https://typst.app/docs/reference/model/bibliography/#parameters-style),
however you can define your own in the [Citation Style
Language](https://citationstyles.org/). Meaning that you can pick the best one
for your usage in the [CSL
repository](https://github.com/citation-style-language/styles).
To put multiple citations (as in `\cite{ref1, ref2}` in _LaTeX_), you just have
to concatenate them, like `@ref1 @ref2`. If the citation style supports it, it would automatically
merge them under the same bracket (however, its not the case for all citation
styles).
# Tooling
Unlike my [blogpost about using nvim for LaTeX]({filename}./nvim-latex.md),
its pretty much easier here.
I just added the [`typst.vim`](https://github.com/kaarmu/typst.vim) plugin to
_neovim_, added a binding for the `:TypstWatch` command and thats basically
all. It is possible to set the `g:typst_pdf_viewer` (`vim.g.typst_pdf_viewer` in
Lua) global variable as well if you want to have a different default PDF viewer
for your system and for edition.
Now, each time I save, the document is incrementally compiled (I think) and
updated in my PDF viewer.
I tried using typst language servers as well, but they dont seem mature enough
yet. In the end, the `typst.vim` plugin suffices to my needs without being too
nosy.
Hopefully, the tooling will get better with time.
# Conclusion
For simple document typesetting, even with features such as bibliography,
cross-referencing, and table of contents, Typst seems to be a good choice.
However, for long-term support documents, such as documentation, I would wait to
see it being more stable. Even while writing this blog post, the _lettre_
template I made was done in Typst 0.11, and preparing it in a repository raised
some warnings about [breaking changes from
0.12](https://typst.app/docs/changelog/0.12.0/) which has been published…
[yesterday](https://typst.app/blog/2024/typst-0.12/) (at the time of writing):
> Added
> [`par.spacing`](https://typst.app/docs/reference/model/par/#parameters-spacing
> "Typst Documentation: paragraph spacing") property for configuring paragraph
> spacing. This should now be used instead of `show par: set block(spacing: ..)`
> **(Breaking change)**
Which also leads me to say that one other advantage of Typst is… the readability of
error messages. They are way much cleaner than the ones from _LaTeX_. Leading to
easier debugging as well. I, however, cant say for sure as I didnt typeset big
documents using Typst yet (which is unlikely to happen given my stance so far,
but who knows…).
Finally, to complete the note about compiling in _LaTeX_ from Pandoc in the
introduction of this blogpost… Im apparently not up to date. For the sake of
transparency, it is possible to use different PDF engines with Pandoc according
to the [documentation](https://pandoc.org/MANUAL.html#option--pdf-engine). I
didnt try them yet, so I cant provide feedbacks about them. Note that Typst is
part of the PDF generators, as well as other nice typesetting projects such as
[pagedjs](https://pagedjs.org/) that aims at paginating HTML sources to have
great web page and print-ready PDFs. However, the argument about avoiding
intermediate steps if possible still holds (moreover when the syntax of Typst is
already clean enough).
## See Also
- [Awesome Typst](https://github.com/qjcg/awesome-typst): a repository listing
useful links for typst users.
- [Typst for _LaTeX_ users](https://typst.app/docs/guides/guide-for-latex-users/
"Typst documentation: guide for LaTeX users"): a comprehensive guide for typst
for _LaTeX_ users.
[markdown]: https://daringfireball.net/projects/markdown/
[asciidoc]: https://asciidoc.org/

97
content/software/xidel.md Normal file
View File

@ -0,0 +1,97 @@
---
Title: Manipulate XML/HTML with Xidel
Date: 2023-10-29 22:00
Lang: en
Author: Fabrice
Category: software
Tags: xidel, html, xml, cli
Slug: xidel
table-of-contents: false
Header_Cover: ../images/covers/aix-en-provence.jpg
Summary: An example-based approach on how to easily parse XML/HTML files and stubs with Xidel.
---
You may know [jq](https://jqlang.github.io/jq/) process
[json](https://www.json.org/json-en.html) files in command line. At some point I
was looking for the simplicity of such a swiss-knife tool for
[XML](https://www.w3.org/XML/)/[HTML](https://html.spec.whatwg.org/multipage/),
mostly for simple usages that don't require me to resort to a full-fledged
scripting language such as [python](https://python.org) or dabbing in [regular
expressions](https://en.wikipedia.org/wiki/Regular_expression) that will never
work because of a carriage return at an unexpected place, and guess what? It exists!
This tool is [Xidel](https://www.videlibri.de/xidel.html). It is a bit more than
that as it also allows downloading files, which enables extra features such as
navigating a site following specific links. You can find more about it in the
[list of examples](https://www.videlibri.de/xidel.html#examples) given in the
project website, which is a nice introduction to the possibilities of the tool.
However, I mainly use it for simple cases, where I mix-and-match the best of
both worlds: a graphical client (such as
[firefox](https://www.mozilla.org/en-US/firefox/new/)), and a CLI tool, which in
this case is Xidel.
To do this, we will see a simple use case, where filtering by hand can be a bit
tedious. Let us assume that we want to obtain the URL list of pdf versions of
Victor Hugo's novels in French from Wikisource if available.
We start from this page: <https://fr.wikisource.org/wiki/Auteur:Victor_Hugo>,
that lists which is available on <https://fr.wikisource.org>.
Now, we can simply select the “Romans” section as it is and copy it. Normally
you can check that you indeed have the html in your clipboard by typing
`wl-paste -t text/html` on wayland or `xclip -selection clipboard -o -t
text/html` on X11 if you have xclip installed. In the following we will assume a
Wayland environment with
[wl-clipboard](https://github.com/bugaevc/wl-clipboard), but it should also work
with `xclip` (not tested, please let me know how it behaves).
Now that's good, but we now need to filter and parse it, we can start with a
simple test:
```bash
wl-paste -t text/html | xidel -e '//a/@href'
```
Which will show us the target of each links in our selection. To explain the
syntax, the option `-e` tells `xidel` to extract the content that is passed as
input, which is either a
[template](https://benibela.de/documentation/internettools/extendedhtmlparser.THtmlTemplateParser.html)
or following the [XPath](https://en.wikipedia.org/wiki/XPath) syntax to parse
the [DOM](https://en.wikipedia.org/wiki/Document_Object_Model) tree. In the
above example we used the latter, to obtain every anchors (`//a`) and then their
`href` attribute with `@href`.
From there we can see that pdf versions contains the string… “pdf”.
Now, we can see another nice part of XPath, is that we can filter using
functions:
```bash
wl-paste -t text/html | xidel -e '//a/@href[contains(., "pdf")]'
```
The last magical part here, is the dot notation, which refers to the current
item “value”. Im not the most familiar with the subtleties here, and you can
refer to this stackoverflow [short answer](https://stackoverflow.com/a/38240971)
or long answer just above for more details.
You can also edit the way the filtering is done, for instance if the anchors you
are targeting are named “Download”, you can obtain the links with:
```bash
wl-paste -t text/html | xidel -e '//a[contains(., "Download")]/@href'
```
If you want strict equality because there are “Download PDF” and “Download epub”
links for instance:
```bash
wl-paste -t text/html | xidel -e '//a[text()="Download PDF"]/@href'
```
To go further, you can also pass HTTP headers and cookies to `xidel` via the
`--header/-H` and `--load-cookies` options respectively. It is also possible to
use the `--follow/-f` command to hop in the pages that matches (using the same
syntax as above) to obtain a link from it… or event directly download it with
the `--download` option and so on.
In this blogpost we only look at a local version of pre-filtered content using
you web browser, but the possibilities are endless!

View File

@ -1,7 +1,7 @@
---
Title: Écrire son courriel en markdown avec vim
Date: 2019-04-22 19:00
Modified: 2019-04-24 09:35
Modified: 2023-10-14 19:00
Author: Fabrice
Category: astuces
Tags: emails, pandoc, Vim
@ -24,7 +24,11 @@ pandoc -t html5 -s <fichier> | xclip -selection clipboard
Ainsi, en ajoutant ce raccourci dans votre fichier de configuration pour les markdown dans vim (pour ma part il est dans `ftplugin/pandoc.vim`):
```vim
map <raccourcis> :w !pandoc -t html5 -s \| xclip -selection clipboard<cr>
map <raccourci> :w !pandoc -t html5 -s \| xclip -selection clipboard<cr>
```
ou en version plus moderne (lua + wayland):
```lua
vim.keymap.set('n', <raccourci>, ':w !pandoc -t html5 -s | wl-copy<CR><CR>', { noremap = true, silent = true })
```
on peut alors directement coller le contenu du *buffer* courant formatée par [pandoc](https://pandoc.org/) dans thunderbird.
Bien entendu, vous pouvez décorer cette commande à laide de vos options favorites.

View File

@ -1,7 +1,7 @@
---
Title: Write your emails in markdown with vim
Date: 2019-04-22 19:00
Modified: 2019-04-24 09:35
Modified: 2023-10-14 19:00
Author: Fabrice
Category: tips
Tags: emails, pandoc, Vim
@ -26,6 +26,10 @@ Therefore, adding:
```vim
map <your map> :w !pandoc -t html5 -s \| xclip -selection clipboard<cr>
```
or a more modern version using `lua` and `wayland`:
```lua
vim.keymap.set('n', <your map>, ':w !pandoc -t html5 -s | wl-copy<CR><CR>', { noremap = true, silent = true })
```
in your vim `ftplugin/pandoc.vim` configuration file allows you to copy directly the output of [pandoc](https://pandoc.org/) on you opened buffer into your clipboard and thus past it directly into thunderbird.
Of course, you can customize this command line as you want. For instance my base-header-level is 4, as I think that first-level titles are a bit too much for emails.
You can even use some simple [css rules](https://perfectmotherfuckingwebsite.com/) in a separated style sheet along with the `--self-contained` option of pandoc to be able to do basic general formating (for a newsletter for instance).

218
content/tips/presenting.md Normal file
View File

@ -0,0 +1,218 @@
---
Title: Tools for making and giving presentations
Date: 2024-11-03
Author: Fabrice
Category: Tips
Tags: presentation, vim, latex
Slug: presenting
Header_Cover: ../images/covers/pts24-talk.jpg
Summary: Some of the tools I use for making and giving presentations.
lang: en
---
# Introduction
Over the past year, I have to give quite a few presentations in different
contexts: internal to the company, for open-source conferences, for business
conferences…
I used these different opportunities to refine a bit my presentation tools, and
I just summarize them here for curious people. Please note that this blog post
will only cover the tooling needed to produce slides, not what to put inside.
This page may be updated, for instance if I start using yet another tool for
slide making that I think is worth mentioning. If you have subscribed to this
blogs [RSS feed], you will be notified of future updates.
# Making Slides
For slide making, I prefer using tools that separate the content from the actual
design. Im thus not using fancy WYSIWYG tools for that. If you are not
interested in that, you can already skip to the [presenting slides] section.
## LaTeX Beamer
As explained in the [typst article], Im mostly using [LaTeX] to produce/typeset
documents, and presentations are not an exception. For this purpose Im using
[beamer].
For this purpose, my [vim setup for LaTeX] proved to be pretty useful,
especially with the “compilation on save” feature. It allows me to have an
already set up text editor for LaTeX without having to fiddle and twiddle with
multiple setups. However, the backward search is not very accurate with beamer
slides.
### Overlays and Graphics
The main advantage, besides my familiarity with [LaTeX], lays in the [overlay]
system in beamer, that is quite powerful and provides a very precise way to
display elements. This overlay mechanism also compounds well with [TikZ] to
design animated graphics.
For instance in the example below, I can show the top part of the graph
initially, then the bottom, and change the name of the last node for the second
slide. That can be easily adjusted to have more steps in the process.
```latex
\usetikzlibrary{positioning}
\begin{tikzpicture}
\tikzstyle{node} = [draw, rectangle, fill=blue!40, minimum height=2em]
\tikzstyle{arrow} = [->, >=stealth, very thick]
\node[node] (start) {Data};
\node[node, right=1cm of start] (a1) {Enc($\cdot$)};
\node<2->[node, below=5mm of a1] (a2) {Sig($\cdot$)};
\node<1>[node, right=1cm of a1] (stop) {Encrypted Data};
\node<2->[node, right=1cm of a1] (stop) {Encrypted and Signed Data};
\draw[arrow] (start) -- (a1);
\draw<2->[arrow] (start) -- (a2);
\draw[arrow] (a1) -- (stop);
\draw<2->[arrow] (a2) -- (stop);
\end{tikzpicture}
```
Resulting in:
![The last image from the above code](../examples/tikz-graph.svg "The last image from the above code"){width=66%}
Moreover, you have access to the whole latex ecosystem, especially those for
neat illustrations such as [tikzpingus].
**Note:** I feel compelled to say that the above technique is unsafe under fairly
reasonable assumptions. Long story short you should sign first *then* encrypt
and not do both in parallel. Please see [this paper](https://ia.cr/2001/045)
from the Crypto 2001 conference if you want a more detailed explanation.
### Customisation
It is also quite easy to customise slides with beamer. For instance, with
[metropolis], from its
[documentation](https://ctan.tetaneutral.net/macros/latex/contrib/beamer-contrib/themes/metropolis/doc/metropolistheme.pdf),
section 8 describes where to find specific colours. As for the fonts, if you are
using xelatex/lualatex, a simple `\setmainfont` suffices to redefine it.
For instance, if I want to have the alert text in orange:
```latex
\setbeamercolor[alerted text]{fg=orange}
```
### Drawbacks
However, LaTeX starts to slow down quickly, especially with a lot of [TikZ]
drawings… On documents, its not really an issue as it is possible to cache the
drawings with the `externalize` tikz library. However, when mixing overlays and
TikZ, it starts to [need some
tweaks](https://tex.stackexchange.com/questions/78955/use-tikz-external-feature-with-beamer-only).
I never included them in my workflow as they make TikZ drawings more complicated
than they are.
## Typst Touying
## Pandoc and reveal.js
[reveal.js] is a javascript framework to produce clean and dynamic slides. My
settings to generate them are liberally inspired by [Pablo
Coves](https://pcoves.gitlab.io/blog/pandoc-markdown-revealjs/).
[Pandoc] on the other hand is a document converter tool that supports a very
extensive spectrum of formats and syntaxes. My most use case is to convert
markdown to some other reflowable format (usually HTML, and sometimes EPUB).
Using both in conjunction allows for quick and dynamic presentations which dont
require _accuracy_ in placements. That may be the case for lightning talks for
instance. The main advantage compared to the two above solutions is that
[reveal.js] takes advantage of web browser capabilities to produce dynamic
transitions. Those are otherwise hard to get from PDFs (some people made custom
PDF reader for that).
I know that its also possible to use [pandoc] to produce directly [beamer] slides
for instance, thus benefiting from the simpler [Markdown] syntax while having
[LaTeX] as an engine. I however find this approach too rigid. It is indeed easy
to feed some LaTeXspecific commands via the YAML header, e.g., for styling.
Unfortunately, when the need arises to do some specific positioning on a slide
for example, then we end up with some markdown-TeX mix that I found deeply
inelegant. Thats why I usually stick to LaTeX (or more recently [typst]) to
produce PDFs, as these tools are designed with an awareness of the page layout
(which blends well into the language). This property is not the case with
[Markdown], which is a markup language for text formatting (not typesetting).
### Ease of use
One nice thing about [pandoc] + [reveal.js] slide making is that, for simple
intends and purpose, there are very little structural codes (contrary to
[beamer] for instance where you have to define several variables before
starting).
From the following code, you can start making a presentation:
```yaml
---
title: Example Presentation
subtitle: Its all about presenting
author: Fabrice Mouhartem
date: 2025-01-29
theme: solarized
---
```
Then run:
```sh
pandoc --standalone -t revealjs -o output.html input.md
```
And thats all… well, its just a title slide and an empty slide, but its the
beginning of a **wonderful** presentation.
Then, similarly to [typst] + [touying], a level 1 heading creates a title slide,
and a level 2 heading spawns a new content slide. You can also spawn a new slide
with three hyphens (`---`).
### Speaker view
One of the advantage of [reveal.js] is the built-in [speaker view]. It spans a
pop-up with useful pieces of information for the speaker: a chronometer, a preview of the
upcoming slide and notes if there are any.
Its behaviour is similar to what you can have with `pdfpc` that Ill show later
for PDF slides.
![Screenshot of the speaker view in reveal.js](/examples/revealjs-speakerview.png "Speaker View")
### Customisation
- List of default [reveal.js styles]
- Simple customisation with CSS:
<https://gist.github.com/jsoma/629b9564af5b1e7fa62d0a3a0a47c296#styling> see
<https://github.com/hakimel/reveal.js/blob/master/css/theme/template/exposer.scss>
as well for exposed variables.
- However, in standalone mode, changing the font does not work well…
- Create custom theme: <https://github.com/hakimel/reveal.js/blob/master/css/theme/README.md>
- <https://github.com/Chouhartem/reveal.js/tree/cryptpad-theme>
# Presenting Slides {#presenting-slides}
## wl-mirror
## pdfpc
[RSS feed]: /feeds/all.rss.xml
[typst]: https://typst.app/
[touying]: https://touying-typ.github.io/
[typst article]: {filename}../software/typst.md
[presenting slides]: #presenting-slides
[LaTeX]: https://www.latex-project.org/
[beamer]: https://ctan.org/pkg/beamer
[vim setup for LaTeX]: {filename}../software/nvim-latex.md
[overlay]: https://www.overleaf.com/learn/latex/Beamer_Presentations%3A_A_Tutorial_for_Beginners_(Part_4)%E2%80%94Overlay_Specifications
[TikZ]: https://www.ctan.org/pkg/pgf
[tikzpingus]: https://github.com/EagleoutIce/tikzpingus
[reveal.js]: https://revealjs.com/
[reveal.js styles]: https://revealjs.com/themes/
[pandoc]: https://pandoc.org/
[metropolis]: https://github.com/matze/mtheme
[markdown]: https://en.wikipedia.org/wiki/Markdown
[speaker view]: https://revealjs.com/speaker-view/

View File

@ -45,10 +45,16 @@ Tessen interprète ensuite la réponse de ces outils pour les intégrer avec
Après avoir essayé _fuzzel_, je me suis rendu compte que leur algorithme de
recherche approximative ne me convenait pas, n'étant pas
[fzf](https://github.com/junegunn/fzf) qui est intégré dans pas mal de mes
programmes et dont j'ai pris mes habitudes quant à la recherche.
programmes et dont j'ai pris mes habitudes quant à la recherche (par exemple
pour la complétion dans mon `shell`), ainsi que certains raccourcis assez
universels comme `Ctrl-u` pour effacer la ligne en cours.
J'ai donc décidé d'utiliser `rofi` qui avait l'air de bien fonctionner… mis à
part les raccourcis claviers. Mais un aperçu de la page de manuel indique que cela est possible:
J'ai donc décidé d'utiliser `rofi` qui avait l'air de bien fonctionner bien
qu'il ne se lance via `xwayland` qui n'est pas considéré comme assez pur par
certaines personnes.
Sauf que les raccourcis claviers spécifiques à `rofi-pass` ne fonctionnent pas
par défaut.
Cependant un aperçu de la page de manuel indique que cela est possible:
> If the dmenu program of your choice supports custom keybindings with exit
> codes greater than or equal to 10, tessen can execute custom operations on a
@ -65,7 +71,7 @@ tombe bien, dans la configuration de rofi, il y a les options
[`kb-custom-<n>`](https://github.com/davatorium/rofi/blob/next/doc/rofi-keys.5.markdown#kb-custom-1)
qui permettent dassocier la touche ou combinaison de touches à un code derreur
valant “_9+n_”, `kb-custom-1` correspondant donc à 10 et `kb-custom-9`
correspondant à 20 (je précise parce qu'au début j'avais associé un raccourcis à
correspondant à 20. Je précise parce qu'au début j'avais associé un raccourcis à
`kb-custom-10` en me disant que ça serait associé au code de sortie 10, et donc
interprété par tessen comme `auto type username and password ` d'après le
manuel… or il n'en était rien. Heureusement que je suis tombé sur un [message sur
@ -93,7 +99,9 @@ J'ai d'abord essayé de le rajouter dans la configuration par défaut
avec [rofimoji](https://github.com/fdw/rofimoji) qui ne s'est pas gêné pour me
le dire.
Maintenant il ne reste plus qu'à l'intégrer à tessen, ce qui est possible à l'aide du fichier de configuration `$HOME/.config/tessen/config` à l'aide des lignes :
Maintenant il ne reste plus qu'à l'intégrer à tessen, ce qui est possible à
l'aide du fichier de configuration `$HOME/.config/tessen/config` à l'aide des
lignes :
```sh
dmenu_backend="rofi"

View File

@ -1,6 +1,6 @@
---
Title: Rofi's keyboard shortcut for passwordstore on Wayland
Date: 2023-07-09 15:00
Date: 2023-07-10 12:00
Author: Fabrice
Category: Tips
Tags: pass, rofi, tessen

View File

@ -32,7 +32,7 @@ TIMEZONE = 'Europe/Paris'
#LANG
DEFAULT_LANG = 'en'
LOCALE = 'en_GB.UTF-8'
LOCALE = 'en_GB.utf8'
DEFAULT_DATE_FORMAT = '%A, %B %d, %Y'
I18N_SUBSITES = {
'fr': {
@ -59,5 +59,5 @@ CATEGORY_SAVE_AS = '{slug}/index.html'
CATEGORY_PAGE_PATH= 'categories'
# Uncomment following line if you want document-relative URLs when developing
#RELATIVE_URLS = True
# RELATIVE_URLS = True

1
plugins/autopages Submodule

Submodule plugins/autopages added at 7ebe350704

1
plugins/i18n_subsites Submodule

Submodule plugins/i18n_subsites added at 5903058c97

View File

@ -15,9 +15,12 @@ RELATIVE_URLS = False
DELETE_OUTPUT_DIRECTORY = True
FEED_ALL_ATOM = None
FEED_DOMAIN = SITEURL
FEED_ALL_ATOM = 'feeds/all.atom.xml'
FEED_ALL_RSS = 'feeds/all.rss.xml'
CATEGORY_FEED_ATOM = None
TRANSLATION_FEED_ATOM = None
TRANSLATION_FEED_ATOM = 'feeds/all-{lang}.atom.xml'
TRANSLATION_FEED_RSS = 'feeds/all-{lang}.rss.xml'
AUTHOR_FEED_ATOM = None
AUTHOR_FEED_RSS = None

16
pyproject.toml Normal file
View File

@ -0,0 +1,16 @@
[project]
authors = [
{name = "Fabrice Mouhartem", email = "chouhartem@epheme.re"},
]
license = {text = "CC-by-SA"}
requires-python = "<4.0,>=3.12"
dependencies = [
"pelican<5.0.0,>=4.10.1",
"markdown<4.0,>=3.7",
"invoke<3.0.0,>=2.2.0",
"livereload<3.0.0,>=2.7.0",
]
name = "blog"
version = "0.1.0"
description = "Personal blog (blog.epheme.re)"
readme = "README.md"

View File

@ -1 +1,52 @@
This repository contains the sources necessary to build the blog at:
<https://blog.epheme.re>
# Dependencies
To use this repository as intended, you need, to build the blog, the following
software:
- `git`
- `make`
- `uv`
- `gettext`
To synchronise the blog remotely with its intended target, the synchronisation
is done using `rsync` over `ssh`.
# Install
To install a local copy to work on this blog, you also need other components,
such as the [theme](https://git.epheme.re/fmouhart/pelican-clean-blog) and
[some](https://git.epheme.re/fmouhart/pelican-autopages)
[plugins](https://git.epheme.re/fmouhart/pelican-clean-blog). Those are embedded
in the repository as a git submodule. You can thus simply run git clone with the
`--recurse-submodule` option:
```sh
git clone --recurse-submodule https://git.epheme.re/fmouhart/blog.git fmouhart-blog
```
This blog relies on [pelican](https://getpelican.com) as a static site
generator. To manage the different python dependencies of this project, we are
using [`uv`](https://github.com/astral-sh/uv) as a python project manager.
Moreover, translations are managed with python `gettext` which requires
compiling the translation file.
Those two steps are performed with the following command:
```sh
make init
```
# Development
When writing an article, you can run the blog with `livereload` enabled with the
command:
```sh
make dev
```
Itll span a local development server on port `8000`: <http://localhost:8000>

150
tasks.py Normal file
View File

@ -0,0 +1,150 @@
import os
import shlex
import shutil
import sys
from invoke import task
from invoke.main import program
from pelican import main as pelican_main
from pelican.server import ComplexHTTPRequestHandler, RootedHTTPServer
from pelican.settings import DEFAULT_CONFIG, get_settings_from_file
OPEN_BROWSER_ON_SERVE = False
SETTINGS_FILE_BASE = "pelicanconf.py"
SETTINGS = {}
SETTINGS.update(DEFAULT_CONFIG)
LOCAL_SETTINGS = get_settings_from_file(SETTINGS_FILE_BASE)
SETTINGS.update(LOCAL_SETTINGS)
CONFIG = {
"settings_base": SETTINGS_FILE_BASE,
"settings_publish": "publishconf.py",
# Output path. Can be absolute or relative to tasks.py. Default: 'output'
"deploy_path": SETTINGS["OUTPUT_PATH"],
# Host and port for `serve`
"host": "localhost",
"port": 8000,
# for publication
"ssh_user": "fmouhart",
"ssh_host": "blog.epheme.re",
"ssh_port": 22,
"ssh_path": "/srv/http/blog",
}
@task
def clean(c):
"""Remove generated files"""
if os.path.isdir(CONFIG["deploy_path"]):
shutil.rmtree(CONFIG["deploy_path"])
os.makedirs(CONFIG["deploy_path"])
@task
def build(c):
"""Build local version of site"""
pelican_run("-s {settings_base}".format(**CONFIG))
@task
def rebuild(c):
"""`build` with the delete switch"""
pelican_run("-d -s {settings_base}".format(**CONFIG))
@task
def regenerate(c):
"""Automatically regenerate site upon file modification"""
pelican_run("-r -s {settings_base}".format(**CONFIG))
@task
def serve(c):
"""Serve site at http://$HOST:$PORT/ (default is localhost:8000)"""
class AddressReuseTCPServer(RootedHTTPServer):
allow_reuse_address = True
server = AddressReuseTCPServer(
CONFIG["deploy_path"],
(CONFIG["host"], CONFIG["port"]),
ComplexHTTPRequestHandler,
)
if OPEN_BROWSER_ON_SERVE:
# Open site in default browser
import webbrowser
webbrowser.open("http://{host}:{port}".format(**CONFIG))
sys.stderr.write("Serving at {host}:{port} ...\n".format(**CONFIG))
server.serve_forever()
@task
def reserve(c):
"""`build`, then `serve`"""
build(c)
serve(c)
@task
def preview(c):
"""Build production version of site"""
pelican_run("-s {settings_publish}".format(**CONFIG))
@task
def livereload(c):
"""Automatically reload browser tab upon file modification."""
from livereload import Server
def cached_build():
cmd = "-s {settings_base} -e CACHE_CONTENT=true LOAD_CONTENT_CACHE=true"
pelican_run(cmd.format(**CONFIG))
cached_build()
server = Server()
theme_path = SETTINGS["THEME"]
watched_globs = [
CONFIG["settings_base"],
f"{theme_path}/templates/**/*.html",
]
content_file_extensions = [".md", ".rst"]
for extension in content_file_extensions:
content_glob = "{}/**/*{}".format(SETTINGS["PATH"], extension)
watched_globs.append(content_glob)
static_file_extensions = [".css", ".js"]
for extension in static_file_extensions:
static_file_glob = f"{theme_path}/static/**/*{extension}"
watched_globs.append(static_file_glob)
for glob in watched_globs:
server.watch(glob, cached_build)
if OPEN_BROWSER_ON_SERVE:
# Open site in default browser
import webbrowser
webbrowser.open("http://{host}:{port}".format(**CONFIG))
server.serve(host=CONFIG["host"], port=CONFIG["port"], root=CONFIG["deploy_path"])
@task
def publish(c):
"""Publish to production via rsync"""
pelican_run("-s {settings_publish}".format(**CONFIG))
c.run(
'rsync --delete --exclude ".DS_Store" -rv -c '
'-e "ssh -p {ssh_port}" '
"{} {ssh_user}@{ssh_host}:{ssh_path}".format(
CONFIG["deploy_path"].rstrip("/") + "/", **CONFIG
)
)
def pelican_run(cmd):
cmd += " " + program.core.remainder # allows to pass-through args to pelican
pelican_main(shlex.split(cmd))

348
uv.lock generated Normal file
View File

@ -0,0 +1,348 @@
version = 1
requires-python = ">=3.12, <4.0"
[[package]]
name = "anyio"
version = "4.8.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "idna" },
{ name = "sniffio" },
{ name = "typing-extensions", marker = "python_full_version < '3.13'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/a3/73/199a98fc2dae33535d6b8e8e6ec01f8c1d76c9adb096c6b7d64823038cde/anyio-4.8.0.tar.gz", hash = "sha256:1d9fe889df5212298c0c0723fa20479d1b94883a2df44bd3897aa91083316f7a", size = 181126 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/46/eb/e7f063ad1fec6b3178a3cd82d1a3c4de82cccf283fc42746168188e1cdd5/anyio-4.8.0-py3-none-any.whl", hash = "sha256:b5011f270ab5eb0abf13385f851315585cc37ef330dd88e27ec3d34d651fd47a", size = 96041 },
]
[[package]]
name = "blinker"
version = "1.9.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/21/28/9b3f50ce0e048515135495f198351908d99540d69bfdc8c1d15b73dc55ce/blinker-1.9.0.tar.gz", hash = "sha256:b4ce2265a7abece45e7cc896e98dbebe6cead56bcf805a3d23136d145f5445bf", size = 22460 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/10/cb/f2ad4230dc2eb1a74edf38f1a38b9b52277f75bef262d8908e60d957e13c/blinker-1.9.0-py3-none-any.whl", hash = "sha256:ba0efaa9080b619ff2f3459d1d500c57bddea4a6b424b60a91141db6fd2f08bc", size = 8458 },
]
[[package]]
name = "blog"
version = "0.1.0"
source = { virtual = "." }
dependencies = [
{ name = "invoke" },
{ name = "livereload" },
{ name = "markdown" },
{ name = "pelican" },
]
[package.metadata]
requires-dist = [
{ name = "invoke", specifier = ">=2.2.0,<3.0.0" },
{ name = "livereload", specifier = ">=2.7.0,<3.0.0" },
{ name = "markdown", specifier = ">=3.7,<4.0" },
{ name = "pelican", specifier = ">=4.10.1,<5.0.0" },
]
[[package]]
name = "docutils"
version = "0.21.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/ae/ed/aefcc8cd0ba62a0560c3c18c33925362d46c6075480bfa4df87b28e169a9/docutils-0.21.2.tar.gz", hash = "sha256:3a6b18732edf182daa3cd12775bbb338cf5691468f91eeeb109deff6ebfa986f", size = 2204444 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/8f/d7/9322c609343d929e75e7e5e6255e614fcc67572cfd083959cdef3b7aad79/docutils-0.21.2-py3-none-any.whl", hash = "sha256:dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2", size = 587408 },
]
[[package]]
name = "feedgenerator"
version = "2.1.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "pytz" },
]
sdist = { url = "https://files.pythonhosted.org/packages/5e/4e/0efde53652edbae3f86c0ec67260bb53287edc67033ac8d00fe08cd02557/feedgenerator-2.1.0.tar.gz", hash = "sha256:f075f23f28fd227f097c36b212161c6cf012e1c6caaf7ff53d5d6bb02cd42b9d", size = 20682 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/bd/a1/b7b1711d9bf43c3795366431633ab6ba6942744243aad809272ebfa59b39/feedgenerator-2.1.0-py3-none-any.whl", hash = "sha256:93b7ce1c5a86195cafd6a8e9baf6a2a863ebd6d9905e840ce5778f73efd9a8d5", size = 21796 },
]
[[package]]
name = "idna"
version = "3.10"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442 },
]
[[package]]
name = "invoke"
version = "2.2.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/f9/42/127e6d792884ab860defc3f4d80a8f9812e48ace584ffc5a346de58cdc6c/invoke-2.2.0.tar.gz", hash = "sha256:ee6cbb101af1a859c7fe84f2a264c059020b0cb7fe3535f9424300ab568f6bd5", size = 299835 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/0a/66/7f8c48009c72d73bc6bbe6eb87ac838d6a526146f7dab14af671121eb379/invoke-2.2.0-py3-none-any.whl", hash = "sha256:6ea924cc53d4f78e3d98bc436b08069a03077e6f85ad1ddaa8a116d7dad15820", size = 160274 },
]
[[package]]
name = "jinja2"
version = "3.1.5"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "markupsafe" },
]
sdist = { url = "https://files.pythonhosted.org/packages/af/92/b3130cbbf5591acf9ade8708c365f3238046ac7cb8ccba6e81abccb0ccff/jinja2-3.1.5.tar.gz", hash = "sha256:8fefff8dc3034e27bb80d67c671eb8a9bc424c0ef4c0826edbff304cceff43bb", size = 244674 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/bd/0f/2ba5fbcd631e3e88689309dbe978c5769e883e4b84ebfe7da30b43275c5a/jinja2-3.1.5-py3-none-any.whl", hash = "sha256:aba0f4dc9ed8013c424088f68a5c226f7d6097ed89b246d7749c2ec4175c6adb", size = 134596 },
]
[[package]]
name = "livereload"
version = "2.7.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "tornado" },
]
sdist = { url = "https://files.pythonhosted.org/packages/43/6e/f2748665839812a9bbe5c75d3f983edbf3ab05fa5cd2f7c2f36fffdf65bd/livereload-2.7.1.tar.gz", hash = "sha256:3d9bf7c05673df06e32bea23b494b8d36ca6d10f7d5c3c8a6989608c09c986a9", size = 22255 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/e4/3e/de54dc7f199e85e6ca37e2e5dae2ec3bce2151e9e28f8eb9076d71e83d56/livereload-2.7.1-py3-none-any.whl", hash = "sha256:5201740078c1b9433f4b2ba22cd2729a39b9d0ec0a2cc6b4d3df257df5ad0564", size = 22657 },
]
[[package]]
name = "markdown"
version = "3.7"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/54/28/3af612670f82f4c056911fbbbb42760255801b3068c48de792d354ff4472/markdown-3.7.tar.gz", hash = "sha256:2ae2471477cfd02dbbf038d5d9bc226d40def84b4fe2986e49b59b6b472bbed2", size = 357086 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/3f/08/83871f3c50fc983b88547c196d11cf8c3340e37c32d2e9d6152abe2c61f7/Markdown-3.7-py3-none-any.whl", hash = "sha256:7eb6df5690b81a1d7942992c97fad2938e956e79df20cbc6186e9c3a77b1c803", size = 106349 },
]
[[package]]
name = "markdown-it-py"
version = "3.0.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "mdurl" },
]
sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528 },
]
[[package]]
name = "markupsafe"
version = "3.0.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/b2/97/5d42485e71dfc078108a86d6de8fa46db44a1a9295e89c5d6d4a06e23a62/markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0", size = 20537 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/22/09/d1f21434c97fc42f09d290cbb6350d44eb12f09cc62c9476effdb33a18aa/MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf", size = 14274 },
{ url = "https://files.pythonhosted.org/packages/6b/b0/18f76bba336fa5aecf79d45dcd6c806c280ec44538b3c13671d49099fdd0/MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225", size = 12348 },
{ url = "https://files.pythonhosted.org/packages/e0/25/dd5c0f6ac1311e9b40f4af06c78efde0f3b5cbf02502f8ef9501294c425b/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028", size = 24149 },
{ url = "https://files.pythonhosted.org/packages/f3/f0/89e7aadfb3749d0f52234a0c8c7867877876e0a20b60e2188e9850794c17/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8", size = 23118 },
{ url = "https://files.pythonhosted.org/packages/d5/da/f2eeb64c723f5e3777bc081da884b414671982008c47dcc1873d81f625b6/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c", size = 22993 },
{ url = "https://files.pythonhosted.org/packages/da/0e/1f32af846df486dce7c227fe0f2398dc7e2e51d4a370508281f3c1c5cddc/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557", size = 24178 },
{ url = "https://files.pythonhosted.org/packages/c4/f6/bb3ca0532de8086cbff5f06d137064c8410d10779c4c127e0e47d17c0b71/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22", size = 23319 },
{ url = "https://files.pythonhosted.org/packages/a2/82/8be4c96ffee03c5b4a034e60a31294daf481e12c7c43ab8e34a1453ee48b/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48", size = 23352 },
{ url = "https://files.pythonhosted.org/packages/51/ae/97827349d3fcffee7e184bdf7f41cd6b88d9919c80f0263ba7acd1bbcb18/MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30", size = 15097 },
{ url = "https://files.pythonhosted.org/packages/c1/80/a61f99dc3a936413c3ee4e1eecac96c0da5ed07ad56fd975f1a9da5bc630/MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87", size = 15601 },
{ url = "https://files.pythonhosted.org/packages/83/0e/67eb10a7ecc77a0c2bbe2b0235765b98d164d81600746914bebada795e97/MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd", size = 14274 },
{ url = "https://files.pythonhosted.org/packages/2b/6d/9409f3684d3335375d04e5f05744dfe7e9f120062c9857df4ab490a1031a/MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430", size = 12352 },
{ url = "https://files.pythonhosted.org/packages/d2/f5/6eadfcd3885ea85fe2a7c128315cc1bb7241e1987443d78c8fe712d03091/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094", size = 24122 },
{ url = "https://files.pythonhosted.org/packages/0c/91/96cf928db8236f1bfab6ce15ad070dfdd02ed88261c2afafd4b43575e9e9/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396", size = 23085 },
{ url = "https://files.pythonhosted.org/packages/c2/cf/c9d56af24d56ea04daae7ac0940232d31d5a8354f2b457c6d856b2057d69/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79", size = 22978 },
{ url = "https://files.pythonhosted.org/packages/2a/9f/8619835cd6a711d6272d62abb78c033bda638fdc54c4e7f4272cf1c0962b/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a", size = 24208 },
{ url = "https://files.pythonhosted.org/packages/f9/bf/176950a1792b2cd2102b8ffeb5133e1ed984547b75db47c25a67d3359f77/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca", size = 23357 },
{ url = "https://files.pythonhosted.org/packages/ce/4f/9a02c1d335caabe5c4efb90e1b6e8ee944aa245c1aaaab8e8a618987d816/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c", size = 23344 },
{ url = "https://files.pythonhosted.org/packages/ee/55/c271b57db36f748f0e04a759ace9f8f759ccf22b4960c270c78a394f58be/MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1", size = 15101 },
{ url = "https://files.pythonhosted.org/packages/29/88/07df22d2dd4df40aba9f3e402e6dc1b8ee86297dddbad4872bd5e7b0094f/MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f", size = 15603 },
{ url = "https://files.pythonhosted.org/packages/62/6a/8b89d24db2d32d433dffcd6a8779159da109842434f1dd2f6e71f32f738c/MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c", size = 14510 },
{ url = "https://files.pythonhosted.org/packages/7a/06/a10f955f70a2e5a9bf78d11a161029d278eeacbd35ef806c3fd17b13060d/MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb", size = 12486 },
{ url = "https://files.pythonhosted.org/packages/34/cf/65d4a571869a1a9078198ca28f39fba5fbb910f952f9dbc5220afff9f5e6/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c", size = 25480 },
{ url = "https://files.pythonhosted.org/packages/0c/e3/90e9651924c430b885468b56b3d597cabf6d72be4b24a0acd1fa0e12af67/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d", size = 23914 },
{ url = "https://files.pythonhosted.org/packages/66/8c/6c7cf61f95d63bb866db39085150df1f2a5bd3335298f14a66b48e92659c/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe", size = 23796 },
{ url = "https://files.pythonhosted.org/packages/bb/35/cbe9238ec3f47ac9a7c8b3df7a808e7cb50fe149dc7039f5f454b3fba218/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5", size = 25473 },
{ url = "https://files.pythonhosted.org/packages/e6/32/7621a4382488aa283cc05e8984a9c219abad3bca087be9ec77e89939ded9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a", size = 24114 },
{ url = "https://files.pythonhosted.org/packages/0d/80/0985960e4b89922cb5a0bac0ed39c5b96cbc1a536a99f30e8c220a996ed9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9", size = 24098 },
{ url = "https://files.pythonhosted.org/packages/82/78/fedb03c7d5380df2427038ec8d973587e90561b2d90cd472ce9254cf348b/MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6", size = 15208 },
{ url = "https://files.pythonhosted.org/packages/4f/65/6079a46068dfceaeabb5dcad6d674f5f5c61a6fa5673746f42a9f4c233b3/MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", size = 15739 },
]
[[package]]
name = "mdurl"
version = "0.1.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979 },
]
[[package]]
name = "ordered-set"
version = "4.1.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/4c/ca/bfac8bc689799bcca4157e0e0ced07e70ce125193fc2e166d2e685b7e2fe/ordered-set-4.1.0.tar.gz", hash = "sha256:694a8e44c87657c59292ede72891eb91d34131f6531463aab3009191c77364a8", size = 12826 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/33/55/af02708f230eb77084a299d7b08175cff006dea4f2721074b92cdb0296c0/ordered_set-4.1.0-py3-none-any.whl", hash = "sha256:046e1132c71fcf3330438a539928932caf51ddbc582496833e23de611de14562", size = 7634 },
]
[[package]]
name = "pelican"
version = "4.11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "blinker" },
{ name = "docutils" },
{ name = "feedgenerator" },
{ name = "jinja2" },
{ name = "ordered-set" },
{ name = "pygments" },
{ name = "python-dateutil" },
{ name = "rich" },
{ name = "tzdata", marker = "sys_platform == 'win32'" },
{ name = "unidecode" },
{ name = "watchfiles" },
]
sdist = { url = "https://files.pythonhosted.org/packages/27/42/c06c1a7a3136729ece5a1f98544ede83edd593b3cd9110c9ad61bcc7f4dd/pelican-4.11.0.tar.gz", hash = "sha256:b90234487b818d391733acc1306b785934009749b1fc112b879df9bd89478bd8", size = 24148058 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/79/32/01df483f255438e792771b11ebe804af15957739e88673ca95c89289c0f4/pelican-4.11.0-py3-none-any.whl", hash = "sha256:aca6993f6b8a03a20f6828471089cb0504a4dca71e0d30b341fa80ab65668fa4", size = 24127868 },
]
[[package]]
name = "pygments"
version = "2.18.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/8e/62/8336eff65bcbc8e4cb5d05b55faf041285951b6e80f33e2bff2024788f31/pygments-2.18.0.tar.gz", hash = "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199", size = 4891905 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/f7/3f/01c8b82017c199075f8f788d0d906b9ffbbc5a47dc9918a945e13d5a2bda/pygments-2.18.0-py3-none-any.whl", hash = "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a", size = 1205513 },
]
[[package]]
name = "python-dateutil"
version = "2.9.0.post0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "six" },
]
sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892 },
]
[[package]]
name = "pytz"
version = "2024.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/3a/31/3c70bf7603cc2dca0f19bdc53b4537a797747a58875b552c8c413d963a3f/pytz-2024.2.tar.gz", hash = "sha256:2aa355083c50a0f93fa581709deac0c9ad65cca8a9e9beac660adcbd493c798a", size = 319692 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/11/c3/005fcca25ce078d2cc29fd559379817424e94885510568bc1bc53d7d5846/pytz-2024.2-py2.py3-none-any.whl", hash = "sha256:31c7c1817eb7fae7ca4b8c7ee50c72f93aa2dd863de768e1ef4245d426aa0725", size = 508002 },
]
[[package]]
name = "rich"
version = "13.9.4"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "markdown-it-py" },
{ name = "pygments" },
]
sdist = { url = "https://files.pythonhosted.org/packages/ab/3a/0316b28d0761c6734d6bc14e770d85506c986c85ffb239e688eeaab2c2bc/rich-13.9.4.tar.gz", hash = "sha256:439594978a49a09530cff7ebc4b5c7103ef57baf48d5ea3184f21d9a2befa098", size = 223149 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/19/71/39c7c0d87f8d4e6c020a393182060eaefeeae6c01dab6a84ec346f2567df/rich-13.9.4-py3-none-any.whl", hash = "sha256:6049d5e6ec054bf2779ab3358186963bac2ea89175919d699e378b99738c2a90", size = 242424 },
]
[[package]]
name = "six"
version = "1.17.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050 },
]
[[package]]
name = "sniffio"
version = "1.3.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235 },
]
[[package]]
name = "tornado"
version = "6.4.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/59/45/a0daf161f7d6f36c3ea5fc0c2de619746cc3dd4c76402e9db545bd920f63/tornado-6.4.2.tar.gz", hash = "sha256:92bad5b4746e9879fd7bf1eb21dce4e3fc5128d71601f80005afa39237ad620b", size = 501135 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/26/7e/71f604d8cea1b58f82ba3590290b66da1e72d840aeb37e0d5f7291bd30db/tornado-6.4.2-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:e828cce1123e9e44ae2a50a9de3055497ab1d0aeb440c5ac23064d9e44880da1", size = 436299 },
{ url = "https://files.pythonhosted.org/packages/96/44/87543a3b99016d0bf54fdaab30d24bf0af2e848f1d13d34a3a5380aabe16/tornado-6.4.2-cp38-abi3-macosx_10_9_x86_64.whl", hash = "sha256:072ce12ada169c5b00b7d92a99ba089447ccc993ea2143c9ede887e0937aa803", size = 434253 },
{ url = "https://files.pythonhosted.org/packages/cb/fb/fdf679b4ce51bcb7210801ef4f11fdac96e9885daa402861751353beea6e/tornado-6.4.2-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a017d239bd1bb0919f72af256a970624241f070496635784d9bf0db640d3fec", size = 437602 },
{ url = "https://files.pythonhosted.org/packages/4f/3b/e31aeffffc22b475a64dbeb273026a21b5b566f74dee48742817626c47dc/tornado-6.4.2-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c36e62ce8f63409301537222faffcef7dfc5284f27eec227389f2ad11b09d946", size = 436972 },
{ url = "https://files.pythonhosted.org/packages/22/55/b78a464de78051a30599ceb6983b01d8f732e6f69bf37b4ed07f642ac0fc/tornado-6.4.2-cp38-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bca9eb02196e789c9cb5c3c7c0f04fb447dc2adffd95265b2c7223a8a615ccbf", size = 437173 },
{ url = "https://files.pythonhosted.org/packages/79/5e/be4fb0d1684eb822c9a62fb18a3e44a06188f78aa466b2ad991d2ee31104/tornado-6.4.2-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:304463bd0772442ff4d0f5149c6f1c2135a1fae045adf070821c6cdc76980634", size = 437892 },
{ url = "https://files.pythonhosted.org/packages/f5/33/4f91fdd94ea36e1d796147003b490fe60a0215ac5737b6f9c65e160d4fe0/tornado-6.4.2-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:c82c46813ba483a385ab2a99caeaedf92585a1f90defb5693351fa7e4ea0bf73", size = 437334 },
{ url = "https://files.pythonhosted.org/packages/2b/ae/c1b22d4524b0e10da2f29a176fb2890386f7bd1f63aacf186444873a88a0/tornado-6.4.2-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:932d195ca9015956fa502c6b56af9eb06106140d844a335590c1ec7f5277d10c", size = 437261 },
{ url = "https://files.pythonhosted.org/packages/b5/25/36dbd49ab6d179bcfc4c6c093a51795a4f3bed380543a8242ac3517a1751/tornado-6.4.2-cp38-abi3-win32.whl", hash = "sha256:2876cef82e6c5978fde1e0d5b1f919d756968d5b4282418f3146b79b58556482", size = 438463 },
{ url = "https://files.pythonhosted.org/packages/61/cc/58b1adeb1bb46228442081e746fcdbc4540905c87e8add7c277540934edb/tornado-6.4.2-cp38-abi3-win_amd64.whl", hash = "sha256:908b71bf3ff37d81073356a5fadcc660eb10c1476ee6e2725588626ce7e5ca38", size = 438907 },
]
[[package]]
name = "typing-extensions"
version = "4.12.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/df/db/f35a00659bc03fec321ba8bce9420de607a1d37f8342eee1863174c69557/typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8", size = 85321 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/26/9f/ad63fc0248c5379346306f8668cda6e2e2e9c95e01216d2b8ffd9ff037d0/typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", size = 37438 },
]
[[package]]
name = "tzdata"
version = "2025.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/43/0f/fa4723f22942480be4ca9527bbde8d43f6c3f2fe8412f00e7f5f6746bc8b/tzdata-2025.1.tar.gz", hash = "sha256:24894909e88cdb28bd1636c6887801df64cb485bd593f2fd83ef29075a81d694", size = 194950 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/0f/dd/84f10e23edd882c6f968c21c2434fe67bd4a528967067515feca9e611e5e/tzdata-2025.1-py2.py3-none-any.whl", hash = "sha256:7e127113816800496f027041c570f50bcd464a020098a3b6b199517772303639", size = 346762 },
]
[[package]]
name = "unidecode"
version = "1.3.8"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/f7/89/19151076a006b9ac0dd37b1354e031f5297891ee507eb624755e58e10d3e/Unidecode-1.3.8.tar.gz", hash = "sha256:cfdb349d46ed3873ece4586b96aa75258726e2fa8ec21d6f00a591d98806c2f4", size = 192701 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/84/b7/6ec57841fb67c98f52fc8e4a2d96df60059637cba077edc569a302a8ffc7/Unidecode-1.3.8-py3-none-any.whl", hash = "sha256:d130a61ce6696f8148a3bd8fe779c99adeb4b870584eeb9526584e9aa091fd39", size = 235494 },
]
[[package]]
name = "watchfiles"
version = "1.0.4"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "anyio" },
]
sdist = { url = "https://files.pythonhosted.org/packages/f5/26/c705fc77d0a9ecdb9b66f1e2976d95b81df3cae518967431e7dbf9b5e219/watchfiles-1.0.4.tar.gz", hash = "sha256:6ba473efd11062d73e4f00c2b730255f9c1bdd73cd5f9fe5b5da8dbd4a717205", size = 94625 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/5b/1a/8f4d9a1461709756ace48c98f07772bc6d4519b1e48b5fa24a4061216256/watchfiles-1.0.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:229e6ec880eca20e0ba2f7e2249c85bae1999d330161f45c78d160832e026ee2", size = 391345 },
{ url = "https://files.pythonhosted.org/packages/bc/d2/6750b7b3527b1cdaa33731438432e7238a6c6c40a9924049e4cebfa40805/watchfiles-1.0.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5717021b199e8353782dce03bd8a8f64438832b84e2885c4a645f9723bf656d9", size = 381515 },
{ url = "https://files.pythonhosted.org/packages/4e/17/80500e42363deef1e4b4818729ed939aaddc56f82f4e72b2508729dd3c6b/watchfiles-1.0.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0799ae68dfa95136dde7c472525700bd48777875a4abb2ee454e3ab18e9fc712", size = 449767 },
{ url = "https://files.pythonhosted.org/packages/10/37/1427fa4cfa09adbe04b1e97bced19a29a3462cc64c78630787b613a23f18/watchfiles-1.0.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:43b168bba889886b62edb0397cab5b6490ffb656ee2fcb22dec8bfeb371a9e12", size = 455677 },
{ url = "https://files.pythonhosted.org/packages/c5/7a/39e9397f3a19cb549a7d380412fd9e507d4854eddc0700bfad10ef6d4dba/watchfiles-1.0.4-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fb2c46e275fbb9f0c92e7654b231543c7bbfa1df07cdc4b99fa73bedfde5c844", size = 482219 },
{ url = "https://files.pythonhosted.org/packages/45/2d/7113931a77e2ea4436cad0c1690c09a40a7f31d366f79c6f0a5bc7a4f6d5/watchfiles-1.0.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:857f5fc3aa027ff5e57047da93f96e908a35fe602d24f5e5d8ce64bf1f2fc733", size = 518830 },
{ url = "https://files.pythonhosted.org/packages/f9/1b/50733b1980fa81ef3c70388a546481ae5fa4c2080040100cd7bf3bf7b321/watchfiles-1.0.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55ccfd27c497b228581e2838d4386301227fc0cb47f5a12923ec2fe4f97b95af", size = 497997 },
{ url = "https://files.pythonhosted.org/packages/2b/b4/9396cc61b948ef18943e7c85ecfa64cf940c88977d882da57147f62b34b1/watchfiles-1.0.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5c11ea22304d17d4385067588123658e9f23159225a27b983f343fcffc3e796a", size = 452249 },
{ url = "https://files.pythonhosted.org/packages/fb/69/0c65a5a29e057ad0dc691c2fa6c23b2983c7dabaa190ba553b29ac84c3cc/watchfiles-1.0.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:74cb3ca19a740be4caa18f238298b9d472c850f7b2ed89f396c00a4c97e2d9ff", size = 614412 },
{ url = "https://files.pythonhosted.org/packages/7f/b9/319fcba6eba5fad34327d7ce16a6b163b39741016b1996f4a3c96b8dd0e1/watchfiles-1.0.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:c7cce76c138a91e720d1df54014a047e680b652336e1b73b8e3ff3158e05061e", size = 611982 },
{ url = "https://files.pythonhosted.org/packages/f1/47/143c92418e30cb9348a4387bfa149c8e0e404a7c5b0585d46d2f7031b4b9/watchfiles-1.0.4-cp312-cp312-win32.whl", hash = "sha256:b045c800d55bc7e2cadd47f45a97c7b29f70f08a7c2fa13241905010a5493f94", size = 271822 },
{ url = "https://files.pythonhosted.org/packages/ea/94/b0165481bff99a64b29e46e07ac2e0df9f7a957ef13bec4ceab8515f44e3/watchfiles-1.0.4-cp312-cp312-win_amd64.whl", hash = "sha256:c2acfa49dd0ad0bf2a9c0bb9a985af02e89345a7189be1efc6baa085e0f72d7c", size = 285441 },
{ url = "https://files.pythonhosted.org/packages/11/de/09fe56317d582742d7ca8c2ca7b52a85927ebb50678d9b0fa8194658f536/watchfiles-1.0.4-cp312-cp312-win_arm64.whl", hash = "sha256:22bb55a7c9e564e763ea06c7acea24fc5d2ee5dfc5dafc5cfbedfe58505e9f90", size = 277141 },
{ url = "https://files.pythonhosted.org/packages/08/98/f03efabec64b5b1fa58c0daab25c68ef815b0f320e54adcacd0d6847c339/watchfiles-1.0.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:8012bd820c380c3d3db8435e8cf7592260257b378b649154a7948a663b5f84e9", size = 390954 },
{ url = "https://files.pythonhosted.org/packages/16/09/4dd49ba0a32a45813debe5fb3897955541351ee8142f586303b271a02b40/watchfiles-1.0.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:aa216f87594f951c17511efe5912808dfcc4befa464ab17c98d387830ce07b60", size = 381133 },
{ url = "https://files.pythonhosted.org/packages/76/59/5aa6fc93553cd8d8ee75c6247763d77c02631aed21551a97d94998bf1dae/watchfiles-1.0.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:62c9953cf85529c05b24705639ffa390f78c26449e15ec34d5339e8108c7c407", size = 449516 },
{ url = "https://files.pythonhosted.org/packages/4c/aa/df4b6fe14b6317290b91335b23c96b488d365d65549587434817e06895ea/watchfiles-1.0.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7cf684aa9bba4cd95ecb62c822a56de54e3ae0598c1a7f2065d51e24637a3c5d", size = 454820 },
{ url = "https://files.pythonhosted.org/packages/5e/71/185f8672f1094ce48af33252c73e39b48be93b761273872d9312087245f6/watchfiles-1.0.4-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f44a39aee3cbb9b825285ff979ab887a25c5d336e5ec3574f1506a4671556a8d", size = 481550 },
{ url = "https://files.pythonhosted.org/packages/85/d7/50ebba2c426ef1a5cb17f02158222911a2e005d401caf5d911bfca58f4c4/watchfiles-1.0.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a38320582736922be8c865d46520c043bff350956dfc9fbaee3b2df4e1740a4b", size = 518647 },
{ url = "https://files.pythonhosted.org/packages/f0/7a/4c009342e393c545d68987e8010b937f72f47937731225b2b29b7231428f/watchfiles-1.0.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:39f4914548b818540ef21fd22447a63e7be6e24b43a70f7642d21f1e73371590", size = 497547 },
{ url = "https://files.pythonhosted.org/packages/0f/7c/1cf50b35412d5c72d63b2bf9a4fffee2e1549a245924960dd087eb6a6de4/watchfiles-1.0.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f12969a3765909cf5dc1e50b2436eb2c0e676a3c75773ab8cc3aa6175c16e902", size = 452179 },
{ url = "https://files.pythonhosted.org/packages/d6/a9/3db1410e1c1413735a9a472380e4f431ad9a9e81711cda2aaf02b7f62693/watchfiles-1.0.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:0986902677a1a5e6212d0c49b319aad9cc48da4bd967f86a11bde96ad9676ca1", size = 614125 },
{ url = "https://files.pythonhosted.org/packages/f2/e1/0025d365cf6248c4d1ee4c3d2e3d373bdd3f6aff78ba4298f97b4fad2740/watchfiles-1.0.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:308ac265c56f936636e3b0e3f59e059a40003c655228c131e1ad439957592303", size = 611911 },
{ url = "https://files.pythonhosted.org/packages/55/55/035838277d8c98fc8c917ac9beeb0cd6c59d675dc2421df5f9fcf44a0070/watchfiles-1.0.4-cp313-cp313-win32.whl", hash = "sha256:aee397456a29b492c20fda2d8961e1ffb266223625346ace14e4b6d861ba9c80", size = 271152 },
{ url = "https://files.pythonhosted.org/packages/f0/e5/96b8e55271685ddbadc50ce8bc53aa2dff278fb7ac4c2e473df890def2dc/watchfiles-1.0.4-cp313-cp313-win_amd64.whl", hash = "sha256:d6097538b0ae5c1b88c3b55afa245a66793a8fec7ada6755322e465fb1a0e8cc", size = 285216 },
]