diff --git a/TODO.org b/TODO.org new file mode 100644 index 0000000..9c5c68d --- /dev/null +++ b/TODO.org @@ -0,0 +1,3 @@ +#+TITLE: Project TODO + +* TODO Display size of current page (text + images) diff --git a/compresscss.sh b/compresscss.sh new file mode 100755 index 0000000..1e29259 --- /dev/null +++ b/compresscss.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env nix-shell +#!nix-shell -i zsh +#!nix-shell -I nixpkgs="https://github.com/NixOS/nixpkgs/archive/19.09.tar.gz" +#!nix-shell -p minify + +minify $1 > $2 diff --git a/mkrss.sh b/mkrss.sh index 8507107..0300467 100755 --- a/mkrss.sh +++ b/mkrss.sh @@ -1,5 +1,6 @@ #!/usr/bin/env nix-shell #!nix-shell -i zsh +#!nix-shell -I nixpkgs="https://github.com/NixOS/nixpkgs/archive/19.09.tar.gz" # Directory webdir="_site" diff --git a/project.el b/project.el index 8cc00d4..55ee334 100644 --- a/project.el +++ b/project.el @@ -1,8 +1,9 @@ ;; sign it with ;; gpg --local-user yann@esposito.host --output project.el.sig --detach-sign project.el (defvar domainname "https://her.esy.fun") -(defvar base-dir (concat (projectile-project-root) "src")) -(defvar publish-dir (concat (projectile-project-root) "_site")) +(defvar root-dir (projectile-project-root)) +(defvar base-dir (concat root-dir "src")) +(defvar publish-dir (concat root-dir "_site")) (defvar assets-dir (concat base-dir "/")) (defvar publish-assets-dir (concat publish-dir "/")) (defvar posts-dir (concat base-dir "/posts")) @@ -215,11 +216,13 @@ Return output file name." (or (equal (expand-file-name (file-name-directory filename)) (file-name-as-directory (expand-file-name pub-dir))) (let ((dst-file (expand-file-name (file-name-nondirectory filename) pub-dir))) - (if (string-match-p ".*\\.\\(png\\|jpg\\|gif\\)$" filename) - (shell-command (format "~/.nix-profile/bin/convert %s -resize 800x800\\> +dither -colors 16 -depth 4 %s" - filename - dst-file)) - (copy-file filename dst-file t))))) + (cond ((string-match-p ".*\\.\\(png\\|jpg\\|gif\\)$" filename) + (shell-command (format "~/.nix-profile/bin/convert %s -resize 800x800\\> +dither -colors 16 -depth 4 %s" + filename + dst-file))) + ((string-match-p ".*\\.css$" filename) + (shell-command (format "%s/compresscss.sh %s %s" root-dir filename dst-file))) + (t (copy-file filename dst-file t)))))) (defalias 'org-blog-posts-sitemap-fn (apply-partially 'org-blog-sitemap-fn-descr posts-descr)) diff --git a/project.el.sig b/project.el.sig index f32620d..1594060 100644 Binary files a/project.el.sig and b/project.el.sig differ diff --git a/solarized-reference-horizontal.png b/solarized-reference-horizontal.png deleted file mode 100644 index 77f22fa..0000000 Binary files a/solarized-reference-horizontal.png and /dev/null differ diff --git a/src/about-me.org b/src/about-me.org index e9a8944..7fe962e 100644 --- a/src/about-me.org +++ b/src/about-me.org @@ -8,6 +8,10 @@ #+OPTIONS: H:5 auto-id:t #+STARTUP: showeverything +#+ATTR_HTML: :style width:120px;display:block;margin-left:auto;margin-right:auto +[[../img/FlatAvatar.png]] + + #+begin_notes *Sorry for the late reply* @@ -24,7 +28,6 @@ So I'll answer, it can just take a lot of time. [fn:dm] http://www.calnewport.com/books/digital-minimalism/ - * Contact :PROPERTIES: :CUSTOM_ID: contact @@ -33,22 +36,22 @@ So I'll answer, it can just take a lot of time. - *email*: @@html: Yann Esposito <yann@esposito.host>@@ Self hosted services: -- [[https://gitlab.esy.fun/yogsototh][programming]] -- [[https://espial.esy.fun/u:yogsototh][bookmarks]] -- [[https://espial.esy.fun/u:yogsototh/notes][notes / micro-blog]] +| programs | [[https://gitlab.esy.fun/yogsototh][gitlab]] | +| bookmarks | [[https://espial.esy.fun/u:yogsototh][espial public bookmarks]] | +| notes/microblog | [[https://espial.esy.fun/u:yogsototh/notes][espial public notes]] | ** Social Platforms :PROPERTIES: :CUSTOM_ID: social-platforms :END: -- keybase: [[https://keybase.io/yogsototh][yogsototh]] -- bookmarks: [[https://pinboard.in/u:yogsototh][u:yogsototh]] -- lobste.rs: [[https://lobste.rs/u/yogsototh][/u/yogsototh]] -- github: [[https://github.com/yogsototh][yogsototh]] -- twitter: [[https://twitter.com/yogsototh][@yogsototh]] -- reddit: [[https://reddit.com/u/yogsototh][/u/yogsototh]] -- stack overflow: [[https://stackoverflow.com/users/40569/yogsototh][yogsototh]] +| keybase | [[https://keybase.io/yogsototh][yogsototh]] | +| bookmarks | [[https://pinboard.in/u:yogsototh][u:yogsototh]] | +| lobste.rs | [[https://lobste.rs/u/yogsototh][/u/yogsototh]] | +| github | [[https://github.com/yogsototh][yogsototh]] | +| twitter | [[https://twitter.com/yogsototh][@yogsototh]] | +| reddit | [[https://reddit.com/u/yogsototh][/u/yogsototh]] | +| stack overflow | [[https://stackoverflow.com/users/40569/yogsototh][yogsototh]] | ** old websites :PROPERTIES: @@ -57,3 +60,16 @@ Self hosted services: - https://yannesposito.com ✞ 2016 - http://yann.esposito.free.fr ✞ 2007 + +* Who am I +:PROPERTIES: +:CUSTOM_ID: who-am-i +:END: + +I am mostly known for some of my older blog post. +In particular: + +- [[http://yannesposito.com/Scratch/en/blog/Learn-Vim-Progressively/][Learn Vim Progressively]] +- [[http://yannesposito.com/Scratch/en/blog/Haskell-the-Hard-Way/][Learn Haskell Fast and Hard]] +- [[http://yannesposito.com/Scratch/en/blog/Yesod-tutorial-for-newbies/][Haskell Web Programming]] +- [[http://yannesposito.com/Scratch/en/blog/Category-Theory-Presentation/][Category Theory Presentation]] diff --git a/src/css/minimalist.css b/src/css/minimalist.css index dbb5f5b..571a0c7 100644 --- a/src/css/minimalist.css +++ b/src/css/minimalist.css @@ -3,12 +3,12 @@ */ :root { - --font-size: 12px; - --line-height: 14px; + --font-size: 14px; + --line-height: 16px; } /* Fonts */ body { - font-family: Menlo, Monaco, monospace; + font-family: Consolas, Anonymous Pro, Monaco, monospace; font-size: var(--font-size); line-height: var(--line-height); } @@ -91,11 +91,10 @@ p > img, li > img { vertical-align: middle; } table { - width: 100%; + max-width: 100%; margin: 1em 0; border-collapse: collapse; border: solid 1px; - display: block; overflow: scroll; } td, th { @@ -221,6 +220,7 @@ navigation > a { max-width: 46em; margin: 0 1em; padding: 1px; + margin: 0 auto; } #content:first-child { margin-top: 0; @@ -266,26 +266,27 @@ navigation > a { /* colors theme */ :root { color-scheme: light dark; /* support color scheme */ - --base03: #002b36; - --base02: #073642; - --base01: #586e75; - --base00: #657b83; - --base0: #839496; - --base1: #93a1a1; - --base2: #eee8d5; - --base3: #fdf6e3; - --yellow: #b58900; - --orange: #cb4b16; - --red: #dc322f; - --magenta: #d33682; - --violet: #6c71c4; - --blue: #268bd2; - --cyan: #2aa198; - --green: #859900; + --base03: #202631; + --base02: #2B313C; + --base01: #656B74; + --base00: #727781; + --base0: #8B919B; + --base1: #989EA8; + --base2: #E5E8ED; + --base3: #F4F7FC; + --yellow: #A98D50; + --orange: #aa6550; + --red: #b85a64; + --magenta: #af53b0; + --violet: #846f93; + --blue: #5679a4; + --cyan: #4c8493; + --green: #728b5c; + --transparent: rgba(255,255,255,0); - /* --main-background: #00151b; /* 0.5 darker than #002b36; */ - --main-background: var(--base03); /* 0.5 darker than #002b36; */ + /* Solaryzed accented colors */ + --main-background: var(--base03); --main-foreground: var(--base0); --second-foreground: var(--base01); --reveal-foreground: var(--base1); @@ -293,19 +294,19 @@ navigation > a { --soft-foreground: var(--base01); --border-color: var(--base02); --todo-txt: #000; - --color-h1: var(--cyan); - --color-h2: var(--green); - --color-h3: var(--yellow); - --color-h4: var(--orange); - --color-h5: var(--red); - --color-h6: var(--magenta); - --color-link: var(--magenta); + --color-h1: var(--reveal-foreground); + --color-h2: var(--reveal-foreground); + --color-h3: var(--reveal-foreground); + --color-h4: var(--reveal-foreground); + --color-h5: var(--reveal-foreground); + --color-h6: var(--reveal-foreground); + --color-link: var(--green); } /* org colors */ @media (prefers-color-scheme: light) { :root { - --main-background: #fefaf0; /* 0.5 lighter than #fdf6e3 */ + --main-background: var(--base3); --main-foreground: var(--base00); --second-foreground: var(--base1); --reveal-foreground: var(--base01); @@ -313,6 +314,13 @@ navigation > a { --soft-foreground: var(--base1); --border-color: var(--base2); --todo-txt: #FFF; + --color-h1: var(--reveal-foreground); + --color-h2: var(--reveal-foreground); + --color-h3: var(--reveal-foreground); + --color-h4: var(--reveal-foreground); + --color-h5: var(--reveal-foreground); + --color-h6: var(--reveal-foreground); + --color-link: var(--orange); } } #labels { @@ -325,24 +333,21 @@ navigation > a { font-style: italic; } -/* Light theme selected */ -input#light:checked ~ .main , -input#light:target ~ .main { - --main-background: #fefaf0; /* 0.5 lighter than #fdf6e3 */ - --main-foreground: var(--base01); - --second-foreground: var(--base00); - --reveal-foreground: var(--base01); - --reveal-background: var(--base2); - --soft-foreground: var(--base1); - --border-color: var(--base2); - --todo-txt: #FFF; +/* Dark themes soften and darken images */ +img { + filter: saturate(0.8) brightness(0.8); } -input#light:checked ~ #labels , -input#light:target ~ #labels { - background: #fefaf0; - color: var(--base00); +/* Light themes does not soften and darken images */ +input#light:checked ~ .main img, +input#light:target ~ .main img, +input#simple:checked ~ .main img, +input#simple:target ~ .main img, +input#modern:checked ~ .main img, +input#modern:target ~ .main img { + filter: none; } +/* --------------------------------------------------------------------------- */ /* Dark theme selected */ input#dark:checked ~ .main , input#dark:target ~ .main { @@ -354,6 +359,13 @@ input#dark:target ~ .main { --soft-foreground: var(--base01); --border-color: var(--base02); --todo-txt: #000; + --color-h1: var(--reveal-foreground); + --color-h2: var(--reveal-foreground); + --color-h3: var(--reveal-foreground); + --color-h4: var(--reveal-foreground); + --color-h5: var(--reveal-foreground); + --color-h6: var(--reveal-foreground); + --color-link: var(--green); } input#dark:checked ~ #labels , input#dark:target ~ #labels { @@ -361,6 +373,33 @@ input#dark:target ~ #labels { color: var(--base0); } +/* --------------------------------------------------------------------------- */ +/* Light theme selected */ +input#light:checked ~ .main , +input#light:target ~ .main { + --main-background: var(--base3); + --main-foreground: var(--base01); + --second-foreground: var(--base00); + --reveal-foreground: var(--base01); + --reveal-background: var(--base2); + --soft-foreground: var(--base1); + --border-color: var(--base2); + --todo-txt: #FFF; + --color-h1: var(--reveal-foreground); + --color-h2: var(--reveal-foreground); + --color-h3: var(--reveal-foreground); + --color-h4: var(--reveal-foreground); + --color-h5: var(--reveal-foreground); + --color-h6: var(--reveal-foreground); + --color-link: var(--orange); +} +input#light:checked ~ #labels , +input#light:target ~ #labels { + background: var(--base3); + color: var(--base00); +} + +/* --------------------------------------------------------------------------- */ /* Light simple theme selected */ input#simple:checked ~ .main , input#simple:target ~ .main { @@ -378,7 +417,7 @@ input#simple:target ~ .main { --color-h4: #333; --color-h5: #333; --color-h6: #333; - --color-link: var(--magenta); + --color-link: var(--orange); font-family: "Helvetica Neue"; font-size: 14px; line-height: 1.5em; @@ -408,6 +447,41 @@ input#simple:target ~ .main pre { font-family: monospace; } + +/* --------------------------------------------------------------------------- */ +/* Dark matrix theme selected */ +input#matrix:checked ~ .main , +input#matrix:target ~ .main { + --main-background: #000; + --main-foreground: #0b0; + --second-foreground: #080; + --reveal-foreground: #0f0; + --reveal-background: #000; + --soft-foreground: #080; + --border-color: #080; + --todo-txt: #0b0; + --color-h1: #0b0; + --color-h2: #0b0; + --color-h3: #0b0; + --color-h4: #0b0; + --color-h5: #0b0; + --color-h6: #0b0; + --color-link: #080; + font-family: monospace; +} +input#matrix:checked ~ #labels , +input#matrix:target ~ #labels { + background: #000; + color: #0b0; +} +input#matrix:checked ~ .main code, +input#matrix:target ~ .main code, +input#matrix:checked ~ .main pre , +input#matrix:target ~ .main pre { + font-family: monospace; +} + +/* --------------------------------------------------------------------------- */ /* Light modern theme selected */ input#modern:checked ~ .main , input#modern:target ~ .main { @@ -457,6 +531,7 @@ input#modern:target ~ .main h2 { font-size: 2em; line-height: 1.5em; margin: 1em 0; + color: var(--reveal-foreground); } input#modern:checked ~ .main h3, @@ -471,6 +546,19 @@ input#modern:target ~ .main h6 { font-size: 1.5em; line-height: 1.5em; margin: 1em 0; + color: var(--reveal-foreground); +} +input#modern:checked ~ .main h4, +input#modern:target ~ .main h4 { + font-size: 1.25em; + line-height: 1.25em; +} +input#modern:checked ~ .main h5, +input#modern:target ~ .main h5, +input#modern:checked ~ .main h6 , +input#modern:target ~ .main h6 { + font-size: 1em; + line-height: 1em; } input#modern:checked ~ .main #preamble , input#modern:target ~ .main #preamble { @@ -548,6 +636,7 @@ input#modern:target ~ #labels .content { margin: 0 auto; } +/* --------------------------------------------------------------------------- */ /* Dark simple theme selected */ input#darksimple:checked ~ .main , input#darksimple:target ~ .main { @@ -565,7 +654,7 @@ input#darksimple:target ~ .main { --color-h4: #ccc; --color-h5: #ccc; --color-h6: #ccc; - --color-link: var(--yellow); + --color-link: var(--green); font-family: "Helvetica Neue", sans-serif; font-weight: 300; font-size: 14px; @@ -597,37 +686,7 @@ input#darksimple:target ~ .main #content { margin: 0 auto; } -/* Dark matrix theme selected */ -input#matrix:checked ~ .main , -input#matrix:target ~ .main { - --main-background: #000; - --main-foreground: #0b0; - --second-foreground: #080; - --reveal-foreground: #0f0; - --reveal-background: #000; - --soft-foreground: #080; - --border-color: #080; - --todo-txt: #0b0; - --color-h1: #0b0; - --color-h2: #0b0; - --color-h3: #0b0; - --color-h4: #0b0; - --color-h5: #0b0; - --color-h6: #0b0; - --color-link: white; - font-family: monospace; -} -input#matrix:checked ~ #labels , -input#matrix:target ~ #labels { - background: #000; - color: #0b0; -} -input#matrix:checked ~ .main code, -input#matrix:target ~ .main code, -input#matrix:checked ~ .main pre , -input#matrix:target ~ .main pre { - font-family: monospace; -} +/* --------------------------------------------------------------------------- */ /* Default color theme */ body,.main { background: var(--main-background); @@ -636,8 +695,8 @@ body,.main { } ::selection, .main ::selection, ::-moz-selection, .main ::-moz-selection { - color: var(--todo-txt); - background-color: var(--yellow); + color: white; + background-color: var(--blue); } a, a:visited, .main a, .main a:visited { @@ -665,9 +724,11 @@ pre::after,pre::before,hr:after, .main pre::after,.main pre::before,.main hr:after { color: var(--soft-foreground); } +#labels label:hover, a:hover, a:active, a:focus, .main a:hover,.main a:active,.main a:focus { - color: var(--color-link); + color: white; + background: var(--color-link); } navigation a, navigation a:visited, .main navigation a,.main navigation a:visited { @@ -675,7 +736,8 @@ navigation a, navigation a:visited, } navigation a:focus, navigation a:hover, .main navigation a:focus,.main navigation a:hover { - color: var(--color-link); + color: white; + background: var(--color-link); } thead, .main thead { @@ -686,12 +748,13 @@ tr:hover, .main tr:hover { background-color: var(--reveal-background); } + +.description { + color: var(--second-foreground); +} h1, .main h1 { color: var(--color-h1); } -#preamble h2, .main #preamble h2 { - color: var(--color-h4); -} h2, .main h2 { color: var(--color-h2); } diff --git a/src/demo.org b/src/demo.org index 2ff61df..b46c9da 100644 --- a/src/demo.org +++ b/src/demo.org @@ -148,7 +148,7 @@ Perhaps a list: (continuous paths from $x_0$ to $x_0$), considered up to homotopy, with the group operations given by the identity path (standing still), path concatenation, and path reversal. For example, the fundamental group of - the $2$-sphere is trivial, but the fundamental group of the torus is + the 2-sphere is trivial, but the fundamental group of the torus is not, which shows that the sphere and the torus are not homotopy equivalent. The intuition is that every loop on the sphere is homotopic to the identity, because its inside can be filled in. In contrast, a @@ -179,7 +179,7 @@ Perhaps a list: (continuous paths from $x_0$ to $x_0$), considered up to homotopy, with the group operations given by the identity path (standing still), path concatenation, and path reversal. For example, the fundamental group of - the $2$-sphere is trivial, but the fundamental group of the torus is + the 2-sphere is trivial, but the fundamental group of the torus is not, which shows that the sphere and the torus are not homotopy equivalent. The intuition is that every loop on the sphere is homotopic to the identity, because its inside can be filled in. In contrast, a @@ -208,7 +208,7 @@ Perhaps a list: right regarding very long lines inside list, but also nested lists. 2. foo 1. Something else to nest. - 2. For example, the fundamental group of the $2$-sphere is trivial, + 2. For example, the fundamental group of the 2-sphere is trivial, but the fundamental group of the torus is not, which shows that the sphere and the torus are not homotopy equivalent. The intuition is that every loop on the sphere is homotopic to the identity, because @@ -223,24 +223,10 @@ Perhaps a list: concatenation, and path reversal. 4. Lot of things. - Lot of things. - -* Level 1 - :PROPERTIES: - :CUSTOM_ID: level-1 - :END: -** Level 2 - paragraph - :PROPERTIES: - :CUSTOM_ID: level-2-85fc - :END: - - There should be whitespace between paragraphs. - GitHub is a code hosting platform for version control and collaboration. - It lets you and others work together on projects from anywhere. - -*** Level 3 - blockquote - :PROPERTIES: - :CUSTOM_ID: level-3 - :END: +* Blockquote +:PROPERTIES: +:CUSTOM_ID: blockquote +:END: #+begin_quote This is a blockquote following a header. @@ -248,11 +234,11 @@ Perhaps a list: When something is important enough, you do it even if the odds are not in your favor. #+end_quote - -**** Level 4 - source code - :PROPERTIES: - :CUSTOM_ID: level-4 - :END: + +* Source code +:PROPERTIES: +:CUSTOM_ID: source-code +:END: #+begin_src javascript // Javascript code with syntax highlighting. @@ -284,10 +270,9 @@ Perhaps a list: name <- getLine putStrLn $ "Hello " <> name <> "!" #+end_src - -***** Level 5 - table and rules +* Tables :PROPERTIES: -:CUSTOM_ID: level-5 +:CUSTOM_ID: tables :END: | head1 | head two | @@ -306,10 +291,20 @@ Bad too wide table... | Galaad | the /pure/ | | | | | | | Zoot | Just =Zoot= | | | | | | +* Rules +:PROPERTIES: +:CUSTOM_ID: rules +:END: - There's a horizontal rule below this +There's a horizontal rule below this - ------ +------ + +Another here + +------ + +After the rule. * Image :PROPERTIES: @@ -370,3 +365,99 @@ CLOSED: [2019-07-09 Tue 13:45] :END: - State "CANCELED" from [2019-07-09 Tue 13:45] \\ cancel reason +* Level 1 +:PROPERTIES: +:CUSTOM_ID: level-1 +:END: +** Level 2 +:PROPERTIES: +:CUSTOM_ID: level-2 +:END: +*** Level 3 +:PROPERTIES: +:CUSTOM_ID: level-3 +:END: +**** Level 4 +:PROPERTIES: +:CUSTOM_ID: level-4 +:END: +***** Level 5 +:PROPERTIES: +:CUSTOM_ID: level-5 +:END: +****** Level 6 +:PROPERTIES: +:CUSTOM_ID: level-6 +:END: +******* Level 7 +:PROPERTIES: +:CUSTOM_ID: level-7 +:END: +******** Level 8 +:PROPERTIES: +:CUSTOM_ID: level-8 +:END: +********* Level 9 +:PROPERTIES: +:CUSTOM_ID: level-9 +:END: +********** Level 10 +:PROPERTIES: +:CUSTOM_ID: level-10 +:END: +*********** Level 11 +:PROPERTIES: +:CUSTOM_ID: level-11 +:END: +************ Level 12 +:PROPERTIES: +:CUSTOM_ID: level-12 +:END: +* TODO Todo 1 +:PROPERTIES: +:CUSTOM_ID: todo-1 +:END: +** TODO Todo 2 +:PROPERTIES: +:CUSTOM_ID: todo-2 +:END: +*** TODO Todo 3 +:PROPERTIES: +:CUSTOM_ID: todo-3 +:END: +**** TODO Todo 4 +:PROPERTIES: +:CUSTOM_ID: todo-4 +:END: +***** TODO Todo 5 +:PROPERTIES: +:CUSTOM_ID: todo-5 +:END: +****** TODO Todo 6 +:PROPERTIES: +:CUSTOM_ID: todo-6 +:END: +******* TODO Todo 7 +:PROPERTIES: +:CUSTOM_ID: todo-7 +:END: +******** TODO Todo 8 +:PROPERTIES: +:CUSTOM_ID: todo-8 +:END: +********* TODO Todo 9 +:PROPERTIES: +:CUSTOM_ID: todo-9 +:END: +********** TODO Todo 10 +:PROPERTIES: +:CUSTOM_ID: todo-10 +:END: +*********** TODO Todo 11 +:PROPERTIES: +:CUSTOM_ID: todo-11 +:END: +************ TODO Todo 12 +:PROPERTIES: +:CUSTOM_ID: todo-12 +:END: diff --git a/src/index.org b/src/index.org index bc963b3..c1a7e6d 100644 --- a/src/index.org +++ b/src/index.org @@ -8,35 +8,39 @@ #+OPTIONS: H:5 #+STARTUP: showeverything +#+ATTR_HTML: :style width:120px;display:block;margin-left:auto;margin-right:auto +[[../img/FlatAvatar.png]] + Welcome to my personal website. +I generally talk about programming, maybe movies, or miscellaneous topics. +You can follow new articles via [[https://her.esy.fun/rss.xml][RSS]]. +I also expose some of my presentations here. - [[file:archive.org][articles]] - [[file:slides.org][presentations]] -Here I will talk mostly about the life of a software developer. -So programming, functional programming in particular. - That website was created with the following constraints in mind by order of priority: 1. *Respect Privacy*; no tracker of any sort (no ads, no google analytics, no referrer for all external links, etc...) -2. *almost javascript free*; no js at all except for a single exception, - pages containing Math formula are displayed using mathjax. That means - that event the CSS theme switcher does not use javascript. -3. *disability friendly*; should be easy to read on a text browser so people - with disabilities could easily consume it -4. *nerdy*; should feel mostly like markdown text in a terminal and source code +2. *Almost javascript free*; no js at all except for a single exception, + pages containing Math formulae are displayed using mathjax. That means + that even the CSS theme switcher does not use javascript. +3. *Accessibility*; should be easy to read on a text browser so people with + disabilities could easily consume it. +4. *Nerdy*; should feel mostly like markdown text in a terminal and source code should be syntax highlighted. -5. *user friendly*; support your preferred light/dark theme by default but you +5. *User friendly*; support your preferred light/dark theme by default but you can change it if you want. -6. *rss*; you should be able to get informed when I add a new blog post. -7. *frugal*; try to minimize the resources needed to visit my website; no +6. *RSS*; you should be able to get informed when I add a new blog post. +7. *Frugal*; try to minimize the resources needed to visit my website; no javascript, no web-font, not too much CSS magic, not much images or really compressed one. I talk about more details [[file:./posts/new-blog.org][here]]. + #+begin_export html [Valid RSS] #+end_export diff --git a/src/posts/0006-modern-irc/index.org b/src/posts/0006-modern-irc/index.org new file mode 100644 index 0000000..ccb05f9 --- /dev/null +++ b/src/posts/0006-modern-irc/index.org @@ -0,0 +1,518 @@ +#+TITLE: Modern IRC +#+SUBTITLE: In 2019, IRC is still the best. +#+AUTHOR: Yann Esposito +#+EMAIL: yann@esposito.host +#+DATE: [2019-10-19 Sat] +#+KEYWORDS: self-hosting, chat, irc +#+DESCRIPTION: How to modernize IRC +#+OPTIONS: auto-id:t toc:t + +#+begin_notes +tl;dr: Why and how to have modern and respectful chat system with IRC. + +After reviewing and testing many different chat solutions the clear winner +is IRC. More precisely via those softwares: +- IRC +- ZNC (with playback and palaver module) +- weechat with replay script (terminal client) +- thelounge (web client) +- Palaver (iOS client). +#+end_notes + +* Why IRC? +:PROPERTIES: +:CUSTOM_ID: why-irc- +:END: + +How to chat in 2019? Certainly with slack, or via a social media app in the +browser or mobile phone app. + +How geeks should chat in 2019? + +To answer this question here is my opinion after having tried many +different chat solutions[fn:tries]. +Here are the feature I think a modern solution should have: + +1. *terminal client* or *terminal-like UI* (in emacs for example). + All modern UI looks cool for screenshots, but if you are going to use it + a lot, you will prefer density over good looking. + Most app, web app are terrible related to information by number of pixel + ratio. +2. *multi-platform*: If you do not have a terminal at hand (or emacs) then, + you should be able to get your message on your phone or via a web + interface for portability. +3. *self-hosted*: you should control your data, your history, your logs, + the encryption methods, etc... +4. *teams* and *direct messages* +5. *notifications*, I tend to control those a lot, but a small private team + chat is one of few exception where you generally want to be notified. +6. *Frugal*. Really, we have a responsibility to do our best not to consume + more resources than we really need. + Chat should be about TEXT, not images, not videos, not presentations and + PDF. +7. *No anti-features*: show when someone is typing, show when someone + as read your message, etc... Those functionality are in fact increasing + social insecurity and forces you to answer sooner instead of really + taking the time to answer correctly. +8. *Free software* + +I am quite disappointed by /modern/ chat applications. + +Their major problems are: + +- *resource-heavy*; most those client applications (slack, gitter, riot, + mattermost, etc...) easily consume more than 300MB of RAM. + Most of the time those clients are all electron app. +- *not private*; most solution do not encrypt your conversations. + Even if using encryption mechanism and you trust your client, and you + will still reveal your social network topology. +- *anti-minimalist*; I want dense /text/. + I do not want: + + emojis, + + images, + + animations (gif or videos), + + HTML/Markdown display +- *manipulative*; they try very hard to optimize engagement. + This is generally achieved through FOMO[fn:FOMO] and social anxiety + manipulations. + A few examples: + + show when someone is writing a message + + show when someone has read a message, + + get notified about missed messages, + + get a "top messages you missed", + + etc... +- *Prepare to EEE[fn:eee]*: + Most of those "modern" solution are all-in-one solutions. + Server API + Clients with specific features. + Doing it that way make it possible to provide specific features only via + this "all in one" solution. + If you want to use another client, or if they deprecate some (like slack + did by removing their IRC bridge) then you will have no choice to use + their entire closed ecosystem. + +[fn:eee] [[https://en.wikipedia.org/wiki/Embrace,_extend,_and_extinguish][Embrace, extend, and extinguish]]. +[fn:dm] http://www.calnewport.com/books/digital-minimalism/ + +** About failed attempts +:PROPERTIES: +:CUSTOM_ID: about-failed-attempts--properties---custom-id--different-tries +:END: + +- Matrix: I've used Matrix, and in fact it was really good except; the + server is written in python and is clearly not frugal at all. + Also I wanted to delete most of the history in the DB, and it was + impossible to find a working documentation explaining how to do that + correctly and easily (I'm not even sure this is possible). + You can easily remove some channels history from the DB, but doing the + opposite, keeping the history only of some channel and removing all others + doesn't appear to be easy. +- Mattermost: I've tried to install mattermost, to install it, there is + no package, you need to start a shell script as root that will erase and + change your nginx configuration.... Seriously... +- Rocket.chat, ho.... a nice word about the difference between community + version and pro version... no thank you. I prefer something sustained by + free software standards. +- I tried XMPP, it was OK. But the clients weren't really good, I could + have used bitlbee, the installation looked more complex than IRC. + +Finally, IRC + ZNC with replay module is the winner. +It was the easiest and best solution. + + +1. it works +2. it is frugal +3. it is old and stable +4. it is both minimalist and feature complete + +[fn:FOMO] Fear Of Missing Out +[fn:tries] Here is a list of the chatting solutions I used for some time +and finally abandoned (I certainly forgot a few ones): + - slack + - matrix (self-hosted) + - keybase + - discord + - gitter + - XMPP (both hosted by a 3rd party and self-hosted) + - IRC ← the winner + +* Tutorial +:PROPERTIES: +:CUSTOM_ID: tutorial +:END: + +Here is how to have a great private self-hosted IRC server to share with a +small group of people. + +If this appear to be too much work for you, you should simply use a service +that host a znc bouncer for you (I found some apparently free services +doing that) and find an IRC server allowing you to create some private +channel. + +** Self Host +:PROPERTIES: +:CUSTOM_ID: self-host +:END: +You might use an external IRC server. +But it is a lot safer to self-host it. + +Self-hosting might not be easy if you are not familiar about how to do +that. + +1. buy a domain name +2. buy a machine (VPS, baremetal, host it at your home) +3. configure the DNS for your domaine name go to your machine +4. configure letsencrypt to support wildcard hostnames. +5. know how to create reverse proxy + +I couldn't find a nice resource to link to with all those details. +This is certainly a call to write such article myself. + +*** Create a reverse proxy with nginx +:PROPERTIES: +:CUSTOM_ID: create-a-reverse-proxy-with-nginx +:END: + +This is how I create new reverse proxy with nginx using a template: +[[./reverse-proxy-template.m4][reverse-proxy-template.m4]]. + +#+begin_src m4 :exports none :tangle reverse-proxy-template.m4 +# Nginx configuration + +server { + server_name SUB.DOMAIN; + access_log /var/log/nginx/SUB()_ssl_access.log; + error_log /var/log/nginx/SUB()_ssl_error.log; + + # # access restricted + # auth_basic "Admin restricted"; + # auth_basic_user_file /etc/nginx/htpasswd; + + listen *:443 ssl; + listen [::]:443 ssl; + server_tokens off; + + ## SSL + ssl on; + ssl_certificate /etc/letsencrypt/live/DOMAIN/fullchain.pem; # managed by Certbot + ssl_certificate_key /etc/letsencrypt/live/DOMAIN/privkey.pem; # managed by Certbot + ssl_prefer_server_ciphers on; + ssl_session_cache shared:SSL:10m; + ssl_session_timeout 5m; + + ## [Optional] Enable HTTP Strict Transport Security + ## HSTS is a feature improving protection against MITM attacks + ## For more information see: https://www.nginx.com/blog/http-strict-transport-security-hsts-and-nginx/ + add_header Strict-Transport-Security "max-age=31536000; includeSubDomains"; + + location / { + proxy_pass http://127.0.0.1:PORT; + gzip off; + proxy_redirect off; + + ## Some requests take more than 30 seconds. + proxy_read_timeout 30s; + proxy_connect_timeout 30s; + + proxy_http_version 1.1; + + proxy_set_header Host $http_host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-Ssl on; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Client-Verify SUCCESS; + proxy_set_header X-Client-DN $ssl_client_s_dn; + proxy_set_header X-SSL-Subject $ssl_client_s_dn; + proxy_set_header X-SSL-Issuer $ssl_client_i_dn; + } +} + +## Redirects all HTTP traffic to the HTTPS host +server { + ## In case of conflict, either remove "default_server" from the listen line below, + ## or delete the /etc/nginx/sites-enabled/default file. + listen 0.0.0.0:80; + listen [::]:80; + server_name SUB.DOMAIN; + server_tokens off; ## Don't show the nginx version number, a security best practice + return 301 https://$http_host$request_uri; + access_log /var/log/nginx/SUB.DOMAIN()_access.log; + error_log /var/log/nginx/SUB.DOMAIN()_error.log; +} +#+end_src + +That I use with the following script: [[./new-reverse-proxy.sh][new-reverse-proxy.sh]] + +#+begin_src bash :tangle new-reverse-proxy.sh +#!/usr/bin/env zsh + +(($#<3)) && { + print "usage: $0:t SUB DOMAIN PORT" + exit 1 +} >&2 + +SUB="$1" +DOMAIN="$2" +PORT="$3" + +m4 -D SUB=$SUB -D DOMAIN=$DOMAIN -D PORT=$PORT reverse-proxy-template.m4 > $SUB.$DOMAIN +#+end_src + +The script will generate a reverse proxy nginx conf that I put in +=/etc/nginx/sites-available/= and I link it in =/etc/nginx/sites-enabled=. + +** Install/configure ngircd +:PROPERTIES: +:CUSTOM_ID: install-configure-ngircd +:END: + +There are multiple IRC server. +I gave my preference to [[https://github.com/ngircd/ngircd][ngircd]] because it appeared lightweight, easy to +install and configure. + +So use your preferred package manager to install it: + +#+begin_src +sudo apt-get install ngircd +#+end_src + +Configure the =/etc/ngircd/ngircd.conf= file. +I only show the really interesting lines for a private small IRC for a team. + +#+begin_src src +[Global] + Name = irc.your.domain + Info = My Incredible IRC + Password = privateIRCSecretPassword + +[Options] + # prevent channel creation + AllowedChannelTypes = + +[SSL] + Certfile = /etc/letsencrypt/live/your.domain/fullchain.pem + Keyfile = /etc/letsencrypt/live/your.domain/privkey.pem + Ports = 6667,9999 + +[Channel] + # predefined channel + Name = #general + Topic = Main team channel + MaxUsers = 23 + +[Channel] + Name = #status + Topic = Status update channel + MaxUsers = 23 +#+end_src + +** Install/configure ZNC +:PROPERTIES: +:CUSTOM_ID: install-configure-znc +:END: + +Install ZNC from sources or via your package manager. +I choose sources. Choose the latest version if you can. + +#+begin_src +> wget https://znc.in/releases/archive/znc-1.7.5.tar.gz +> tar xzf znc-1.7.5.tar.gz +> cd znc-1.7.5 +> mkdir build +> cd build +> make +> make install +#+end_src + +Then create your config file for example via: + +#+begin_src +> znc --makeconf +#+end_src + +For the question, keep buffers after replay, you should certainly answer +yes. +To use znc web interface behind an nginx reverse proxy: + +#+begin_src conf + + AllowIRC = false + AllowWeb = true + Host = localhost + IPv4 = true + IPv6 = false + Port = 10001 + SSL = false + URIPrefix = / + + + + AllowIRC = true + AllowWeb = false + IPv4 = true + IPv6 = true + Port = 10002 + SSL = true + URIPrefix = / + +#+end_src + +Now you can put your ZNC behind a reverse proxy. + +In order not to miss any message in your clients you should keep a bouncer +running for you that will keep all IRC messages. +But in order to sync this history correctly among all your different IRC +clients you should install the playback module. +And if you wish to receive push notification you should also add a module +for your application (in my case palaver). + +*** Playback module +:PROPERTIES: +:CUSTOM_ID: playback-module +:END: + +In order not to miss any messages in all your clients you should add this +[[https://wiki.znc.in/Playback][ZNC playback module]]. + +#+begin_src +> cd ~/.znc/modules +> wget https://raw.githubusercontent.com/jpnurmi/znc-playback/master/playback.cpp +> znc-buildmod playback.cpp +#+end_src + +Should create a =playback.so= in =~/.znc/modules=. + +*** Palaver push module +:PROPERTIES: +:CUSTOM_ID: palaver-push-module +:END: + +You should find the ZNC push palaver module here: + +https://github.com/cocodelabs/znc-palaver + +#+begin_src +> git clone https://github.com/cocodelabs/znc-palaver znc-palaver +> cd znc-palaver +> znc-buildmod palaver.cpp +> cp palaver.so ~/.znc/modules/ +#+end_src + +*** Configure your IRC servers +:PROPERTIES: +:CUSTOM_ID: configure-your-irc-servers +:END: + +Now you should be able to reach =znc.my.domain=. +You should see something like + +#+NAME: ZNC Login Page +[[./znc-login.png]] + +Login with your admin user (set during the configuration or znc). +Then go to your Global settings + +#+NAME: ZNC Global Settings +[[./znc-global-settings.png]] + +And if you scroll down you should see a list of modules. Select the +playback and palaver modules and save your preferences. + +#+NAME: ZNC Modules +[[./znc-modules.png]] + +Then under the global settings, go to your User settings and scroll down to +see the Flags: + +#+NAME: ZNC User Settings Flags +[[./znc-user-settings-flags.png]] + +Take care to unselect the "Auto Clear Chan Buffer", "Auto Clear Query +Buffer" and to select "Multi Clients". +If you forget to do that, the playback plugin will not work as expected. + +Finally add your IRC server to via the Network block (in your User Settings): + +#+NAME: ZNC Add Network +[[./znc-add-network.png]] + +From now on, you should always appear as a connected user to your IRC server. +This is your ZNC bouncer reading all the messages for you even when you are +not here. + +** Install/configure clients +:PROPERTIES: +:CUSTOM_ID: install-configure-clients +:END: +*** weechat +:PROPERTIES: +:CUSTOM_ID: weechat +:END: + +Weechat the IRC client I use the most. +It is terminal based, use very few resources, it is fast, dense and very nice +to use. + +1. add the [[https://weechat.org/scripts/source/zncplayback.py.html/][weechat znc playback script]] +2. in weechat, set server capabilities + #+begin_src irc + /set irc.server_default.capabilities "account-notify,away-notify,cap-notify,multi-prefix,server-time,znc.in/server-time-iso,znc.in/self-message,znc.in/playback + #+end_src +3. add your server + #+begin_src irc + /server add zncnetwork znc.my.domain/6697 -ssl -username=username/zncnetwork -password=password -autoconnect + /connect zncnetwork + #+end_src +4. save your confi with =/save= + +More details here: https://wiki.znc.in/Weechat +*** thelounge +:PROPERTIES: +:CUSTOM_ID: thelounge +:END: + +Here are the infos for installing it. + +https://thelounge.chat/docs/install-and-upgrade + +You can use my reverse proxy scripts to put the lounge behind a reverse +proxy from your host. So you'll be able to reach =thelounge.my.domain=. +Of course, connect the lounge via ZNC not directly to your IRC server. + +*** Palaver +:PROPERTIES: +:CUSTOM_ID: palaver +:END: +Using palaver should be straightfoward. +There is a very clear ZNC configuration choice. + +Here is its website: https://palaverapp.com + +I previously used the app mutter, but it stopped to work after the iOS 13 +update. +* Bonus +:PROPERTIES: +:CUSTOM_ID: bonus +:END: +** No brainer upload file +:PROPERTIES: +:CUSTOM_ID: no-brainer-upload-file +:END: + +Quite often you want to share images/files in your chat. +Instead of using a public channel, I preferred to create a minimalist (223 +lines of haskell) private server for this purpose only. + +It is highly inspired from the image uploader example of the Yesod web +framework. +It is a single self-executable file + one css and jquery. +The only dependency is [[https://docs.haskellstack.org/en/stable/README/][stack]]. + +So to install it: + +1. install [[https://docs.haskellstack.org/en/stable/README/][stack]] +2. =git clone https://gitlab.esy.fun/yogsototh/ymgur .= +3. follow the README instructions to launch it +4. create an nginx reverse proxy protected with basic-auth +5. share the creds to your group members +6. enjoy diff --git a/src/posts/0006-modern-irc/new-reverse-proxy.sh b/src/posts/0006-modern-irc/new-reverse-proxy.sh new file mode 100644 index 0000000..4b41f69 --- /dev/null +++ b/src/posts/0006-modern-irc/new-reverse-proxy.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env zsh + +(($#<3)) && { + print "usage: $0:t SUB DOMAIN PORT" + exit 1 +} >&2 + +SUB="$1" +DOMAIN="$2" +PORT="$3" + +m4 -D SUB=$SUB -D DOMAIN=$DOMAIN -D PORT=$PORT reverse-proxy-template.m4 > $SUB.$DOMAIN diff --git a/src/posts/0006-modern-irc/reverse-proxy-template.m4 b/src/posts/0006-modern-irc/reverse-proxy-template.m4 new file mode 100644 index 0000000..ce4f3db --- /dev/null +++ b/src/posts/0006-modern-irc/reverse-proxy-template.m4 @@ -0,0 +1,63 @@ +# Nginx configuration + +server { + server_name SUB.DOMAIN; + access_log /var/log/nginx/SUB()_ssl_access.log; + error_log /var/log/nginx/SUB()_ssl_error.log; + + # # access restricted + # auth_basic "Admin restricted"; + # auth_basic_user_file /etc/nginx/htpasswd; + + listen *:443 ssl; + listen [::]:443 ssl; + server_tokens off; + + ## SSL + ssl on; + ssl_certificate /etc/letsencrypt/live/DOMAIN/fullchain.pem; # managed by Certbot + ssl_certificate_key /etc/letsencrypt/live/DOMAIN/privkey.pem; # managed by Certbot + ssl_prefer_server_ciphers on; + ssl_session_cache shared:SSL:10m; + ssl_session_timeout 5m; + + ## [Optional] Enable HTTP Strict Transport Security + ## HSTS is a feature improving protection against MITM attacks + ## For more information see: https://www.nginx.com/blog/http-strict-transport-security-hsts-and-nginx/ + add_header Strict-Transport-Security "max-age=31536000; includeSubDomains"; + + location / { + proxy_pass http://127.0.0.1:PORT; + gzip off; + proxy_redirect off; + + ## Some requests take more than 30 seconds. + proxy_read_timeout 30s; + proxy_connect_timeout 30s; + + proxy_http_version 1.1; + + proxy_set_header Host $http_host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-Ssl on; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Client-Verify SUCCESS; + proxy_set_header X-Client-DN $ssl_client_s_dn; + proxy_set_header X-SSL-Subject $ssl_client_s_dn; + proxy_set_header X-SSL-Issuer $ssl_client_i_dn; + } +} + +## Redirects all HTTP traffic to the HTTPS host +server { + ## In case of conflict, either remove "default_server" from the listen line below, + ## or delete the /etc/nginx/sites-enabled/default file. + listen 0.0.0.0:80; + listen [::]:80; + server_name SUB.DOMAIN; + server_tokens off; ## Don't show the nginx version number, a security best practice + return 301 https://$http_host$request_uri; + access_log /var/log/nginx/SUB.DOMAIN()_access.log; + error_log /var/log/nginx/SUB.DOMAIN()_error.log; +} diff --git a/src/posts/0006-modern-irc/znc-add-network.png b/src/posts/0006-modern-irc/znc-add-network.png new file mode 100644 index 0000000..5c72e18 Binary files /dev/null and b/src/posts/0006-modern-irc/znc-add-network.png differ diff --git a/src/posts/0006-modern-irc/znc-global-settings.png b/src/posts/0006-modern-irc/znc-global-settings.png new file mode 100644 index 0000000..7e1a52b Binary files /dev/null and b/src/posts/0006-modern-irc/znc-global-settings.png differ diff --git a/src/posts/0006-modern-irc/znc-login.png b/src/posts/0006-modern-irc/znc-login.png new file mode 100644 index 0000000..640af2a Binary files /dev/null and b/src/posts/0006-modern-irc/znc-login.png differ diff --git a/src/posts/0006-modern-irc/znc-modules.png b/src/posts/0006-modern-irc/znc-modules.png new file mode 100644 index 0000000..d00701b Binary files /dev/null and b/src/posts/0006-modern-irc/znc-modules.png differ diff --git a/src/posts/0006-modern-irc/znc-user-settings-flags.png b/src/posts/0006-modern-irc/znc-user-settings-flags.png new file mode 100644 index 0000000..ace5d38 Binary files /dev/null and b/src/posts/0006-modern-irc/znc-user-settings-flags.png differ diff --git a/src/posts/project-el/index.org b/src/posts/project-el/index.org index 8549ba2..a0a4a42 100644 --- a/src/posts/project-el/index.org +++ b/src/posts/project-el/index.org @@ -97,7 +97,7 @@ You can setup the emacs package in spacemacs with: ;; ... (require 'auto-load-project) (setq auto-load-project/trusted-gpg-key-fingerprints - '("0000000000000000000000000000000000000000" ;; figerprint of trusted key 1 + '("0000000000000000000000000000000000000000" "1111111111111111111111111111111111111111" "2222222222222222222222222222222222222222" ))) diff --git a/src/slides.org b/src/slides.org index 95cc6f6..7fee4bc 100644 --- a/src/slides.org +++ b/src/slides.org @@ -8,14 +8,14 @@ #+OPTIONS: H:5 #+STARTUP: showeverything -* Slides +* English - [2018-10-25 Thu] [[file:slides/git-project-manager.org][Git Project Manager]] is a talk I made for the "Commission Open Source" about how to gain back autonomy with git by providing most features Github propose just with a few conventions. I even written a tool to help manage issues, code review, git hosting, etc... -** French talks +* French - [2018-03-15 Thu] [[file:slides/Intro-to-FP-with-Haskell.org][Introduction à la programmation fonctionnelle avec Haskell]]