imported posts from jekyll

master
lafleur 2 years ago
commit 55fd598405
  1. 3
      .gitmodules
  2. 6
      archetypes/default.md
  3. 45
      config.toml
  4. 6
      content/about.md
  5. 181
      content/posts/arch-on-rpi.md
  6. 62
      content/posts/local-name-resolution.md
  7. 137
      content/posts/midi-on-rpi.md
  8. 55
      content/posts/partitions-backup.md
  9. 101
      content/posts/rpi-access-point.md
  10. 117
      content/posts/rpi-alt-review.md
  11. 75
      content/posts/svg.md
  12. 135
      content/posts/transition-2.md
  13. 93
      content/posts/transition-3.md
  14. 103
      content/posts/transition-4.md
  15. 60
      content/posts/transition.md
  16. 152
      content/posts/vim-language-servers.md
  17. 30
      content/posts/vim-links.md
  18. 5
      content/search.md
  19. 48
      data/transition-3.js
  20. 59
      data/transition-4.js
  21. BIN
      static/images/icon.jpg
  22. 1
      themes/harbor

3
.gitmodules vendored

@ -0,0 +1,3 @@
[submodule "harbor"]
path = themes/harbor
url = https://github.com/matsuyoshi30/harbor

@ -0,0 +1,6 @@
---
title: "{{ replace .Name "-" " " | title }}"
date: {{ .Date }}
draft: true
---

@ -0,0 +1,45 @@
baseURL = "http://example.org/"
languageCode = "en-us"
title = "wanderings"
theme = "harbor"
[Author]
name = "la Fleur"
[outputs]
section = ["JSON", "HTML"]
[[params.nav]]
identifier = "about"
name = "About"
icon = "fas fa-user fa-lg"
url = "/about/"
weight = 3
[[params.nav]]
identifier = "tags"
name = "Tags"
icon = "fas fa-tag fa-lg"
url = "tags"
weight = 3
[[params.nav]]
identifier = "categories"
name = "Category"
icon = "fas fa-folder-open fa-lg"
url = "categories"
weight = 3
[[params.nav]]
identifier = "search"
name = "Search"
icon = "fas fa-search fa-lg"
url = "search"
weight = 3
[params.logo]
url = "icon.jpg" # static/images/icon.jpg
width = 50
height = 50
alt = "Logo"

@ -0,0 +1,6 @@
---
title: About
permalink: /about/
---
I am la Fleur, login `lafleur`. I'm a free software enthousiast. Else about me,
you will discover by scrolling this site.

@ -0,0 +1,181 @@
+++
title = "Arch on a Raspberry Pi - the basics"
date = 2020-09-26
tags = ["admin"]
toc = true
+++
Installing Arch on a Raspberry Pi is amazingly easy. the Arch ARM project gives
instructions for the basic installation [here][aw-arm-install].
## Chroot into the SD card
When you finished installing the basic system to the SD card, you may wish to
install the following packages in your host system (we'll suppose your host
system is already an Arch linux) : `arch-install-script` that provides the
`arch-chroot` command, and `qemu-user-static-bin`, that provides the means to
run ARM binaries from the host system. Then you can mount the SD card on e.g.
`/mnt` and do :
```
# arch-chroot /mnt
```
`arch-chroot` will mount all useful mounts like `/mnt/proc`, `/mnt/sys`,
`/mnt/dev`, and event `/mnt/boot` if it's in `/mnt/etc/fstab`. When you're done,
it will clean everything and hopefully let you unmount the drive cleanly.
## Setup SSH
Over there you can modify your target system, which provides a user named `pi`.
You might use that or add a user. Make sure you set up ssh login credentials. If
in doubt check [Archlinux wiki's OpenSSh page][aw-ssh] ; I believe it's
installed by default on archlinuxarm.
When you're done just hit `Ctrl-d` to unbind the chroot.
## Networking
The tricky bit is to be able to log into it on a headless config. You will have
to make sure it's reachable on the network so you can ssh into it. There are a
few options here : if the RPi provides an ethernet plug, connect over ethernet.
If not (as for the RPi zero series), connect with USB over ethernet. If you need
(or crave) to connect wirelessly, the wifi leaves you options to either make it
connect to your local network, or turn the RPi into a wifi hotspot.
Note that wireless networking does imply a certain overhead ; you might consider
turning it off when in production use, specially when running JACK (I read it
somewhere in a JACK tutorial page but I can't remember where).
### Wired over USB
Skip this part if your RPi has an ethernet socket. Else chroot in the SD card.
Add the following to `/boot/cmdline.txt` :
modules_load=dwc2,g_ether
Now into `/boot/config.txt`, add the following line :
dtoverlay=dwc2
This will let the system load an ethernet over USB module at boot-time. To have
it setup at boot-time run :
```
# systemctl enable systemd-networkd
```
And create or edit `/etc/systemd/network/01-usb.network` so that it reads :
``` systemd
[Match]
Name=usb*
[Network]
LinkLocalAddressing=ipv4
IPv4LLRoute=yes
```
get out of the chroot, unmount the SD card and boot.
### Wireless - connect to a local network
You will need the `wpa_supplicant` package, and a systemd service to launch it.
Chroot into the SD card, and run as root :
```
# pacman -S wpa_supplicant
```
`wpa_supplicant` can be started by `systemd-networkd` : edit/create
`/etc/systemd/network/05-wlan.network` with the following contents :
``` systemd
[Match]
Name=wlan*
# Run as a client with wpa_supplicant
[Network]
DHCP=ipv4
MulticastDNS=yes
```
Now edit or create `/etc/wpa_supplicant/wpa_supplicant-wlan0.conf`, and add your
local network credentials. Refer to `man wpa_supplicant.conf` for details. The
syntax is :
``` wpa-supplicant
network={
ssid="THE_NAME_OF_YOUR_ACCESS_POINT"
psk="THE_ACCESS_POINT_PASSWORD"
}
```
`systemd-networkd` will start `wpa_supplicant@wlan0.service`, who will in turn
use the latter config file to run. You can quit the chroot, unmount the SD card
and boot it ; the system should connect to the local network at boot-time. You
can add Access Points at run-time with the `wpa_passphrase` utility, like in :
```
wpa_passphrase AP_NAME AP_PASSWORD | sudo tee --append
/etc/wpa_supplicant/wpa_supplicant-wlan0.conf
```
### Wireless - fallback to hotspot
It can be very handy to fall back to setting up an Access Point if we can't
connect to any known network. This way you're still able to `ssh` back into the
RPi and for instance register a new Access Point to connect to. This trick was
derived from [this post][replace-hostapd].
`wpa_supplicant` provides this feature with a rather ... cryptic manner ; see by
yourself :
```
# AP configuration as a fallback :
network={
ssid="RPi"
mode=2
priority=-999
key_mgmt=WPA-PSK
psk="a rather long raspberry pi password"
frequency=2412
}
```
You just insert this snippet in `/etc/wpa_supplicant/wpa_supplicant-wlan0.conf`
so that it gets loaded only if the other networks are not found. It doesn't have
to sit in the bottom, so you can still append more attractive APs with
`wpa_passphrase`. Even if you don't use the `priority` option, `wpa_supplicant`
will be smart enough to load it only if no known host is found. The RPi Zero W
doesn't do 802.11g, it's only capable of 802.11b, but you don't need to tell it,
it will handle that. I actually tested all these options, because I couldn't
find documentation on these features.
But it won't give you or your client an IP address - for this a DHCP server is
needed. The RPi can act like one with the `dnsmasq` package, who will in turn
require the interface to be setup with a static address. As stated in this
[StackExchange discussion][se-static-addr], you can have several addresses on
the same interface at the same moment. So in
`/etc/systemd/network/05-wlan0.network`, append this line to the `[Network]`
section :
```
Address=192.168.2.1/24
```
We just want `dnsmasq` to run if the RPi is in Access Point mode. Systemd can
help here, you may add a conditional on `dnsmasq.service`'s execution with the
following snippet :
file: /etc/systemd/system/dnsmasq.service.d/only-if-we-are-an-AP.conf
``` systemd
# Insert a test that fails if the device is not an Access Point.
[Unit]
After=wpa_supplicant@wlan0.service
[Service]
ExecStartPre=/bin/sh -c 'eval $(wpa_cli -i wlan0 status) && test "$mode" == "AP"'
[Install]
WantedBy=wpa_supplicant@wlan0.service
```
If you want to know what it does, try `sudo wpa_cli -i wlan0 status` ; you'll
see it outputs various informations about the network device, including its
mode, with possible values `AP` and `managed`. The above test succeeds only if
the interface is configured as an Access Point. And Systemd services fail if any
of the ExecStartPre instructions fail. The drawback of this setup is that the
service will simply appear as failed in journalctl, with no explanation.
Copy the snippet to the given location, reboot. If you can't ping the RPi on the
local network, look for it in the available APs. Connect to it, ssh it. You're
done !
[aw-arm-install]: https://archlinuxarm.org/platforms/armv6/raspberry-pi
[aw-ssh]: https://wiki.archlinux.org/index.php/OpenSSH
[replace-hostapd]: https://raspberrypi.stackexchange.com/questions/94970/replacing-hostapd-with-wpa-supplicant
[se-static-addr]: https://unix.stackexchange.com/questions/308944/systemd-networkd-default-static-address-if-dhcp-fails

@ -0,0 +1,62 @@
+++
title = "Local Name Resolution in Arch"
date = 2020-09-26
tags = ["admin"]
+++
It's quite handy to be able to access clients connected at the same Access Point
as you using their hostname. There are a few technologies to do that : mostly,
Apple's Bonjour translates as mDNS in the Linux world, then there is Microsoft's
NetBIOS protocol, that is provided by samba under Linux but not running as a
default. And Microsoft also created the LLMNR resolution protocol, that ships
with Windows starting at Vista (and with 7, 8 and 10).
To have local name resolution, your best option is probably `systemd-resolved`,
since it does both mDNS and LLMNR. On the other side, avahi does mDNS and lets
you advertise specific services on the network. If you're interested in this you
might want to go for it ; check [Archwiki's page on network
configuration][aw-net-conf]. Here we will deal with `systemd-resolved`.
The first thing to do is to disable avahi services, since they interfere with
`systemd-networkd`. Go on and
```
# systemctl disable avahi-daemon
```
Now we need to check how you do domain resolution on your computer. Under Linux
this is dependent on who manages `/etc/resolv.conf`. If it's a symlink to some
NetworkManager folder, it's NetworkManager. If it's linked to some Systemd
folder, then it's `systemd-resolved`. If it is NetworkManager and you want to
switch to `systemd-resolved` and keep NetworkManager to manage connections, just
do :
```
# ln -sf /run/systemd/resolve/stub-resolve.conf /etc/resolv.conf
```
It may also be a real file, meaning your computer uses static IP addresses for
DNS servers. In this case you can do the same (back it up if you will).
Edit `/etc/systemd/resolved.conf` so that inside of the `[Resolve]` section, you
have `MulticastDNS=yes` and `LLMNR=yes`. After that, you will have to
```
# systemctl enable systemd-resolved
```
And reboot.
Now run `resolvectl` to see how it goes. If your network interface doesn't
specifically allow MulticastDNS, you will have to tell the network manager to do
it. If it's `systemd-networkd`, you will need to add the very same option in the
relevant `.network` files in `/etc/systemd/network/`. If it's NetworkManager, no
luck, you will have to update each and every connection by adding `mdns=2` to
the `[connection]` section of the connection definition in
`/etc/NetworkManager/system-connections`.
If you changed anything, you might want to reboot again. Then try
```
$ resolvectl query SOMEHOSTNAME.local
```
SOMEHOSTNAME being the name of a device you know is connected. It should answer
through mDNS. You may also try `resolvectl query SOMEHOSTNAME`, skipping the
`.local` extension ; it should answer with LLMNR. Do note that Android devices
don't seem to show up on the local network. But they do see declared neighbours
; I suspect they ship with LLMNR since they're only able to `ping` names without
the `.local` extension.
[aw-net-conf]: https://wiki.archlinux.org/index.php/Network_configuration#Local_network_hostname_resolution

@ -0,0 +1,137 @@
+++
title = "Arch on a Raspberry Pi - sound and MIDI"
date = 2020-09-26
tags = ["admin"]
toc = true
+++
This post follows a first one on [basics of Arch onto a Raspberry
Pi][arch-basics]. Now we will see how to have high quality, low latency sound
and a MIDI keyboard on this beautiful but still useless system.
## MIDI keyboard with JACK
To reliably output sound using a MIDI keyboard and have a reasonable latency,
consider dropping pulseaudio, installing and setting up JACK, and setting up
fluidsynth to use JACK.
### Disable pulseaudio's autostart with systemd
Run as the relevant user :
$ systemctl --user mask pulseaudio.socket
### Setup JACK
First install the `jack2` package, and enable it at login with :
$ systemctl --user enable jack@lowlatency.service
The `@` argument sets a JACK configuration file that will be searched in the
following places, first match wins :
~/.config/jack/lowlatency.conf
/etc/jack/lowlatency.conf
If you use a DAC, drop a second conf file in `~/.config/jack/MYDAC.conf`. For
instance HifiBerry-style DACs are identified as device `hw:sndrpihifiberry`.
In this case create `~/.config/jack/hifiberry.conf` as follows, and instead of
the former, enable `jack@hifiberry.service` :
# The name of the JACK server
JACK_DEFAULT_SERVER="default"
# Options to JACK (e.g. -m, -n, -p, -r, -P, -t, -C, -u, -v)
JACK_OPTIONS=""
# Audio backend (e.g. alsa, dummy, firewire, netone, oss, portaudio)
DRIVER="alsa"
# Device name (used by the audio backend) - defaults to "hw:0"
DEVICE="hw:sndrpihifiberry"
# Specific settings for the audio backend in use
DRIVER_SETTINGS="-n2 -p128 -r48000"
Note that `JACK_OPTIONS` is empty ; all the useful options go in
`DEVICE_SETTINGS`. I suppose it's a bug of the JACK service file.
More about DACs at [the end of the post](#use-an-external-sound-card).
### Setup fluidsynth
Install the `fluidsynth` package ; it comes with a default user service that
will connect to the JACK service if you tell it to in its conf file
`~/.config/fluidsynth`. First do :
```
$ systemctl --user enable fluidsynth
```
You will need a soundfont in `/usr/share/soundfonts/default.sf2`. I actually
made it a symbolical link to a soundfont file in my home dir. If you're
desperate about soundfonts install the `soundfont-fluid` package.
JACK2 is said to need package `a2jmidid` to detect MIDI keyboards. I've noticed
that in fact `fluidsynth` was very capable of detecting them on its own with the
option `-o midi.autoconnect=1`, be it at boot or hotplugged.
A useful feature is that it can read a command file with the -f option ; an
arguably preferable strategy is to create and load one in
/usr/share/soundfonts/fluidsynth.command
Finally my `~/.config/fluidsynth` reads as follows :
# Mandatory parameters (uncomment and edit)
SOUND_FONT=/usr/share/soundfonts/default.sf2
# Additional optional parameters (may be useful, see 'man fluidsynth' for further info)
# -C0 disables chorus
# -R0 disables reverb
# -g0.2 would be the default gain
# -m can be jack, alsa, alsa_seq or raw ? jack should be more efficient
# -K number of MIDI channels (minimum 16)
# -f command file input
OTHER_OPTS="-a jack -j -f /usr/share/soundfonts/fluidsynth.command -o midi.autoconnect=1 -o synth.polyphony=64"
Its systemd service's default target used to be "multi-user" whereas it should
have been "default" (bug resolved as of version 2.1.5-2). One can now enable it
with
On older versions of the program, you had to do instead : `systemctl --user
add-wants default fluidsynth`
## Autostart
If you want the systemd user services started at boot do _as root_ :
```
# loginctl enable-linger YOURUSERNAME
```
## Deal with latency
To optimise latency, you will want to use a realtime process for jackd.
You can follow [instructions for Arch][aw-instr] to have the right to make your
process prioritary on system tasks. Eventually, you should belong to the
`realtime` group. After that jackd should detect it's able to run a realtime
process and proceed. You can check that it does with
```
journalctl --user -u jack@hifiberry -e
```
(replace `hifiberry` with the config name you gave at the beginning of this
post). The `-e` option jumps at the end of the logs.
I was only able to use the alsa-seq MIDI plugin with JACK, but some people
report more effectiveness with jack's own plugin. You can check [Archwiki's
JACK page][aw-jack-midi] for detailed instructions.
Another [Archwiki page on professional audio][aw-pro-audio] mentions a
`-Xalsarawmidi` option in JACK used with a2jmidid to enhance MIDI jitter.
## Use an external sound card
For Phat DACs, follow these [instructions][dac-instr]. What's a Phat ? It's a
small cardboard that fits above a Raspberry Pi, plugged into its IO pins. And a
DAC ? Digital to Analog Converter - in other words, an audio card.
[arch-basics]: {% post_url computing/2020-09-26-Arch-on-RPi %}
[aw-instr]: https://wiki.archlinux.org/index.php/Realtime_process_management#PAM-enabled_login
[aw-jack-midi]: https://wiki.archlinux.org/index.php/JACK_Audio_Connection_Kit#MIDI
[aw-pro-audio]: https://wiki.archlinux.org/index.php/Professional_audio#MIDI
[dac-instr]: https://www.hifiberry.com/docs/software/configuring-linux-3-18-x/

@ -0,0 +1,55 @@
+++
title = "disks backup"
date = 2020-09-16
tags = ["admin"]
+++
Backing up a whole disk is always quite sensible ; you rarely have the occasion
to test if your backup really works. Besides, the logical architecture of a disk
and its partitions can be quite sophisticated, with numerous different options,
that vary much between operating systems. Here we will describe how to backup
and restore a disk with some primary partitions. We will assume it is a bootable
disk with a Master Boot Record at the very beginning of it (MBR is limited to
446 bytes by conception). Note that most recent machines boot in UEFI mode,
which is not covered here.
## Backup a bootable disk
Let's say your disk is called sdX.
### Backup Master Boot Record
The MBR is a portion of bootable binary code. Do not try to read it with any
text editor ! They may slightly modify it, rendering it unbootable.
$ dd if=/dev/sdX of=path/to/disk_backup.mbr bs=446 count=1
### Backup partition table
$ sfdisk -d /dev/sdX > path/to/disk_backup.partition_table.txt
### Backup useful partitions
For each partition (numbered Y) :
$ dd if=/dev/sdXY of=path/to/partition_backup.dd status=progress
Compressed variant :
$ dd if=/dev/sdXY status=progress | gzip > path/to/partition_backup.dd.gz
## Restore a backed up disk into sdX
Edit `path/to/disk_backup.partition_table.txt` to reflect the partitions you
will actually restore. Then :
$ dd if=path/to/disk_backup.mbr of=/dev/sdX
$ sfdisk /dev/sdX < path/to/disk_backup.partition_table.txt
You might have to eject and re-plug the disk to see the added
partitions. Then for each of those :
$ zcat if=arch_zero_pY.dd.gz | dd of=/dev/sdXY
And finally restore MBR :
$ dd if=path/to/disk_backup.mbr of=/dev/sdX bs=446 count=1

@ -0,0 +1,101 @@
+++
title = "Access Point on a Raspberry Pi"
date = 2020-09-26
tags = ["admin"]
+++
The Raspberry Pi family who have wireless capabilities can all serve as wifi
Access Points - meaning some other devices connect to it, perhaps accessing
internet through it. We'll leave the "forward internet" part for another
time, and concentrate on serving wifi connections as a hotspot.
If what you need is a "rescue" Access Point to be able to log back into the
machine, you might rather check my other article on [RPi basics][arch-on-rpi],
where you will see how to let `wpa_supplicant` fall back to being an Access
Point if it can't connect to any.
And for now let's use `hostapd`, a package to implement and advertise a wifi
access point. It might be a better option for production use since it's a
dedicated tool, whereas `wpa_supplicant` is a bit out of its league here.
`hostapd` needs the interface to be up and configured with an IP. So we'll
configure `systemd-networkd` to do that, and by the way propose an IP address to
clients that get connected to our AP. Change
`/etc/systemd/network/05-wlan.network` so that it reads :
``` systemd
[Network]
Address=192.168.1.1
DHCPServer=true
IPMasquerade=true
IPForward=true
[DHCPServer]
PoolOffset=100
PoolSize=20
EmitDNS=true
```
Now `hostapd` won't work (at least in this configuration) if you have
`wpa_supplicant` installed. Go on and
```
# pacman -Rs wpa_supplicant
```
And last, you will need to tweak `/etc/hostapd/hostapd.conf`. This one was taken
from [Archwiki's][aw-ap], and curated for RPi zero W :
```
interface=wlan0
#bridge=br0
# Country code (ISO/IEC 3166-1)
country_code=FR
# SSID to be used in IEEE 802.11 management frames
ssid=zero
wpa_passphrase=YOUR_PASSWORD_HERE
# Driver interface type (hostap/wired/none/nl80211/bsd) - default hostapd
#driver=rtl871xdrv
#driver=nl80211
# Operation mode (a = IEEE 802.11a (5 GHz), b = IEEE 802.11b (2.4 GHz)
# RPi zero W only supports b
hw_mode=b
# Channel number
channel=5
# Maximum number of stations allowed
max_num_sta=5
# Bit field: bit0 = WPA, bit1 = WPA2
wpa=2
# Bit field: 1=wpa, 2=wep, 3=both
auth_algs=1
# Set of accepted cipher suites; disabling insecure TKIP
wpa_pairwise=CCMP
# Set of accepted key management algorithms
wpa_key_mgmt=WPA-PSK
# hostapd event logger configuration
logger_stdout=-1
# 0 = verbose debugging
# 1 = debugging
# 2 = informational messages
# 3 = notification
# 4 = warning
logger_stdout_level=2
## QoS support
#wmm_enabled=1
## Use "iw list" to show device capabilities and modify ht_capab accordingly
#ht_capab=[HT40+][SHORT-GI-40][TX-STBC][RX-STBC1][DSSS_CCK-40]
ht_capab=[HT20][TX-STBC1][DSSS_CCK-40]
```
If you use a Realtek-based wifi adapter, there is a specific hostapd package for
hosts using rtl871 hardware. You will have to install it from the AUR ; you will
have to
```
$ yay -S hostapd-rtl871xrdv
```
_And_ modify `/etc/hostapd/hostapd.conf` to mention `driver=rtl871xdrv`.
[arch-on-rpi]: {% post_url computing/2020-09-26-Arch-on-RPi %}
[aw-ap]: https://wiki.archlinux.org/index.php/Software_access_point

@ -0,0 +1,117 @@
+++
title = "2020 review of Raspberry Pi alternatives"
date = 2020-10-06
tags = ["admin"]
toc = true
+++
The Raspberry Pi is a cardboard-computer that lets you run your own operating
system. Or put it the other way around, an open computer the size of a
cigarettes pack (depending on what kind of cigarettes you refer to). But it's
quite a barebone system, mostly lacking proper ADC and DAC, and battery
management. Extensions allow to stack those features, but they're overall quite
expensive. It might be time to consider alternatives to the rPi Zero in the same
... hem league ?
## rPi Zero features
The smallest form factor is the Raspberry Pi Zero at 60x32mm. The RPi Zero w is
a wifi+bluetooth model for a 40% increase in price, topping at ... €11.
The rPi Zero roughly shares its processor with the initial rPi, that is a
Broadcom BCM2835 SoC, with an ARMv6 core. Those were the first ARMs to support
floating point calculations.
The various available models all lack an ADC and a proper DAC, although most do
have an audio output that can do CD-like audio quality using PWM modulation -
that is, pulsing alternative 0s and 1s so quickly it feels like an analog output
on an average (see [this rPi post][rpi-audio] on rPi audio quality).
Power management is also quite barebone : you power them through a micro-USB
port and they can't control a battery's charging state. You can plug a battery
rescue pack to feed it, but then you will have to charge it separately, and if
you ever want to craft a case too put it in, this will hardly fit into the same
proportions.
Various constructors provide extensions called pHats that stack on the rPi using
its GPIO pins. If you need a self-contained system with a battery and power
control, you will need a pHat specialized in power management, like
[pibat][pibat] that bills $26 for 1600mAh, or [pisugar2][pisugar2], very neat
but $40 worth for 900mAh. Another interesting extension is the [UPS power
expansion board][ups], hosting 1Ah for $20. The PiHut also sells a [LiPo
shim][shim] to keep the functionality packed.
## Banana Pi M2
Banana Pi obviously tries to challenge the features of the Raspberry Pi. The
firm developed the [bPi M2][bpi-m2-wiki] that's a revamped version of the rPi
Zero, featuring a ARMv7 quad-core processor, and a soft power-on button. It's
$18 worth at [Aliexpress][bpi-m2].
## Arietta
Acme Systems propose the [Arietta][acme-arietta]. This board sports the
Microchip AT91SAM9G25 processor, based on the ARM926 core, which belongs to the
ARMv5 series. Acme assures customers that it is guaranteed to run the latest
linux kernel for quite long. It is supported by the ArchARM distribution. Acme's
site offers a €25 basic model.
## NanoPi
The [NanoPi NEO][nanopi] features an Allwinner H3 processor, with a quad-core
cortex-A7 ARM core. It's 40x40mm large, and has an ethernet and a USB-A socket.
It seems able to do basic power management, according to notes in
[nanopi.io][nanopi.io]. [friendlyARM's wiki][nanopi-wiki] states that the nanopi
has a builtin audio codec and that it should output sound on the lineout pins.
Its price is reported between $10 and $50.
There also is a NanoPi AIR with wifi instead of ethernet and USB sockets,
available for $20 at [friendlyARM][nanopi-air]. Specs are otherwise similar,
it's just slimmer.
## Banana Pi D1
But then there is the [bPi D1][bpi-d1-wiki]. This challenger is 38x38mm big, and
ships with an HD camera, analog audio in and out pins, power control management
and a 3.7V li-ion battery socket, a power switch and a user-definable soft
button, RTC (basically, a clock that remembers time between boots), a
microphone, and a CMOS image sensor (I don't even know what this means). I saw
it at $20 at [Aliexpress][bpi-d1].
This is really a huge feature pack for such a small package. The question that
rises is : what will we be able to run on this platform ? The processor is an
Anyka AK3918, with an ARM chipset the generation just before rPi Zero, and a
bunch of peripherals as stated in the [specs][ak3918] pdf description. Looking
into it, I found out it uses the same ARM core (ARM926EJ is an ARM9, which is
part of ARMv5) as the [Olinuxino][olinuxino] board from Olimex, and the same
amount of RAM (64MB). The latter is supported in [ArchARM][olinuxino-archarm],
with a caveat due to the limited RAM : localegen would need swap, so there's no
UTF-8 support. I wonder if distcc would help here. Mainline Arch linux does
provide a cross-platform distcc installation : `distccd-alarm-armv5`. But this
doesn't prove Arch will run on Anyka's chip. ArchARM install instructions for
various ARMv5 boards imply installing uboot in the boot sector. This should
work, as it relies on pretty standard settings.
I don't know what to expect from the audio hardware capabilities either, since
they're embedded into Anyka's chip, and probably differ from Olinuxino's i.MX233
chip audio hardware. I'll keep this article up with any information I might get
on this subject.
## Conclusion
The bPi D1 seems to be the most fitted product, but there is no guarantee it
will run your preferate OS. The NanoPi seems to be the next interesting choice,
but then you will have to deal with power management. In the end, the rPi Zero
is still a favorable choice, years and years after its design.
[rpi-audio]: https://www.raspberrypi.org/forums/viewtopic.php?t=195178
[pibat]: https://www.ebay.fr/itm/PiBat-Raspberry-Pi-Zero-W-1600mAh-LiPo-Battery-Shield-5-6-Hours-of-autonomy/283842482176
[pisugar2]: https://www.tindie.com/products/pisugar/pisugar2-battery-for-raspberry-pi-zero/
[ups]: https://www.alibaba.com/product-detail/Raspberry-Pi-Zero-UPS-Power-Expansion_62093794216.html
[shim]: https://thepihut.com/products/lipo-shim?ref=isp_rel_prd&isp_ref_pos=2
[nanopi]: https://www.alibaba.com/product-detail/Open-Source-H3-Quad-core-Cortex_62093676606.html
[nanopi.io]: http://nanopi.io/nanopi-neo-air.html
[nanopi-air]: https://www.friendlyarm.com/index.php?route=product/product&path=69&product_id=151
[nanopi-wiki]: http://wiki.friendlyarm.com/wiki/index.php/NanoPi_NEO_Air#Custom_welcome_message
[bpi-m2-wiki]: http://wiki.banana-pi.org/Getting_Started_with_M2_Zero
[bpi-m2]: https://www.aliexpress.com/item/32839074880.html
[bpi-d1-wiki]: http://wiki.banana-pi.org/Banana_PI_D1
[bpi-d1]: https://www.aliexpress.com/item/32240699956.html
[ak3918]: https://drive.google.com/file/d/0B4PAo2nW2KfnbGJpa19FWWpwWDQ/view
[olinuxino]: https://www.olimex.com/wiki/IMX233
[olinuxino-archarm]: https://archlinuxarm.org/platforms/armv5/olinuxino
[acme-arietta]: https://www.acmesystems.it/arietta

@ -0,0 +1,75 @@
+++
title = "SVG"
date = 2020-04-13
tags = ["html"]
+++
Pour intégrer un fichier SVG et pouvoir le redimensionner, on peut le mettre
dans un conteneur HTML. Il va s'arranger pour tout afficher et centrer le
résultat, comme ça :
``` html
<span class="svgContainer" style="background-color: darkgrey;">
<svg viewbox="0 0 100 100" width="20em" height="10em">
<path fill="magenta" stroke="black" d="M0,0 H100 V100 H0 Z">
</svg>
</span>
```
Centrage vertical :
``` html
<span class="svgContainer" style="background-color: darkgrey;">
<svg viewbox="0 0 100 100" width="10em" height="20em">
<path fill="magenta" stroke="black" d="M0,0 H100 V100 H0 Z">
</svg>
</span>
```
Si on veut qu'il "stretche" sur son conteneur, on peut ajouter l'option
`preserveAspectRatio = none` :
``` html
<span id="svgContainer">
<svg viewbox="0 0 100 100" width="20em" height="10em"
preserveAspectRatio="none">
<path fill="magenta" stroke="black" d="M0,0 H100 V100 H0 Z">
</svg>
</span>
```
# Position et taille
L'élément SVG a des attributs transitionables, comme `svg.width` et `svg.height`,
`svg.style.left` et `svg.style.top`. En utilisant le mode `position = relative`,
on peut déplacer l'élément tout en gardant sa place initiale réservée, comme si
rien n'avait changé :
``` html
<span class="svgContainer" style="background-color: darkgrey;">
<svg viewbox="0 0 100 100" width="10em" height="10em"
style="position: relative; left: -3em; top: 1.5em;">
<path fill="magenta" stroke="black" d="M0,0 H100 V100 H0 Z">
</svg>
</span>
```
On peut enfin utiliser le mode `position = absolute`, où le conteneur est
arraché au flot du document. Il est positionné relativement à son plus proche
parent positionné. La plupart des éléments ont par défaut `position = static`,
qui la laisse à sa place dans le flot du document. Donc le positionnement se
fait le plus souvent par rapport à l'élément `<body>`.
Ici l'élément est `absolute` et son parent direct `relative`. On remarque que le
span svgContainer a maintenant une hauteur nulle ; en fait en position
`absolute`, le svg n'est plus contenu dans svgContainer mais dans `<body>` ;
svgContainer est donc vide.
``` html
<span class="svgContainer"
style="background-color: darkgrey; position: relative">
<svg viewbox="0 0 100 100" width="10em" height="10em"
style="position: absolute; left: 35vw; top: -4vw;">
<path fill="magenta" stroke="black" d="M0,0 H100 V100 H0 Z">
</svg>
</span>
```

@ -0,0 +1,135 @@
+++
title = "transitions II - javascript"
date = 2020-04-13
# layout: code
+++
<script>
function move_into(el, destEl) {
let destStyle = window.getComputedStyle(destEl);
let destHeight = destStyle.height;
let startPos = el.getBoundingClientRect();
console.log('start point', startPos.left, startPos.top);
/*
destEl.style.backgroundColor = "grey";
destEl.style.position = "relative";
destEl.style.height = destEl.offsetHeight + "px";
destEl.style.width = destEl.offsetWidth + "px";
destEl.style.transition = "all 1s";
void destEl.offsetLeft;
//destEl.focus();
// DEBUG Trigger transition on destination ?
destEl.appendChild(el);
*/
destEl.appendChild(el);
destFinalHeight = destEl.offsetHeight;
console.log(destHeight, destFinalHeight);
destEl.style.transition = "0s";
destEl.style.height = destHeight;
void destEl.offsetHeight;
destEl.style.transition = "1s";
destEl.style.height = destFinalHeight + "px";
let endPos = el.getBoundingClientRect();
console.log("way to go", endPos.left-startPos.left, endPos.top-startPos.top);
// We are in final position, we need to move back to inital pos.
el.style.position = "relative";
el.style.left = startPos.left - endPos.left + "px";
el.style.top = startPos.top - endPos.top + "px";
// Let javascript notice current state :
void el.offsetLeft;
el.addEventListener('transitionend', function log() {
el.removeEventListener('transitionend', log);
console.log("après", destEl.offsetHeight);
});
// Trigger transition : move to final position.
el.style.transition = "all 1s";
el.style.left = "0";
el.style.top = "0";
}
</script>
On veut maintenant déplacer un div dans un autre avec une transition sur le
déplacement. Ppur ça, on va dans l'ordre :
- récupérer les coordonnées de départ par rapport à la racine du document
- arracher l'élément et le mettre dans le conteneur d'arrivée
- le basculer en position relative et lui passer les coodonnées de départ
- déclencher le déplacement jusqu'à sa position dans le conteneur d'arrivée
{% highlight js linenos %}
function move_into(el, destEl) {
let startPos = el.getBoundingClientRect();
console.log('start point', startPos.left, startPos.top);
destEl.appendChild(el);
let endPos = el.getBoundingClientRect();
console.log("way to go", endPos.left-startPos.left, endPos.top-startPos.top);
// We are in final position, we need to move back to inital pos.
el.style.position = "relative";
el.style.left = startPos.left - endPos.left + "px";
el.style.top = startPos.top - endPos.top + "px";
el.style.transition = "all 1s";
// Let javascript notice current state :
void el.offsetLeft;
// Trigger transition : move to final position.
el.style.left = "0";
el.style.top = "0";
}
{% endhighlight %}
Si vous lisez l'anglais, ce qu'on vient de voir devrait être assez lisible. Le
plus obscur est certainement l'utilisation de `void`. Ligne 16, On utilise la
commande
{% highlight js %}
void el.offsetLeft;
{% endhighlight %}
pour obliger javascript à constater l'état de
départ, avant de rentrer les coordonnées d'arrivée. En effet, sinon javascript
ne voit pas le changement de valeur des coordonnées, il ne voit que la valeur
finale. Et dans ce cas, pas d'animation de la transition, on va directement aux
valeurs finales. Voilà le fragment de html et sa prévisualisation :
``` html
<style>
.half {
display: inline-block;
width: 45%;
}
.large {
height: 8em;
}
</style>
<div class="half">
<button onclick="move_into(fleche, cible)">
bouger la flèche
</button>
<div id="cible">
texte dans la cible
</div>
</div>
<div class="half">
<div class="large">
un div pour prendre un peu de place
</div>
<div id="fleche" class="large">
texte dans la flèche
</div>
</div>
```
Je dois dire que je trouve assez frustrant de ne pas pouvoir déclencher les
transitions sur les éléments impactés. J'ai essayé, je n'ai rien trouvé de
concluant.
[la suite]({% post_url html/2020-04-13-transition-3 %})

@ -0,0 +1,93 @@
+++
title = "transitions III - css"
date = 2020-04-13
# layout: code
+++
<script src="{% link html/transition-3.js %}" type="text/javascript"></script>
On a la possibilité de donner des réglages par défaut à nos transitions en css ;
ça permet d'alléger le javascript, et de contrôler les paramètres de la
transition depuis le css.
Ici `move_into(el, destEl)` va utiliser une classe qui durera le temps de la
transition, qui s'appelera `transition-deplacement`. On pourra y stocker des
valeurs par défaut pour la transition. Par exemple,
{% highlight js %}
el.style.top = "";
{% endhighlight %}
donnera à l'élément `el` la valeur `top` décrite dans le css. Comme `el`
appartient à la classe `transition-deplacement` qui dit que `top = 0;` eh ben
voilà.
{% highlight js %}
function move_into(el, destEl) {
if(el.classList.contains('transition-deplacement')) {
console.log('! already movin');
} else {
el.classList.add('transition-deplacement');
// Do the job
el.addEventListener('transitionend', function declasse() {
el.removeEventListener('transitionend', declasse);
el.classList.remove('transition-deplacement');
console.log("élément", el, "déclassé");
});
}
{% endhighlight %}
Pour finir, `toggle(el, destEl)` utilisera une autre classe pour déterminer si
`el` a déjà été ouvert, la classe `ouvert`. Si oui, il le mettra dans le parent
direct de `destEl`.
Dans ce cas, on n'utilise même pas cette classe dans le css. Elle ne sert qu'à
javascript.
{% highlight js %}
function toggle(el, destEl) {
if(el.classList.contains('ouvert')) {
el.classList.remove('ouvert');
move_into(el, destEl.parentElement);
} else {
el.classList.add('ouvert');
move_into(el, destEl);
}
}
{% endhighlight %}
Tous ces points sont dans le fichier javascript inclus dans la page à partir de
[]({% link html/transition-3.js %}). Et maintenant le html et la
prévisualisation :
``` html
<style>
.transition-deplacement {
position: relative;
transition: 1s;
left: 0;
top: 0;
}
.min {
display: inline-block;
width: 10em;
height: 5em;
}
</style>
<div>
<button onclick="toggle(fleche, cible)">
bouger le div 1
</button>
<div id="cible" class="min">
texte de la cible
</div>
<div class="min">
un div pour prendre un peu de place
</div>
<div id="fleche" class="min">
texte de la flèche
</div>
</div>
```
[la suite]({% post_url html/2020-04-13-transition-4 %})

@ -0,0 +1,103 @@
+++
title = "transition IV - SVG"
date = 2020-04-13
# layout: code
+++
<script src="{% link html/transition-4.js %}" type="text/javascript"></script>
Et si c'est un objet SVG qu'on veut déplacer ? Ça fonctionne encore, en
utilisant `svg.getBoundingClientRect()` pour récupérer la position, et la même
fonction pour initialiser la position. Pour clarifier, on crée des fonctions qui
font le tri en fonction du type de l'élément : `initialize_state(el)` et
`tell_coords(el)`.
{% highlight js %}
function tell_coords(el) {
var coords = new Object();
if(el.viewBox === undefined) {
coords["x"] = el.offsetLeft;
coords["y"] = el.offsetTop;
} else {
let geom = el.getBoundingClientRect();
coords["x"] = geom.left;
coords["y"] = geom.top;
}
return coords;
}
function initialize_state(el) {
if(el.viewBox === undefined) {
void el.offsetWidth;
} else {
el.getBoundingClientRect();
}
}
{% endhighlight %}
Ensuite `move_into(el, destEl)` fait le même travail qu'avant, mais
en appelant les fonctions qu'on a construites.
{% highlight js %}
function move_into(el, destEl) {
if(el.classList.contains('transition-deplacement')) {
console.log('! already movin');
} else {
el.classList.add('transition-deplacement');
let coords_depart = tell_coords(el);
console.log('déplacement depuis', el.parentElement);
//console.log('déplacement depuis ', coords_depart);
destEl.appendChild(el);
el.style.position = "relative";
let coords_arrivee = tell_coords(el);
console.log("destination", destEl);
//console.log("destination ", coords_arrivee);
el.style.transition = "all 0s";
el.style.left = coords_depart["x"] - coords_arrivee["x"] + "px";
el.style.top = coords_depart["y"] - coords_arrivee["y"] + "px";
el.addEventListener('transitionend', function declasse() {
el.removeEventListener('transitionend', declasse);
el.classList.remove('transition-deplacement');
console.log("élément", el, "déclassé");
});
initialize_state(el);
el.style.transition = ""; // Reset to css value.
el.style.left = "";
el.style.top = "";
}
}
{% endhighlight %}
Le javascript inclus complet est toujours [là][js].
Enfin le html :
```html
<style>
span {
display: inline-block;
}
.minimum {
width: 10em;
height: 3em;
}
.transition-deplacement {
position: relative;
top: 0;
left: 0;
transition: 1s;
}
</style>
<div>
<button onclick="toggle(fleche, cible)">bouger</button>
<span class="minimum">
un span pour prendre un peu de place
</span>
<span id="cible" class="minimum">
texte du span d'arrivée
</span>
<svg id="fleche" viewBox="0 0 100 100">
<path d="M0,0 H100 V100 H0 Z" fill="transparent" stroke="black" />
</svg>
</div>
```
[js]: {% link html/transition-4.js %}

@ -0,0 +1,60 @@
+++
tite = "transition"
date = 2020-04-13
# layout: code
+++
<script>
function move_span() {
let span = document.querySelector("#movableSpan");
if(span.classList.contains('slided')) {
span.classList.remove('slided');
} else {
span.classList.add('slided');
}
}
</script>
Voilà un sujet délicat. Les transitions permettent de changer un paramètre de
manière progressive sur un temps donné.
Pour créer ces effets avec accélération matérielle, il faut les écrire comme
effets css. Il y a pour ça les paramètres `transitionProperty` et
`transitionDuration`, avec le paramètre abrégé `transition`. La transition est
déclenchée la prochaine fois qu'un élément prend la classe impliquée. Par
exemple :
``` html
<script>
function move_span(span) {
let span = document.querySelector("#movableSpan");
if(span.classList.contains('slided')) {
span.classList.remove('slided');
} else {
span.classList.add('slided');
}
}
</script>
<style>
#movableSpan {
position: relative;
top: 0;
left: 0;
transition: 1s;
}
.slided {
transition: 1s;
top: 5em !important;
}
</style>
<div>
<button onclick="move_span()">bouger le span 1</button>
<span id="movableSpan" >
texte du span 1
</span>
<span>
texte du span 2
</span>
</div>
```
[la suite]({% post_url html/2020-04-13-transition-2 %})

@ -0,0 +1,152 @@
+++
title = "Language Server Protocols in Vim"
date = 2020-09-16
tags = ["admin", "vim"]
+++
Language Server Protocol aka LSP is an open source initiative of ... Microsoft.
Each "language server" defines every detail of a programming language's syntax
and grammar, like its keywords, and how you put them together. This knowledge
base is in turn used by editor helpers who can for instance lint your code or
suggest completion, a field of features labeled "Intellisense".
Since Vim 8, programmers may run asynchronous code in vimscript. This feature is
leveraged in some plugins that use LSP to lint or suggest while you type. Let's
name [vim-lsp][vim-lsp], [coc.vim][coc.vim] and [vim-ale][vim-ale].
## Vim plugins on your own
It is worth noting that coc.vim provides a language server installer (vim-lsp
also has `vim-lsp-settings` that _should_ do the job, although I was not able to
run it on my system). It is very likely that your operating system chips some,
at least if it's a linux. _But_ if you already have Vim running on your OS, it
should be quite simple to use one of the formers with a plugin manager. If you
don't have any plugin manager, be advised that Vim 8 ships its own plugin system
; you just need to unpack the plugin's main directory in
`~/.vim/pack/PACK_NAME/start/`. `PACK_NAME` can be anything you want, and its
subdirectory `start` can be filled with as many plugins as you please. In this
case, you could do for instance :
```
$ mkdir -p ~/.vim/pack/LSP/start
$ cd ~/.vim/pack/LSP/start
$ git clone https://github.com/neoclide/coc.nvim
```
And that's it ! (If you don't have git neither a terminal but still have Vim,
you can still go at [coc.vim][coc.vim] and manually unpack the latest version's
tarball you Vim's config dir - `pack` should lie next to `ftplugin` et al).
## Vim plugins in your package manager
You might prefer to use your system's package manager to install plugins, so
that they get upgraded with the rest of the system. In this case you could do
for instance in [Arch linux](http://archlinux.org) :
```
# pacman -S vim-ale
```
Once properly set up, vim-ale will lint the file you are editing in the
background (using Vim 8 async processes). It will send all errors and warnings
found while you type to the location window, a temporary "subwindow" that you
can check back with `:lw`. See `:help location-list-window` to get more info on
this feature. You can jump to the last diagnosed error with `:ll`, and navigate
with `:lla` and `:lne`.
The nice bonus is the completion option. It lists all matching expressions with
what you began typing, and as you scroll the list, Vim's `preview-window` pops
up and gives a description of whatever you are coding - objects in python,
functions in bash, etc.
I'm afraid vim-ale on its own doesn't know much about any language at all. You
will have to feed it with a knowledge provider for each of the languages you
care for. See the next chapter about this.
Meanwhile, I tweaked vim-ale to populate Vim's default completion sequence,
remap completion shortcuts, and let the preview window be opened at the bottom
with the following content into `~/.vim/plugin/ale.vim` :
``` vimscript
" vim-ale settings
" Enable ale completion where available.
" This setting must be set before ALE is loaded.
let g:ale_completion_enabled = 1
let g:ale_completion_autoimport = 1
" Lets ctrl-x ctrl-o open ale completion
set omnifunc=ale#completion#OmniFunc
" Remap tab, shift-tab and enter to useful completion shortcuts when
" completion already began :
inoremap <expr> <Tab> pumvisible() ? "\<C-n>" : "\<Tab>"
inoremap <expr> <S-Tab> pumvisible() ? "\<C-p>" : "\<S-Tab>"
inoremap <expr> <cr> pumvisible() ? "\<C-y>" : "\<cr>"
" Let preview window open on the bottom (J)
augroup previewWindowPosition
au!
autocmd BufWinEnter * call PreviewWindowPosition()
augroup END
function! PreviewWindowPosition()
if &previewwindow
wincmd J
endif
endfunction
```
## Installing language servers
You could install your favourite language's language server. For example under
Arch :
# pacman -S bash-language-server
And then let Vim know about it. Create `~/.vim/plugin/language-servers.vim` and
paste :
``` vimscript
" Support for bash
if executable('bash-language-server')
augroup LspBash
autocmd!
autocmd User lsp_setup call lsp#register_server({
\ 'name': 'bash-language-server',
\ 'cmd': {server_info->[&shell, &shellcmdflag, 'bash-language-server start']},
\ 'allowlist': ['sh'],
\ })
augroup END
endif
```
Arch users also get python, C/C++ (in the `ccls` package), and LaTeX language
servers in community. I also found `rls-git` for Rust language server, HTML from
Microsoft's VSCode, and javascript with `typescript-language-server-bin` in the
AUR.
vim-lsp maintains a list of usual Vim LSP support scripts [on its
wiki][vim-lsp-support]. Once you've picked up your choice and installed it it's
mostly a matter of copy and paste.
Well, all this sounded quite systematic ! But ... vim-ale also maintains a list
of working linters [here][vim-ale-linters]. And it turns out that before
language servers, there was quite a bunch of linters doing the job all right.
And vim-ale is able to run those "traditional" linters while you type. And they
allow completion while you type. They also bear a smaller footprint. This
discussion could go more into the details of each language support, but finally
why go for the big overhead of this open source infrastructure ?
I ended up replacing language servers as follows :
| language | LSP implementation | linter |
|---|---|---|
| bash | `bash-language-server` | `shellcheck` |
| C/C++ | ? | `ccls` |
| css | `vscode-css-languageserver-bin` | `prettier` |
| javascript | `typescript-language-server-bin` | `typescript`'s tsserver |
| json | `vscode-json-language-server` | `prettier` |
| html | `vscode-html-languageserver-bin` | `htmlhint` |
| markdown | ? | `mdl` |
| python | `python-language-server` | `autopep8` |
| vala | `vala-language-server` | `uncrustify` |
| vimscript | `vim-language-server` | `vint` |
[vim-ale]: https://github.com/dense-analysis/ale
[vim-ale-linters]: https://github.com/dense-analysis/ale/blob/master/supported-tools.md
[vim-lsp]: https://github.com/prabirshrestha/vim-lsp
[vim-lsp-support]: https://github.com/prabirshrestha/vim-lsp/wiki/Servers
[coc.vim]: https://github.com/neoclide/coc.nvim

@ -0,0 +1,30 @@
+++
title = "Vim resources"
date = 2020-09-17
tags = ["admin", "vim"]
+++
I'll be dropping links to pages proposing interesting insights of Vim in here.
Let's start with a very classical one : [Learn Vimscript the Hard Way][hardway]
by Steve Losh, that I find very useful to give hints when you know precisely
what you want to do. I also dig the spirit.
Then there is the cheatsheet approach. I bookmarked Rico's
[devhints.io][devhints] page on Vimscript, that I come back to when there's a
need. There's also this [regex tutorial][regex] that is nice to bookmark, as
regex are really something you're not in a hurry to master.
Oh and then there's vimways.org's [quickfix shortcuts][quickfix] that introduces
the thing with its sarcastic tone, quite a journey.
Lastly I also found [this useful post][jakobgm] on integrating git into vim.
Then I remembered I had found this wizard's way of teaching
[operators][operators] in Vim, coming out with comments on orthogonality between
operators and motions.
[hardway]: https://learnvimscriptthehardway.stevelosh.com/
[devhints]: https://devhints.io/vimscript-functions
[regex]: https://medium.com/factory-mind/regex-tutorial-a-simple-cheatsheet-by-examples-649dc1c3f285
[quickfix]: https://vimways.org/2018/colder-quickfix-lists/
[jakobgm]: https://jakobgm.com/posts/vim/git-integration/
[operators]: https://whileimautomaton.net/2008/11/vimm3/operator

@ -0,0 +1,5 @@
---
title: "Search"
---
{{<search>}}

@ -0,0 +1,48 @@
function move_into(el, destEl) {
if(el.classList.contains('transition-deplacement')) {
console.log('! already movin');
} else {
el.classList.add('transition-deplacement');
let startPos = el.getBoundingClientRect();
console.log('déplacement depuis', startPos.left, startPos.top);
destEl.appendChild(el);
let endPos = el.getBoundingClientRect();
console.log("destination", endPos.left, endPos.top);
// Remove transitional class on completion.
el.addEventListener('transitionend', function declasse() {
console.log("élément", el, "déclassé");
el.removeEventListener('transitionend', declasse);
el.classList.remove('transition-deplacement');
});
// Move to initial position, coords from final position :
console.log(
startPos.left - endPos.left + "px",
startPos.top - endPos.top + "px");
el.style.transition = "0s";
el.style.left = startPos.left - endPos.left + "px";
el.style.top = startPos.top - endPos.top + "px";
// Let javascript notice current state.
void el.offsetLeft;
// Trigger transition : move to final position.
el.style.transition = "";
el.style.left = "";
el.style.top = "";
}
}
function toggle(el, destEl) {
if(el.classList.contains('ouvert')) {
el.classList.remove('ouvert');
move_into(el, destEl.parentElement);
} else {
el.classList.add('ouvert');
move_into(el, destEl);
}
}

@ -0,0 +1,59 @@
function tell_coords(el) {
var coords = new Object();
if(el.viewBox === undefined) {
coords.left = el.offsetLeft;
coords.top = el.offsetTop;
} else {
let geom = el.getBoundingClientRect();
coords.left = geom.left;
coords.top = geom.top;
}
return coords;
}
function initialize_state(el) {
if(el.viewBox === undefined) {
void el.offsetWidth;
} else {
el.getBoundingClientRect();
}
}
function move_into(el, destEl) {
if(el.classList.contains('transition-deplacement')) {
console.log('! already movin');
} else {
el.classList.add('transition-deplacement');
let startPos = tell_coords(el);
console.log('déplacement depuis', el.parentElement);
//console.log('déplacement depuis ', startPos);
destEl.appendChild(el);
let endPos = tell_coords(el);
console.log("destination", destEl);
//console.log("destination ", endPos);
el.style.transition = "all 0s";
el.style.left = startPos.left - endPos.left + "px";
el.style.top = startPos.top - endPos.top + "px";
el.addEventListener('transitionend', function declasse() {
el.removeEventListener('transitionend', declasse);
el.classList.remove('transition-deplacement');
console.log("élément", el, "déclassé");
});
initialize_state(el);
el.style.transition = ""; // Reset to css value.
el.style.left = "";
el.style.top = "";
}
}
function toggle(el, destEl) {
if(el.classList.contains('ouvert')) {
//move_into(depart, arrivee.parentElement);
el.classList.remove('ouvert');
move_into(el, destEl.parentElement);
} else {
el.classList.add('ouvert');
move_into(el, destEl);
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

@ -0,0 +1 @@
Subproject commit e7af87b290f47acb2a2463c3cb8bcbed4405713c
Loading…
Cancel
Save