initial commit
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
/public
|
||||||
|
/resources
|
0
.hugo_build.lock
Normal file
4
TODO.md
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
- Add permalinks to sections
|
||||||
|
- Add summmary to articles
|
||||||
|
- Add open graph metadata
|
||||||
|
- Add json+ld metadata
|
5
archetypes/default.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
+++
|
||||||
|
date = '{{ .Date }}'
|
||||||
|
draft = true
|
||||||
|
title = '{{ replace .File.ContentBaseName "-" " " | title }}'
|
||||||
|
+++
|
0
assets/.gitkeep
Normal file
4
build.sh
Executable file
|
@ -0,0 +1,4 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
rm -rf public
|
||||||
|
hugo build --minify
|
25
content/_index.md
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
+++
|
||||||
|
title = 'Home'
|
||||||
|
date = 2025-06-03T23:00:00Z
|
||||||
|
draft = false
|
||||||
|
+++
|
||||||
|
|
||||||
|
With a dual experience in marketing and technical fields, I have contributed to
|
||||||
|
the success of numerous projects by adopting a comprehensive vision of them.
|
||||||
|
|
||||||
|
Graduate from a business school (Skema Business School), I completed a full
|
||||||
|
training in management (accounting, finance, project management, HR, strategy,
|
||||||
|
etc.) and specialized in marketing.
|
||||||
|
|
||||||
|
Passionate about computer science from a young age, I am also a self-taught
|
||||||
|
versatile engineer, performing tasks in system and network architecture and
|
||||||
|
administration, development, support, and training.
|
||||||
|
|
||||||
|
I now have over 18 years of experience in the open-source software world,
|
||||||
|
including 8 years professionally.
|
||||||
|
|
||||||
|
Curious about the continuous innovations offered by all free software, I
|
||||||
|
dedicate part of my time to technological watch, both on productivity tools and
|
||||||
|
system and network tools, which allows me to work on the design and
|
||||||
|
implementation of complex distributed systems as well as on the management and
|
||||||
|
execution of application integration projects within an information system.
|
45
content/blog/001-setup-nginx-for-mediawiki.md
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
---
|
||||||
|
title: Setup Nginx for Mediawiki
|
||||||
|
date: "2010-09-15T00:00:00+02:00"
|
||||||
|
tags:
|
||||||
|
- Nginx
|
||||||
|
- Mediawiki
|
||||||
|
slug: setup-nginx-for-mediawiki
|
||||||
|
summary: >
|
||||||
|
A simple configuration to serve Mediawiki with Nginx and FastCGI
|
||||||
|
---
|
||||||
|
|
||||||
|
Two weeks ago, I migrated a server from Apache/mod_php to nginx/php-fpm. Only
|
||||||
|
today did i succeed to remove all side effects. The latest one:
|
||||||
|
|
||||||
|
Static files must not go through php-fpm, but a simple test on extensions
|
||||||
|
is ineffective, as url like `http://server/File:name_of_the_file.png`
|
||||||
|
must be processed by PHP.
|
||||||
|
|
||||||
|
Here is my final setup, that corrects all the errors I encountered:
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name server_name;
|
||||||
|
index index.php;
|
||||||
|
root /path/to/www/;
|
||||||
|
|
||||||
|
# Serve static files with a far future expiration
|
||||||
|
# date for browser caches
|
||||||
|
location ^~ /images/ {
|
||||||
|
expires 1y;
|
||||||
|
}
|
||||||
|
location ^~ /skins/ {
|
||||||
|
expires 1y;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Pass the request to php-cgi
|
||||||
|
location / {
|
||||||
|
fastcgi_pass 127.0.0.1:9000;
|
||||||
|
fastcgi_param SCRIPT_FILENAME $document_root/index.php;
|
||||||
|
fastcgi_index index.php;
|
||||||
|
include fastcgi_params;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
32
content/blog/002-build-pgpool-on-debian.md
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
---
|
||||||
|
title: Build the latest PgPool-II on Debian Etch
|
||||||
|
date: "2010-12-14T00:00:00+01:00"
|
||||||
|
tags: [Debian, PgPool-II]
|
||||||
|
slug: build-pgpool-on-debian
|
||||||
|
summary: >
|
||||||
|
Building PgPool-II on RHEL 5.5 to avoid the "libpq is not installed or
|
||||||
|
libpq is old" error
|
||||||
|
---
|
||||||
|
|
||||||
|
After having build PgPool-II on Red Hat Enterprise Linux 5.5 without any
|
||||||
|
problem, I tried to build it on a fresh Debian Etch. The catch is that I did
|
||||||
|
not want to install PostgreSQL 9.0, but just extract it from the binary
|
||||||
|
packages provided by Entreprisedb (with option `--extract-only 1`).
|
||||||
|
|
||||||
|
Whatever options I passed to `./configure`, it resulted in the same error:
|
||||||
|
|
||||||
|
{{< highlight text >}}
|
||||||
|
checking for PQexecPrepared in -lpq... no
|
||||||
|
configure: error: libpq is not installed or libpq is old
|
||||||
|
{{< /highlight >}}
|
||||||
|
|
||||||
|
Here is the answer: the binary package contains the libpq with the name
|
||||||
|
`libcrypto.so.0.9.8` (the RHEL name) when pgpool is looking `libcrypto.so.6`
|
||||||
|
on Debian. The same applies to `libssl`. So a simple
|
||||||
|
|
||||||
|
{{< highlight bash >}}
|
||||||
|
ln -s libcrypto.so.0.9.8 libcrypto.so.0.9.8
|
||||||
|
ln -s libssl.so.0.9.8 libssl.so.6
|
||||||
|
{{< /highlight >}}
|
||||||
|
|
||||||
|
before your `./configure` will solve it!
|
87
content/blog/003-aptana-eclipse-and-xulrunner.md
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
---
|
||||||
|
slug: aptana-eclipse-and-xulrunner
|
||||||
|
title: Aptana Studio/Eclipse and Xulrunner
|
||||||
|
tags: [Aptana Studio, Eclipse, Xulrunner, Arch Linux]
|
||||||
|
date: "2011-12-16T00:00:00+01:00"
|
||||||
|
summary: >
|
||||||
|
How to solve the "Unhandled event loop exception" error in Aptana Studio and Eclipse 3.7 with Xulrunner
|
||||||
|
---
|
||||||
|
|
||||||
|
Since a few months, I encountered an annoying error in Aptana Studio and
|
||||||
|
Eclipse 3.7 (the autonomous packages, not the packages from the repositories)
|
||||||
|
whenever I tried to do a git or hg action.
|
||||||
|
|
||||||
|
I could live without until now, but today, it was really bothering me.
|
||||||
|
|
||||||
|
The error is:
|
||||||
|
|
||||||
|
{{< highlight text >}}
|
||||||
|
Unhandled event loop exception
|
||||||
|
No more handles [Unknown Mozilla path (MOZILLA_FIVE_HOME not set)]
|
||||||
|
{{< /highlight >}}
|
||||||
|
|
||||||
|
The log file showed the following backtrace:
|
||||||
|
|
||||||
|
{{< highlight text >}}
|
||||||
|
!ENTRY org.eclipse.ui 4 0 2011-12-16 17:17:30.825
|
||||||
|
!MESSAGE Unhandled event loop exception
|
||||||
|
!STACK 0
|
||||||
|
org.eclipse.swt.SWTError: No more handles [Unknown Mozilla path (MOZILLA_FIVE_HOME not set)]
|
||||||
|
at org.eclipse.swt.SWT.error(SWT.java:4109)
|
||||||
|
at org.eclipse.swt.browser.Mozilla.initMozilla(Mozilla.java:1739)
|
||||||
|
at org.eclipse.swt.browser.Mozilla.create(Mozilla.java:656)
|
||||||
|
at org.eclipse.swt.browser.Browser.<init>(Browser.java:119)
|
||||||
|
at com.aptana.git.ui.internal.actions.CommitDialog.createDiffArea(CommitDialog.java:237)
|
||||||
|
at com.aptana.git.ui.internal.actions.CommitDialog.createDialogArea(CommitDialog.java:158)
|
||||||
|
|
||||||
|
[...]
|
||||||
|
|
||||||
|
at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:620)
|
||||||
|
at org.eclipse.equinox.launcher.Main.basicRun(Main.java:575)
|
||||||
|
at org.eclipse.equinox.launcher.Main.run(Main.java:1408)
|
||||||
|
at org.eclipse.equinox.launcher.Main.main(Main.java:1384)
|
||||||
|
{{< /highlight >}}
|
||||||
|
|
||||||
|
To make it short, after having read [a](https://bugs.archlinux.org/task/5149)
|
||||||
|
[lot](https://bugs.archlinux.org/task/27130)
|
||||||
|
[of](https://github.com/eclipse-color-theme/eclipse-color-theme/issues/50)
|
||||||
|
[posts](https://bbs.archlinux.org/viewtopic.php?id=129982)
|
||||||
|
[about](http://forums.gentoo.org/viewtopic-t-827838-view-previous.html?sid=546c5717e2167c45d9b02f9f20ab36f4)
|
||||||
|
[this](<http://stackoverflow.com/questions/1017945/problem-with-aptana-studio-xulrunner-8-1)
|
||||||
|
[problem](http://www.eclipse.org/swt/faq.php#gtk64), it seemed it was enough
|
||||||
|
to give the path to Xulrunner to Aptana.
|
||||||
|
|
||||||
|
On my Arch Linux, it was
|
||||||
|
|
||||||
|
{{< highlight bash >}}
|
||||||
|
export MOZILLA_FIVE_HOME=/usr/lib/xulrunner-8.0
|
||||||
|
{{< /highlight >}}
|
||||||
|
|
||||||
|
Trying to start Aptana Studio, I had a new error. It simply stated
|
||||||
|
|
||||||
|
{{< highlight text >}}
|
||||||
|
XPCOM error -2147467261
|
||||||
|
{{< /highlight >}}
|
||||||
|
|
||||||
|
The solution is that Aptana Studio cannot work with the version of Xulrunner
|
||||||
|
in Arch Linux repositories because it is too recent.
|
||||||
|
|
||||||
|
To solve this problem, I had to install xulrunner 1.9.2 from AUR:
|
||||||
|
|
||||||
|
{{< highlight bash >}}
|
||||||
|
yaourt -S xulrunner192
|
||||||
|
{{< /highlight >}}
|
||||||
|
|
||||||
|
The PKGBUILD was broken this morning and ended in a 404 Error when fetching
|
||||||
|
sources. If you have the same problem, `here is an updated PKGBUILD
|
||||||
|
<https://gist.github.com/1486851>`__
|
||||||
|
|
||||||
|
Finally, I put
|
||||||
|
|
||||||
|
{{< highlight bash >}}
|
||||||
|
-Dorg.eclipse.swt.browser.XULRunnerPath=/usr/lib/xulrunner-1.9.2
|
||||||
|
{{< /highlight >}}
|
||||||
|
|
||||||
|
at the end of the `AptanaStudio3.ini` file in the Aptana Studio folder. For
|
||||||
|
the package in the Arch Linux repositories, this file is
|
||||||
|
`/usr/share/aptana/AptanaStudio3.ini`.
|
178
content/blog/004-locking-buzhug.md
Normal file
|
@ -0,0 +1,178 @@
|
||||||
|
---
|
||||||
|
tags: [Python, Buzhug, Database, Locks]
|
||||||
|
slug: locking-buzhug
|
||||||
|
title: Locking Buzhug
|
||||||
|
date: "2012-02-07T00:00:00+01:00"
|
||||||
|
summary: >
|
||||||
|
How to implement a cross-process, system-wide lock for Buzhug
|
||||||
|
---
|
||||||
|
|
||||||
|
I have recently decided to work with [Buzhug] on a project. As far as I can tell,
|
||||||
|
it has proven efficient, fast, easy to use and to maintain. However, I ran into
|
||||||
|
a few gotchas.
|
||||||
|
|
||||||
|
[Buzhug]: http://buzhug.sourceforge.net
|
||||||
|
|
||||||
|
Simple solutions are often the best
|
||||||
|
===================================
|
||||||
|
|
||||||
|
I came to use Buzhug for the following requirements:
|
||||||
|
|
||||||
|
- I needed a single table
|
||||||
|
- I did not want to add additional dependencies to the project
|
||||||
|
- The size of the table will average 5K entries (without having more than
|
||||||
|
10k entries in peaks)
|
||||||
|
|
||||||
|
And an additional (personal) one:
|
||||||
|
|
||||||
|
- I did not want to bother with SQL. Really not. no way!
|
||||||
|
|
||||||
|
That left me one option: pure-python embedded database.
|
||||||
|
|
||||||
|
After having considered a few libraries, I have been seduced by the way Buzhug
|
||||||
|
interface is close to manipulating python objects. And the benchmarks seemed
|
||||||
|
to show that it is performant enough for this project.
|
||||||
|
|
||||||
|
After a quick prototyping (1 day), the choice was done.
|
||||||
|
|
||||||
|
Then came a few weeks of development and the first stress tests...
|
||||||
|
|
||||||
|
|
||||||
|
And the real world came back fast
|
||||||
|
=================================
|
||||||
|
|
||||||
|
|
||||||
|
A few times a day, the application backed by this database is intensely used:
|
||||||
|
|
||||||
|
- It can be run up to 50 times simultaneously in separate python process
|
||||||
|
- Each run makes a read and a write/delete operation
|
||||||
|
|
||||||
|
This causes a race condition on the files used to store data, and concurent
|
||||||
|
writes corrupts database.
|
||||||
|
|
||||||
|
Using `buzhug.TS_Base` instead of `buzhug.Base` did not solve anything,
|
||||||
|
as the problem is not thread, but processes. What I need is a system-wide
|
||||||
|
cross-process lock.
|
||||||
|
|
||||||
|
|
||||||
|
Here is the answer
|
||||||
|
==================
|
||||||
|
|
||||||
|
First step was to find how to implement a cross-process, system-wide lock.
|
||||||
|
As it only has to work on Linux, the
|
||||||
|
[Lock class given by Chris from
|
||||||
|
Vmfarms](http://blog.vmfarms.com/2011/03/cross-process-locking-and.html) fits
|
||||||
|
perfectly. Here is a version slightly modified to make it a context manager :
|
||||||
|
|
||||||
|
|
||||||
|
{{< highlight python >}}
|
||||||
|
import fcntl
|
||||||
|
|
||||||
|
class PsLock:
|
||||||
|
"""
|
||||||
|
Taken from:
|
||||||
|
http://blog.vmfarms.com/2011/03/cross-process-locking-and.html
|
||||||
|
"""
|
||||||
|
def __init__(self, filename):
|
||||||
|
self.filename = filename
|
||||||
|
self.handle = open(filename, 'w')
|
||||||
|
|
||||||
|
# Bitwise OR fcntl.LOCK_NB if you need a non-blocking lock
|
||||||
|
def acquire(self):
|
||||||
|
fcntl.flock(self.handle, fcntl.LOCK_EX)
|
||||||
|
|
||||||
|
def release(self):
|
||||||
|
fcntl.flock(self.handle, fcntl.LOCK_UN)
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
self.handle.close()
|
||||||
|
|
||||||
|
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||||
|
if exc_type is None:
|
||||||
|
pass
|
||||||
|
self.release()
|
||||||
|
|
||||||
|
def __enter__(self):
|
||||||
|
self.acquire()
|
||||||
|
{{< /highlight >}}
|
||||||
|
|
||||||
|
The second step is to define a new class that inheritates from `buzhug.Base`
|
||||||
|
that uses `PsLock` (inspired by `TS_Base`):
|
||||||
|
|
||||||
|
|
||||||
|
{{< highlight python >}}
|
||||||
|
import buzhug
|
||||||
|
|
||||||
|
_lock = PsLock("/tmp/buzhug.lck")
|
||||||
|
|
||||||
|
class PS_Base(buzhug.Base):
|
||||||
|
|
||||||
|
def create(self,*args,**kw):
|
||||||
|
with _lock:
|
||||||
|
res = buzhug.Base.create(self,*args,**kw)
|
||||||
|
return res
|
||||||
|
|
||||||
|
def open(self,*args,**kw):
|
||||||
|
with _lock:
|
||||||
|
res = buzhug.Base.open(self,*args,**kw)
|
||||||
|
return res
|
||||||
|
|
||||||
|
def close(self,*args,**kw):
|
||||||
|
with _lock:
|
||||||
|
res = buzhug.Base.close(self,*args,**kw)
|
||||||
|
return res
|
||||||
|
|
||||||
|
def destroy(self,*args,**kw):
|
||||||
|
with _lock:
|
||||||
|
res = buzhug.Base.destroy(self,*args,**kw)
|
||||||
|
return res
|
||||||
|
|
||||||
|
def set_default(self,*args,**kw):
|
||||||
|
with _lock:
|
||||||
|
res = buzhug.Base.set_default(self,*args,**kw)
|
||||||
|
return res
|
||||||
|
|
||||||
|
def insert(self,*args,**kw):
|
||||||
|
with _lock:
|
||||||
|
res = buzhug.Base.insert(self,*args,**kw)
|
||||||
|
return res
|
||||||
|
|
||||||
|
def update(self,*args,**kw):
|
||||||
|
with _lock:
|
||||||
|
res = buzhug.Base.update(self,*args,**kw)
|
||||||
|
return res
|
||||||
|
|
||||||
|
def delete(self,*args,**kw):
|
||||||
|
with _lock:
|
||||||
|
res = buzhug.Base.delete(self,*args,**kw)
|
||||||
|
return res
|
||||||
|
|
||||||
|
def cleanup(self,*args,**kw):
|
||||||
|
with _lock:
|
||||||
|
res = buzhug.Base.cleanup(self,*args,**kw)
|
||||||
|
return res
|
||||||
|
|
||||||
|
def commit(self,*args,**kw):
|
||||||
|
with _lock:
|
||||||
|
res = buzhug.Base.commit(self,*args,**kw)
|
||||||
|
return res
|
||||||
|
|
||||||
|
def add_field(self,*args,**kw):
|
||||||
|
with _lock:
|
||||||
|
res = buzhug.Base.add_field(self,*args,**kw)
|
||||||
|
return res
|
||||||
|
|
||||||
|
def drop_field(self,*args,**kw):
|
||||||
|
with _lock:
|
||||||
|
res = buzhug.Base.drop_field(self,*args,**kw)
|
||||||
|
return res
|
||||||
|
{{< /highlight >}}
|
||||||
|
|
||||||
|
Now I just use
|
||||||
|
|
||||||
|
{{< highlight python >}}
|
||||||
|
database = PS_Base( ... )
|
||||||
|
{{< /highlight >}}
|
||||||
|
|
||||||
|
|
||||||
|
And all the errors have vanished.
|
|
@ -0,0 +1,35 @@
|
||||||
|
---
|
||||||
|
tags: [Sublime Text 2]
|
||||||
|
slug: automatically-open-sublime-text-projects-in-a-directory
|
||||||
|
title: Automatically open Sublime Text projects in a directory
|
||||||
|
date: "2013-05-15T00:00:00+02:00"
|
||||||
|
summary: >
|
||||||
|
How to automatically open Sublimetext with a file, a project or the current
|
||||||
|
directory according to the context.
|
||||||
|
---
|
||||||
|
|
||||||
|
I usually start Sublime Text 2 from the command line to work, depending
|
||||||
|
on the case, on the content of a directory or on a project (materialized
|
||||||
|
with a `*.sublime-project` file).
|
||||||
|
|
||||||
|
It ends up with one of the following commands :
|
||||||
|
|
||||||
|
- `subl .`
|
||||||
|
- `subl my-project.sublime-project`
|
||||||
|
|
||||||
|
Here is the snippet I added to my .bashrc file to have the `subl`
|
||||||
|
command automatically "guess" what I want. It does the following:
|
||||||
|
|
||||||
|
- If a path is given (subl "my/file.txt"), it opens the file.
|
||||||
|
- If nothing is given and a .sublime-project file exists in the current
|
||||||
|
directory, it opens it
|
||||||
|
- If nothing is given and no .sublime-project file has been found, it
|
||||||
|
opens the folder.
|
||||||
|
|
||||||
|
{{< highlight bash >}}
|
||||||
|
function project_aware_subl {
|
||||||
|
project_file=$(ls *.sublime-project 2>/dev/null | head -n 1)
|
||||||
|
subl ${*:-${project_file:-.}}
|
||||||
|
}
|
||||||
|
alias subl="project_aware_subl"
|
||||||
|
{{< /highlight >}}
|
395
content/blog/006-discourse-without-docker.md
Normal file
|
@ -0,0 +1,395 @@
|
||||||
|
---
|
||||||
|
title: Discourse without Docker
|
||||||
|
slug: discourse-without-docker
|
||||||
|
date: "2016-06-27T00:00:00+02:00"
|
||||||
|
tags: [discourse, docker]
|
||||||
|
summary: Detailed instructions on how to install Discourse and plugins without Docker.
|
||||||
|
---
|
||||||
|
|
||||||
|
{{< warning >}}
|
||||||
|
The only official method is [with docker]. You might not be able
|
||||||
|
to get support from Discourse by following this method.
|
||||||
|
|
||||||
|
[with docker]: http://blog.discourse.org/2014/04/install-discourse-in-under-30-minutes/
|
||||||
|
{{< /warning >}}
|
||||||
|
|
||||||
|
The team behind [Discourse] has chosen to only release Docker images of
|
||||||
|
their software. The rational behind it is: it is easier to only support
|
||||||
|
a single setup. I will not discuss that. It is their choice.
|
||||||
|
|
||||||
|
However, I don't like to use docker to deploy apps in prodution. I even
|
||||||
|
hate it. If you are like me, here are the steps I used to install it
|
||||||
|
and to set it up.
|
||||||
|
|
||||||
|
I use Debian servers in production, so the steps below are all debian
|
||||||
|
oriented.
|
||||||
|
|
||||||
|
{{< note >}}
|
||||||
|
This is not intended as a comprehensive guide. A lot of commands and
|
||||||
|
configuration files might need to be adapted to your environment.
|
||||||
|
|
||||||
|
It does not even tries to talk about important topics in production such as
|
||||||
|
security. This is left as an exercise to the reader.
|
||||||
|
{{< /note >}}
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
After all, Discourse is a rails application. It can be installed like
|
||||||
|
any other rails application:
|
||||||
|
|
||||||
|
First things first: Discourse uses Redis and PostgreSQL (or at least,
|
||||||
|
I prefer to use Postgres). I also use Nginx as a proxy to the
|
||||||
|
application. Install the external dependencies:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Add the reposirory for Redis
|
||||||
|
echo "deb http://packages.dotdeb.org jessie all" > /etc/apt/sources.list.d/dotdeb.list
|
||||||
|
wget https://www.dotdeb.org/dotdeb.gpg -O - | apt-key add -
|
||||||
|
|
||||||
|
# Add the repository for PostgreSQL:
|
||||||
|
echo "deb http://apt.postgresql.org/pub/repos/apt/ jessie-pgdg main" > /etc/apt/sources.list.d/postgresql.list
|
||||||
|
wget -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add -
|
||||||
|
|
||||||
|
apt-get update
|
||||||
|
apt-get install postgresql-9.5 redis-server nginx
|
||||||
|
```
|
||||||
|
|
||||||
|
Then, create a database for the application. Enter postgres command
|
||||||
|
line interface:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
su - postgres -c psql
|
||||||
|
```
|
||||||
|
|
||||||
|
and enter the following commands:
|
||||||
|
|
||||||
|
```sql
|
||||||
|
CREATE DATABASE discourse;
|
||||||
|
CREATE USER discourse;
|
||||||
|
ALTER USER discourse WITH ENCRYPTED PASSWORD 'password';
|
||||||
|
ALTER DATABASE discourse OWNER TO discourse;
|
||||||
|
\connect discourse
|
||||||
|
CREATE EXTENSION hstore;
|
||||||
|
CREATE EXTENSION pg_trgm;
|
||||||
|
```
|
||||||
|
|
||||||
|
Then, you can checkout the Discourse code:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
git clone https://github.com/discourse/discourse.git /path/to/discourse
|
||||||
|
|
||||||
|
# Optionally, checkout a specific tag
|
||||||
|
cd /path/to/discourse
|
||||||
|
git checkout v1.5.3
|
||||||
|
```
|
||||||
|
|
||||||
|
Then, go in the application top directory, and set it up as any rails
|
||||||
|
application:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Optionally setup rvm with ruby 1.9.3 minimum (I use 2.3.0)
|
||||||
|
rvm install 2.3.0
|
||||||
|
rvm use 2.3.0
|
||||||
|
|
||||||
|
# install dependencies
|
||||||
|
cd /path/to/discourse
|
||||||
|
RAILS_ENV bundle install
|
||||||
|
```
|
||||||
|
|
||||||
|
It's time to configure the application.
|
||||||
|
|
||||||
|
Here, Discourse has a little particularity: The production
|
||||||
|
configuration is located in the file `./config/discourse.conf`.
|
||||||
|
|
||||||
|
Create this file :
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cp config/discourse_defaults.conf config/discourse.conf
|
||||||
|
```
|
||||||
|
|
||||||
|
And edit it with your configuration. The main areas of interest are
|
||||||
|
configuration for the database and for the email server:
|
||||||
|
|
||||||
|
```ini
|
||||||
|
# host address for db server
|
||||||
|
# This is set to blank so it tries to use sockets first
|
||||||
|
db_host = localhost
|
||||||
|
|
||||||
|
# port running db server, no need to set it
|
||||||
|
db_port = 5432
|
||||||
|
|
||||||
|
# database name running discourse
|
||||||
|
db_name = discourse
|
||||||
|
|
||||||
|
# username accessing database
|
||||||
|
db_username = discourse
|
||||||
|
|
||||||
|
# password used to access the db
|
||||||
|
db_password = password
|
||||||
|
```
|
||||||
|
|
||||||
|
and for the SMTP server (in this example, we use Gmail):
|
||||||
|
|
||||||
|
```ini
|
||||||
|
# address of smtp server used to send emails
|
||||||
|
smtp_address = smtp.gmail.com
|
||||||
|
|
||||||
|
# port of smtp server used to send emails
|
||||||
|
smtp_port = 587
|
||||||
|
|
||||||
|
# domain passed to smtp server
|
||||||
|
smtp_domain = gmail.com
|
||||||
|
|
||||||
|
# username for smtp server
|
||||||
|
smtp_user_name = your-address@gmail.com
|
||||||
|
|
||||||
|
# password for smtp server
|
||||||
|
smtp_password = password
|
||||||
|
|
||||||
|
# smtp authentication mechanism
|
||||||
|
smtp_authentication = plain
|
||||||
|
|
||||||
|
# enable TLS encryption for smtp connections
|
||||||
|
smtp_enable_start_tls = true
|
||||||
|
```
|
||||||
|
|
||||||
|
Now, we can prepare discourse for production:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
RAILS_ENV=production bundle exec rake db:migrate
|
||||||
|
RAILS_ENV=production bundle exec rake assets:precompile
|
||||||
|
```
|
||||||
|
|
||||||
|
It is time to start the application. I usually use Puma to deploy
|
||||||
|
Rails app.
|
||||||
|
|
||||||
|
Create the file `config/puma.rb` in discourse directory. Following
|
||||||
|
content should be enough (for more info, see
|
||||||
|
[Puma's documentation]):
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
#!/usr/bin/env puma
|
||||||
|
|
||||||
|
application_path = '/home/discuss.waarp.org/discourse'
|
||||||
|
directory application_path
|
||||||
|
environment 'production'
|
||||||
|
daemonize false
|
||||||
|
pidfile "#{application_path}/tmp/pids/puma.pid"
|
||||||
|
state_path "#{application_path}/tmp/pids/puma.state"
|
||||||
|
bind "unix://#{application_path}/tmp/sockets/puma.socket"
|
||||||
|
```
|
||||||
|
|
||||||
|
From there, the application can be run with the following command :
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bundle exec puma -C config/puma.rb
|
||||||
|
```
|
||||||
|
|
||||||
|
Finally, setup nginx to forward requests to Discourse. Create the file
|
||||||
|
`/etc/nginx/conf.d/discourse.conf` with the following content :
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
upstream discourse {
|
||||||
|
server unix:/path/to/discourse/tmp/sockets/puma.socket;
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name example.com;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
try_files $uri @proxy;
|
||||||
|
}
|
||||||
|
|
||||||
|
location @proxy {
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
proxy_pass http://discourse;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Your very own forum with Discourse is setup!
|
||||||
|
|
||||||
|
## Service Management
|
||||||
|
|
||||||
|
According to your workflow, you can add systemd units to run discourse.
|
||||||
|
It needs at least two service definition:
|
||||||
|
|
||||||
|
1. Sidekiq, which is used to process asynchronous background tasks
|
||||||
|
2. Rails, for Discource itself.
|
||||||
|
|
||||||
|
With the services setup, services can be started/stopped/enabled with
|
||||||
|
`systemctl` commands.
|
||||||
|
|
||||||
|
But before that, if you use RVM, you must create a wrapper for the
|
||||||
|
environment (local ruby, and optional gemset) used by Discourse:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
rvm wrapper 2.3.0 systemd bundle
|
||||||
|
```
|
||||||
|
|
||||||
|
This creates an executable in `$rvm_bin_path` that you can call
|
||||||
|
in lieu of bundle that will automatically load the right envirnoment.
|
||||||
|
|
||||||
|
### Sidekiq
|
||||||
|
|
||||||
|
First, create a configuration for sidekiq. Create the file
|
||||||
|
`config/sidekiq.yml` in your discoure project with the following
|
||||||
|
content (for more info, see [Sidekiq's documentation]):
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
---
|
||||||
|
:concurrency: 5
|
||||||
|
:pidfile: tmp/pids/sidekiq.pid
|
||||||
|
staging:
|
||||||
|
:concurrency: 10
|
||||||
|
production:
|
||||||
|
:concurrency: 20
|
||||||
|
:queues:
|
||||||
|
- default
|
||||||
|
- critical
|
||||||
|
- low
|
||||||
|
```
|
||||||
|
|
||||||
|
Then, create the service unit for Sidekiq. Create the file
|
||||||
|
`/etc/systemd/system/discourse-sidekiq.service` with the
|
||||||
|
following content:
|
||||||
|
|
||||||
|
```ini
|
||||||
|
[Unit]
|
||||||
|
Description=discourse sidekiq service
|
||||||
|
After=multi-user.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
WorkingDirectory=/path/to/discourse
|
||||||
|
Environment=RAILS_ENV=production
|
||||||
|
ExecStart=/path/to/rvm/.rvm/bin/systemd_bundle exec sidekiq -C config/sidekiq.yml
|
||||||
|
Restart=always
|
||||||
|
RestartSec=10
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
```
|
||||||
|
|
||||||
|
### Discourse
|
||||||
|
|
||||||
|
For Discourse, just create the service unit for Puma. Create the file
|
||||||
|
`/etc/systemd/system/discourse.service` with the
|
||||||
|
following content:
|
||||||
|
|
||||||
|
```ini
|
||||||
|
[Unit]
|
||||||
|
Description=discourse service
|
||||||
|
After=discourse-sidekiq.service
|
||||||
|
Requires=discourse-sidekiq.service
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
WorkingDirectory=/path/to/discourse
|
||||||
|
Environment=RAILS_ENV=production
|
||||||
|
ExecStart=/path/to/rvm/.rvm/bin/systemd_bundle exec puma -C config/puma.rb
|
||||||
|
Restart=always
|
||||||
|
RestartSec=10
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
```
|
||||||
|
|
||||||
|
## Upgrades
|
||||||
|
|
||||||
|
Upgrades are even easier:
|
||||||
|
|
||||||
|
First read the release notes.
|
||||||
|
Then make backups of the code and the database.
|
||||||
|
Now you can checkout the newest version:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /path/to/discourse
|
||||||
|
git checkout vX.X.X
|
||||||
|
```
|
||||||
|
|
||||||
|
Install the new dependencies, run the migrations and rebuild the
|
||||||
|
assets:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
RAILS_ENV=production bundle install
|
||||||
|
RAILS_ENV=production bundle exec rake db:migrate
|
||||||
|
RAILS_ENV=production bundle exec rake assets:precompile
|
||||||
|
```
|
||||||
|
|
||||||
|
Restart Discourse:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
systemctl restart discourse
|
||||||
|
```
|
||||||
|
|
||||||
|
What can go wrong? If if I do not give any solution here, it is always
|
||||||
|
recoverable (hence the backups!).
|
||||||
|
|
||||||
|
- The database migration failed (restore the database with your backup,
|
||||||
|
fix the problem and try again!)
|
||||||
|
- The plugins are not compatible with the latest version (rollback to
|
||||||
|
the previous working solution and wit for them to be compatible)
|
||||||
|
|
||||||
|
## Plugins
|
||||||
|
|
||||||
|
Discourse plugins can be handles the same way.
|
||||||
|
|
||||||
|
### Plugin Installation
|
||||||
|
|
||||||
|
Install the plugin with the url of its repository:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /path/to discourse
|
||||||
|
RAILS_ENV=production bundle exec rake plugin:install[URL]
|
||||||
|
```
|
||||||
|
|
||||||
|
Install the new dependencies, run the migrations and rebuild the
|
||||||
|
assets:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
RAILS_ENV=production bundle install
|
||||||
|
RAILS_ENV=production bundle exec rake db:migrate
|
||||||
|
RAILS_ENV=production bundle exec rake assets:precompile
|
||||||
|
```
|
||||||
|
|
||||||
|
Restart Discourse:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
systemctl restart discourse
|
||||||
|
```
|
||||||
|
|
||||||
|
### Upgrade
|
||||||
|
|
||||||
|
To upgrade a specific plugin, use the following command:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
RAILS_ENV=production bundle exec rake plugin:update[ID]
|
||||||
|
```
|
||||||
|
|
||||||
|
You can also upgrade all plugins at once with the command:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
RAILS_ENV=production bundle exec rake plugin:update_all
|
||||||
|
```
|
||||||
|
|
||||||
|
Then, install the new dependencies, run the migrations and rebuild the
|
||||||
|
assets:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
RAILS_ENV=production bundle install
|
||||||
|
RAILS_ENV=production bundle exec rake db:migrate
|
||||||
|
RAILS_ENV=production bundle exec rake assets:precompile
|
||||||
|
```
|
||||||
|
|
||||||
|
and restart Discourse:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
systemctl restart discourse
|
||||||
|
```
|
||||||
|
|
||||||
|
[Discourse]: http://www.discourse.org/
|
||||||
|
[Sidekiq's documentation]: https://github.com/mperham/sidekiq/wiki/Advanced-Options
|
||||||
|
[Puma's documentation]: https://github.com/puma/puma
|
6
content/blog/_index.md
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
+++
|
||||||
|
title = 'Blog'
|
||||||
|
date = 2025-06-04T23:00:00Z
|
||||||
|
draft = false
|
||||||
|
+++
|
||||||
|
|
0
data/.gitkeep
Normal file
68
hugo.yaml
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
baseURL: 'https://bcarlin.net/'
|
||||||
|
languageCode: 'fr-FR'
|
||||||
|
title: 'Bruno Carlin'
|
||||||
|
theme: ['bcarlin']
|
||||||
|
uglyUrls: true
|
||||||
|
copyright: '© 2025 Bruno Carlin'
|
||||||
|
summaryLength: 15
|
||||||
|
params:
|
||||||
|
author:
|
||||||
|
email: mail@bcarlin.net
|
||||||
|
name: Bruno Carlin
|
||||||
|
|
||||||
|
outputs:
|
||||||
|
home:
|
||||||
|
- html
|
||||||
|
section:
|
||||||
|
- html
|
||||||
|
- rss
|
||||||
|
taxonomy:
|
||||||
|
- html
|
||||||
|
term:
|
||||||
|
- html
|
||||||
|
- rss
|
||||||
|
|
||||||
|
outputFormats:
|
||||||
|
rss:
|
||||||
|
noUgly: false
|
||||||
|
|
||||||
|
markup:
|
||||||
|
highlight:
|
||||||
|
noClasses: false
|
||||||
|
|
||||||
|
menus:
|
||||||
|
main:
|
||||||
|
- name: 'Home'
|
||||||
|
pageRef: '/'
|
||||||
|
weight: 10
|
||||||
|
params:
|
||||||
|
class: "u-url"
|
||||||
|
|
||||||
|
- name: 'Blog'
|
||||||
|
pageRef: '/blog'
|
||||||
|
weight: 20
|
||||||
|
|
||||||
|
secondary:
|
||||||
|
- name: '@bcarlin@hachyderm.io'
|
||||||
|
url: 'https://hachyderm.io/@bcarlin'
|
||||||
|
weight: 10
|
||||||
|
params:
|
||||||
|
rel: "me"
|
||||||
|
iconName: "mastodon-fill"
|
||||||
|
iconTitle: "Mastodon"
|
||||||
|
|
||||||
|
- name: 'LinkedIn'
|
||||||
|
url: 'https://www.linkedin.com/in/brunocarlin'
|
||||||
|
weight: 20
|
||||||
|
params:
|
||||||
|
rel: "me"
|
||||||
|
iconName: "linkedin-fill"
|
||||||
|
iconTitle: "LinkedIn"
|
||||||
|
|
||||||
|
- name: 'GPG Key'
|
||||||
|
url: '/bcarlin.gpg'
|
||||||
|
weight: 30
|
||||||
|
params:
|
||||||
|
class: "u-key"
|
||||||
|
iconName: "key-fill"
|
||||||
|
iconTitle: "GPG Public Key"
|
0
i18n/.gitkeep
Normal file
0
layouts/.gitkeep
Normal file
73
static/.well-known/keybase.txt
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
==================================================================
|
||||||
|
https://keybase.io/bcarlin
|
||||||
|
--------------------------------------------------------------------
|
||||||
|
|
||||||
|
I hereby claim:
|
||||||
|
|
||||||
|
* I am an admin of https://bcarlin.net
|
||||||
|
* I am bcarlin (https://keybase.io/bcarlin) on keybase.
|
||||||
|
* I have a public key with fingerprint 08C4 4FD4 4533 1DEF 6696 FB5D 2298 63E4 199F C2F8
|
||||||
|
|
||||||
|
To do so, I am signing this object:
|
||||||
|
|
||||||
|
{
|
||||||
|
"body": {
|
||||||
|
"key": {
|
||||||
|
"eldest_kid": "01018f3e0c8806ef31d1cb43ec77b9eca048310cde351f76ee10a02cdfc97418eb660a",
|
||||||
|
"fingerprint": "08c44fd445331def6696fb5d229863e4199fc2f8",
|
||||||
|
"host": "keybase.io",
|
||||||
|
"key_id": "229863e4199fc2f8",
|
||||||
|
"kid": "01018f3e0c8806ef31d1cb43ec77b9eca048310cde351f76ee10a02cdfc97418eb660a",
|
||||||
|
"uid": "5b44e763b8950ddd1fb7d76f03513f19",
|
||||||
|
"username": "bcarlin"
|
||||||
|
},
|
||||||
|
"service": {
|
||||||
|
"hostname": "bcarlin.net",
|
||||||
|
"protocol": "https:"
|
||||||
|
},
|
||||||
|
"type": "web_service_binding",
|
||||||
|
"version": 1
|
||||||
|
},
|
||||||
|
"ctime": 1748993287,
|
||||||
|
"expire_in": 157680000,
|
||||||
|
"prev": "14f3ba62d0615cfe0295d5c6924e5175fe57e3e414def1bdd48cfd793cdc78ad",
|
||||||
|
"seqno": 2,
|
||||||
|
"tag": "signature"
|
||||||
|
}
|
||||||
|
|
||||||
|
which yields the signature:
|
||||||
|
|
||||||
|
-----BEGIN PGP MESSAGE-----
|
||||||
|
|
||||||
|
owGtUmtMHFUY3aVgZStKLZWqJaQDjegSOrPz3vDYxFoRQVpoqwiWzOMOO90nOwNI
|
||||||
|
t8hqC1sCC0hbG9jV1gbCaxuLUNTSlECFaEwQxIipMQTE1m2DgKaBpJE6Q+wPE396
|
||||||
|
/9zcc88593zf/ZqjNml02sbdr114cNdv034TYjXmjJqn3RDr4CshoxuygI0NWHkg
|
||||||
|
ycUWkYeMEIzACCWgAOYoCiaAgCI8wrEYCjiSZGnAMTBGoQjM8QDFEYEkAEBgBjZw
|
||||||
|
vMDRJIZQgCUImIGSIUG0lwCX0yXaZdWW4jBM4DEMRxVHIBAETQgszhsMNEWgAENo
|
||||||
|
WuAMAqUIzQ5JVSjhWEYCKaJDwZRD8Ua8/+D/z7nLNuxwFsMASaAsReMwz/OIwJI8
|
||||||
|
SQiwokYFhFaJEnDZGRtQ2CzHuKyiHapKhhSwXOSA2le1kH8TUuxAVpROl0N2cA6r
|
||||||
|
cmGWZadkVIVypVNlVgC2+B+PYla080oXFUU5cEmiww4ZEYXJyaJqipAYRdOogSKT
|
||||||
|
IfC2U3SBYlFl4CRBwcpS3wHliiWCCSjLEAb+MEwgOCcA2EDjPM4RtAEDOELiAsBJ
|
||||||
|
oDYVU34GYXkeoziBJ2mU4zmSYnhILavU7oCMBiUnU6J4SmKJnZHLXACq0tWFoeEa
|
||||||
|
rU4T99Su8NjY1v01+3IShvWTCQ8nLyJMHTuNLjL6IfLmXLQmcD7ksVFNfWtfZFw4
|
||||||
|
7kOuDdZ9dTJq6Yyr1Hs9lri/9Iq1d22s1pK4tSAm1d/wwOiToaNLe+C1QO6ch25M
|
||||||
|
6q4c6/N1ts51hJ80h/oHZ24NZs6vN4Wqj9cHPxma3bL3crCA60idSa36aMtEgceX
|
||||||
|
vGx01xt0loCuZcesdqgp/d5zkeyVvvSUg/mPPxt92lzddeLc1e2YfnlxWZt4o/ax
|
||||||
|
MGmbH7vr3byPyHk0PjI8RiJKGwqvt+Xkgt9au0591n5Tk17kPeEfTDzkBrXl+gKb
|
||||||
|
3c2k9W3tHm3WbloKnlv+YFVz9HzL5JHxmRf/9JR8mXcg4Gs7/bERaYxr/dn0+Y63
|
||||||
|
gDmgy2uQWrJfYAa2TUzV7X59z3f777zcOzT1rSdT/PRdc+jYk4fz3su6uoKmcDWm
|
||||||
|
l+7ELSZAI5HP+2/o/1jN8l5ZS9o+OWrL90DW3vSBgxnVm99of3Uy23FgYZfQKcYU
|
||||||
|
zcdG1K7csvrTz85UyLnV98um/rqY9sw7AVPloZG5az0NP0YVOpHfFwsL9A1dq4v9
|
||||||
|
EeV7vx83Rf4Q2jm+Xt3+yC9ed+ECIHqPBFf4S3U9w7cT7g3M9p+dyOxstsyvHXvC
|
||||||
|
lL1afzs/7/0e4etg/HAwaf1y2s4z08KHXdO+n4riLTfxhV8r4lBppLKIKVw2NWX1
|
||||||
|
XGRaO7pH8elL+gUetelyotrax3x/Aw==
|
||||||
|
=JR35
|
||||||
|
-----END PGP MESSAGE-----
|
||||||
|
|
||||||
|
And finally, I am proving ownership of this host by posting or
|
||||||
|
appending to this document.
|
||||||
|
|
||||||
|
View my publicly-auditable identity here: https://keybase.io/bcarlin
|
||||||
|
|
||||||
|
==================================================================
|
||||||
|
|
148
static/bcarlin.gpg
Normal file
|
@ -0,0 +1,148 @@
|
||||||
|
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||||
|
|
||||||
|
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
|
||||||
|
=ebDO
|
||||||
|
-----END PGP PUBLIC KEY BLOCK-----
|
5
themes/bcarlin/archetypes/default.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
+++
|
||||||
|
date = '{{ .Date }}'
|
||||||
|
draft = true
|
||||||
|
title = '{{ replace .File.ContentBaseName "-" " " | title }}'
|
||||||
|
+++
|
353
themes/bcarlin/assets/static/css/bcarlin.css
Normal file
|
@ -0,0 +1,353 @@
|
||||||
|
@import url("/static/css/syntax-light.css") only screen and (prefers-color-scheme: light);
|
||||||
|
@import url('/static/css/syntax-dark.css') only screen and (prefers-color-scheme: dark);
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Gentium Plus';
|
||||||
|
src: url('/static/fonts/GentiumPlus-Regular.woff2');
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Gentium Plus';
|
||||||
|
font-style: italic;
|
||||||
|
src: url('/static/fonts/GentiumPlus-Italic.woff2');
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Gentium Plus';
|
||||||
|
font-weight: bold;
|
||||||
|
src: url('/static/fonts/GentiumPlus-Bold.woff2');
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Gentium Plus';
|
||||||
|
font-weight: bold;
|
||||||
|
font-style: italic;
|
||||||
|
src: url('/static/fonts/GentiumPlus-BoldItalic.woff2');
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Nunito Sans';
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 200 1000;
|
||||||
|
font-stretch: 100%;
|
||||||
|
src: url('/static/fonts/NunitoSans-Italic.woff2') format('woff2');
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Nunito Sans';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 200 1000;
|
||||||
|
font-stretch: 100%;
|
||||||
|
src: url('/static/fonts/NunitoSans.woff2') format('woff2');
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* pico.css variables overrides
|
||||||
|
*/
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--pico-font-family: 'Gentium Plus', serif, var(--pico-font-family-emoji);
|
||||||
|
--pico-font-family-sans-serif: 'Nunito Sans', sans-serif, var(--pico-font-family-emoji);
|
||||||
|
--pico-card-sectioning-background-color: transparent;
|
||||||
|
--pico-typography-spacing-vertical: calc(1.5 * var(--pico-spacing));
|
||||||
|
--markup-color: rgb(194 198 207 / 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media only screen and (prefers-color-scheme: light) {
|
||||||
|
:root:not([data-theme=dark]), [data-theme=light] {
|
||||||
|
--pico-background-color: rgb(247 249 252 / 100%);
|
||||||
|
--pico-card-background-color: rgb(255 255 255 / 100%);
|
||||||
|
--pico-card-sectioning-background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admonition.warning {
|
||||||
|
--admonition-background-color: rgb(255 240 219 / 100%);
|
||||||
|
--admonition-border-color: rgb(255 208 143 / 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.admonition.note {
|
||||||
|
--admonition-background-color: rgb(219 238 255 / 100%);
|
||||||
|
--admonition-border-color: rgb(142 202 255 / 100%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media only screen and (prefers-color-scheme: dark) {
|
||||||
|
.admonition.warning {
|
||||||
|
--admonition-background-color: rgb(52 46 38 / 100%);
|
||||||
|
--admonition-border-color: rgb(255 208 143 / 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.admonition.note {
|
||||||
|
--admonition-background-color: rgb(48 59 68 / 100%);
|
||||||
|
--admonition-border-color: rgb(142 202 255 / 100%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
article {
|
||||||
|
--pico-card-box-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
header nav :where(a:not([role="button"])):is([aria-current]:not([aria-current="false"]),:hover,:active,:focus),
|
||||||
|
header nav [role="link"]:is([aria-current]:not([aria-current="false"]),:hover,:active,:focus) {
|
||||||
|
--pico-text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
header :where(a:not([role="button"])) {
|
||||||
|
--pico-text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
max-width: 40em;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Layout Styling
|
||||||
|
*/
|
||||||
|
|
||||||
|
.mobile-header {
|
||||||
|
padding: 0 var(--pico-block-spacing-horizontal);
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
background-color: rgb(from var(--pico-background-color) r g b / 80%);
|
||||||
|
backdrop-filter: blur(3px);
|
||||||
|
font-family: var(--pico-font-family-sans-serif);
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-header img {
|
||||||
|
height: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#menu-close, #menu-close:hover, #menu-toggle {
|
||||||
|
color: inherit;
|
||||||
|
background-color: inherit;
|
||||||
|
border: none;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
background-color: var(--pico-background-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
body > header {
|
||||||
|
width: 100vw;
|
||||||
|
text-align: center;
|
||||||
|
height: 100vh;
|
||||||
|
position: fixed;
|
||||||
|
left: -100vw;
|
||||||
|
top: 0;
|
||||||
|
background-color: var(--pico-background-color);
|
||||||
|
z-index: 100;
|
||||||
|
transition: left 0.5s;
|
||||||
|
overflow: hidden auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
body > header.active {
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
body > header nav, body > header nav ul {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
body > header .u-logo {
|
||||||
|
width: 5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
body > header .title {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
font-weight: bold;
|
||||||
|
font-family: var(--pico-font-family-sans-serif);
|
||||||
|
margin-top: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
body > header nav {
|
||||||
|
font-size: 1rem;
|
||||||
|
font-family: var(--pico-font-family-sans-serif);
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
body > header nav li {
|
||||||
|
padding: 0;
|
||||||
|
padding: calc( var(--pico-nav-element-spacing-vertical) / 4)
|
||||||
|
calc( var(--pico-nav-element-spacing-horizontal) / 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
body > header a {
|
||||||
|
color: var(--pico-color);
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
body > header > a {
|
||||||
|
float: right;
|
||||||
|
padding: 0 1.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-primary {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
body > footer p {
|
||||||
|
font-size: 0.7em;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (width >= 1024px) {
|
||||||
|
.mobile-header, #menu-close {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
body > header {
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 15em;
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
margin-right: 0;
|
||||||
|
margin-left: 18em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Content Styling
|
||||||
|
*/
|
||||||
|
|
||||||
|
main > article {
|
||||||
|
text-align: justify;
|
||||||
|
}
|
||||||
|
|
||||||
|
main > article > header {
|
||||||
|
margin-bottom: calc(2 * var(--pico-block-spacing-vertical));
|
||||||
|
}
|
||||||
|
|
||||||
|
.metadata {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.tags, ul.tags li {
|
||||||
|
list-style-type: none;
|
||||||
|
display: inline;
|
||||||
|
padding-left: 0;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tags li:not(:last-child)::after {
|
||||||
|
content: ", ";
|
||||||
|
}
|
||||||
|
|
||||||
|
ul > li {
|
||||||
|
list-style-type: disc;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1, h2, h3, h4, h5, h6 {
|
||||||
|
font-family: var(--pico-font-family-sans-serif);
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 1.7rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1::before {
|
||||||
|
content: "# ";
|
||||||
|
color: var(--markup-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
font-size: 1.3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2::before {
|
||||||
|
content: "## ";
|
||||||
|
color: var(--markup-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3::before {
|
||||||
|
content: "### ";
|
||||||
|
color: var(--markup-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
h4 {
|
||||||
|
font-size: 1.1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
h4::before {
|
||||||
|
content: "#### ";
|
||||||
|
color: var(--markup-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
h5 {
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
h5::before {
|
||||||
|
content: "##### ";
|
||||||
|
color: var(--markup-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
h6 {
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
h6::before {
|
||||||
|
content: "###### ";
|
||||||
|
color: var(--markup-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
article header h1, article header p {
|
||||||
|
margin-bottom: calc(0.1 * var(--pico-typography-spacing-vertical));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
h1 a, h2 a, h3 a, h4 a, h5 a, h6 a {
|
||||||
|
color: var(--pico-muted-color);
|
||||||
|
text-decoration: none;
|
||||||
|
margin-left: 0.4rem;
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre {
|
||||||
|
margin-bottom: calc(1.5 * var(--pico-spacing));
|
||||||
|
}
|
||||||
|
|
||||||
|
code, kdb, pre {
|
||||||
|
font-size: 0.8em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Admonitions
|
||||||
|
*/
|
||||||
|
|
||||||
|
.admonition {
|
||||||
|
font-size: 0.9em;
|
||||||
|
padding: 1em;
|
||||||
|
border-left: solid 4px var(--admonition-border-color);
|
||||||
|
background-color: var(--admonition-background-color);
|
||||||
|
margin-bottom: var(--pico-typography-spacing-vertical);
|
||||||
|
border-radius: var(--pico-border-radius);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Post lists
|
||||||
|
*/
|
||||||
|
|
||||||
|
.h-feed article:not(:last-child) {
|
||||||
|
border-bottom: var(--pico-border-width) solid var(--pico-card-border-color);
|
||||||
|
}
|
4
themes/bcarlin/assets/static/css/pico.min.css
vendored
Normal file
3099
themes/bcarlin/assets/static/css/remixicon.css
Normal file
22
themes/bcarlin/assets/static/js/bcarlin.js
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
// Listens for clicks on the mobile menu toggle and the main menu close toggle.
|
||||||
|
window.addEventListener('click', function(e) {
|
||||||
|
const menu = document.getElementById('menu'),
|
||||||
|
menuToggle = document.getElementById('menu-toggle'),
|
||||||
|
body = document.body;
|
||||||
|
|
||||||
|
if (e.target.matches('#menu-toggle, #menu-toggle i')) {
|
||||||
|
menu.classList.toggle('active');
|
||||||
|
menu.ariaHidden = false;
|
||||||
|
menuToggle.ariaExpanded = true;
|
||||||
|
body.style.overflow = 'hidden';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e.target.matches('#menu-close, #menu-close i')) {
|
||||||
|
menu.classList.remove('active');
|
||||||
|
menu.ariaHidden = true;
|
||||||
|
menuToggle.ariaExpanded = false;
|
||||||
|
body.style.overflow = 'auto';
|
||||||
|
}
|
||||||
|
});
|
9
themes/bcarlin/content/_index.md
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
+++
|
||||||
|
title = 'Home'
|
||||||
|
date = 2023-01-01T08:00:00-07:00
|
||||||
|
draft = false
|
||||||
|
+++
|
||||||
|
|
||||||
|
Laborum voluptate pariatur ex culpa magna nostrud est incididunt fugiat
|
||||||
|
pariatur do dolor ipsum enim. Consequat tempor do dolor eu. Non id id anim anim
|
||||||
|
excepteur excepteur pariatur nostrud qui irure ullamco.
|
24
themes/bcarlin/hugo.toml
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
baseURL = 'https://example.org/'
|
||||||
|
languageCode = 'en-US'
|
||||||
|
title = 'My New Hugo Site'
|
||||||
|
|
||||||
|
[menus]
|
||||||
|
[[menus.main]]
|
||||||
|
name = 'Home'
|
||||||
|
pageRef = '/'
|
||||||
|
weight = 10
|
||||||
|
|
||||||
|
[[menus.main]]
|
||||||
|
name = 'Posts'
|
||||||
|
pageRef = '/posts'
|
||||||
|
weight = 20
|
||||||
|
|
||||||
|
[[menus.main]]
|
||||||
|
name = 'Tags'
|
||||||
|
pageRef = '/tags'
|
||||||
|
weight = 30
|
||||||
|
|
||||||
|
[module]
|
||||||
|
[module.hugoVersion]
|
||||||
|
extended = false
|
||||||
|
min = '0.146.0'
|
15
themes/bcarlin/layouts/_partials/footer.html
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
|
||||||
|
<p>
|
||||||
|
© {{ now.Year }} Bruno Carlin
|
||||||
|
<br>
|
||||||
|
The content of this site is licensed under
|
||||||
|
<a href="https://creativecommons.org/licenses/by-nc/4.0/" rel="license">
|
||||||
|
Creative Commons Attribution-NonCommercial 4.0
|
||||||
|
International
|
||||||
|
</a>
|
||||||
|
{{partial "icon.html" (dict "icon" "creative-commons-fill" "label" "Creative Commons Logo")}}
|
||||||
|
{{partial "icon.html" (dict "icon" "creative-commons-by-fill" "label" "Creative Commons Attribution Logo")}}
|
||||||
|
{{partial "icon.html" (dict "icon" "creative-commons-nc-fill" "label" "Creative Commons Non Commercial Logo")}}
|
||||||
|
<br>
|
||||||
|
Generated with <a href="https://gohugo.io">Hugo</a> using a custom theme.
|
||||||
|
</p>
|
24
themes/bcarlin/layouts/_partials/head.html
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
||||||
|
|
||||||
|
{{ with .Site.GetPage "/blog" }}
|
||||||
|
{{- with .OutputFormats.Get "rss" }}
|
||||||
|
{{- printf `<link rel=%q type=%q href=%q title=%q>` .Rel .MediaType.Type .Permalink site.Title | safeHTML }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
{{ if eq .Kind "term" }}
|
||||||
|
{{- with .OutputFormats.Get "rss" }}
|
||||||
|
{{- printf `<link rel=%q type=%q href=%q title='%s | %s'>` .Rel .MediaType.Type .Permalink $.Title site.Title | safeHTML }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
<link rel="icon" type="image/png" href="/favicon-96x96.png" sizes="96x96" />
|
||||||
|
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||||
|
<link rel="shortcut icon" href="/favicon.ico" />
|
||||||
|
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
|
||||||
|
<meta name="apple-mobile-web-app-title" content="bcarlin.net" />
|
||||||
|
<link rel="manifest" href="/site.webmanifest" />
|
||||||
|
|
||||||
|
<title>{{ if .IsHome }}{{ site.Title }}{{ else }}{{ printf "%s | %s" .Title site.Title }}{{ end }}</title>
|
||||||
|
{{ partialCached "head/css.html" . }}
|
||||||
|
{{ partialCached "head/js.html" . }}
|
29
themes/bcarlin/layouts/_partials/head/css.html
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
{{- with resources.Get "static/css/pico.min.css" }}
|
||||||
|
{{- if hugo.IsDevelopment }}
|
||||||
|
<link rel="stylesheet" href="{{ .RelPermalink }}">
|
||||||
|
{{- else }}
|
||||||
|
{{- with . | minify | fingerprint }}
|
||||||
|
<link rel="stylesheet" href="{{ .RelPermalink }}" integrity="{{ .Data.Integrity }}" crossorigin="anonymous">
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{- with resources.Get "static/css/remixicon.css" }}
|
||||||
|
{{- if hugo.IsDevelopment }}
|
||||||
|
<link rel="stylesheet" href="{{ .RelPermalink }}">
|
||||||
|
{{- else }}
|
||||||
|
{{- with . | minify | fingerprint }}
|
||||||
|
<link rel="stylesheet" href="{{ .RelPermalink }}" integrity="{{ .Data.Integrity }}" crossorigin="anonymous">
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{- with resources.Get "static/css/bcarlin.css" }}
|
||||||
|
{{- if hugo.IsDevelopment }}
|
||||||
|
<link rel="stylesheet" href="{{ .RelPermalink }}">
|
||||||
|
{{- else }}
|
||||||
|
{{- with . | minify | fingerprint }}
|
||||||
|
<link rel="stylesheet" href="{{ .RelPermalink }}" integrity="{{ .Data.Integrity }}" crossorigin="anonymous">
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
16
themes/bcarlin/layouts/_partials/head/js.html
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
{{- with resources.Get "static/js/bcarlin.js" }}
|
||||||
|
{{- $opts := dict
|
||||||
|
"minify" (not hugo.IsDevelopment)
|
||||||
|
"sourceMap" (cond hugo.IsDevelopment "external" "")
|
||||||
|
"targetPath" "static/js/bcarlin.js"
|
||||||
|
}}
|
||||||
|
{{- with . | js.Build $opts }}
|
||||||
|
{{- if hugo.IsDevelopment }}
|
||||||
|
<script src="{{ .RelPermalink }}"></script>
|
||||||
|
{{- else }}
|
||||||
|
{{- with . | fingerprint }}
|
||||||
|
<script src="{{ .RelPermalink }}" integrity="{{ .Data.Integrity }}" crossorigin="anonymous"></script>
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
20
themes/bcarlin/layouts/_partials/header.html
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
<a id="menu-close">
|
||||||
|
{{partial "icon.html" (dict "icon" "close-large-fill" "label" "Close the menu")}}
|
||||||
|
</a>
|
||||||
|
<nav id="main-menu" aria-label="Main menu">
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<a href="/" class="contrast">
|
||||||
|
<img class="u-logo" src="/static/logo.svg" alt="Logo"/>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<span class="title p-name"><a href="/" class="contrast">Bruno Carlin</a></span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
{{ partial "menu.html" (dict "menuID" "main" "class" "menu-primary" "page" .) }}
|
||||||
|
|
||||||
|
{{ partial "menu.html" (dict "menuID" "secondary" "class" "menu-secondary" "page" .) }}
|
||||||
|
</nav>
|
||||||
|
|
1
themes/bcarlin/layouts/_partials/icon.html
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<i class="ri-{{ .icon }}" role="img" title="{{ .label }}"></i>
|
62
themes/bcarlin/layouts/_partials/menu.html
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
{{- /*
|
||||||
|
Renders a menu for the given menu ID.
|
||||||
|
|
||||||
|
@context {page} page The current page.
|
||||||
|
@context {string} menuID The menu ID.
|
||||||
|
|
||||||
|
@example: {{ partial "menu.html" (dict "menuID" "main" "page" .) }}
|
||||||
|
*/}}
|
||||||
|
|
||||||
|
{{- $page := .page }}
|
||||||
|
{{- $menuID := .menuID }}
|
||||||
|
{{- $menuClass := .class }}
|
||||||
|
|
||||||
|
{{- with index site.Menus $menuID }}
|
||||||
|
<ul class="{{ $menuClass }}">
|
||||||
|
{{- partial "inline/menu/walk.html" (dict "page" $page "menuEntries" .) }}
|
||||||
|
</ul>
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{- define "_partials/inline/menu/walk.html" }}
|
||||||
|
{{- $page := .page }}
|
||||||
|
{{- range .menuEntries }}
|
||||||
|
{{- $attrs := dict "href" .URL "class" "" }}
|
||||||
|
{{- if $page.IsMenuCurrent .Menu . }}
|
||||||
|
{{- $attrs = merge $attrs (dict "class" "active" "aria-current" "page") }}
|
||||||
|
{{- else if $page.HasMenuCurrent .Menu .}}
|
||||||
|
{{- $attrs = merge $attrs (dict "class" "ancestor" "aria-current" "true") }}
|
||||||
|
{{- end }}
|
||||||
|
{{- $attrs = merge $attrs (dict "class" (print $attrs.class " contrast")) }}
|
||||||
|
{{- if (ne .Params.class nil) }}
|
||||||
|
{{- $attrs = merge $attrs (dict "class" (print $attrs.class " " .Params.class)) }}
|
||||||
|
{{- end }}
|
||||||
|
{{- if (ne .Params.rel nil) }}
|
||||||
|
{{- $attrs = merge $attrs (dict "rel" .Params.rel) }}
|
||||||
|
{{- end }}
|
||||||
|
{{- $name := .Name }}
|
||||||
|
{{- with .Identifier }}
|
||||||
|
{{- with T . }}
|
||||||
|
{{- $name = . }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
<li>
|
||||||
|
<a
|
||||||
|
{{- range $k, $v := $attrs }}
|
||||||
|
{{- with $v }}
|
||||||
|
{{- printf " %s=%q" $k $v | safeHTMLAttr }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end -}}
|
||||||
|
>
|
||||||
|
{{- if (ne .Params.iconName nil) }}
|
||||||
|
{{partial "icon.html" (dict "icon" .Params.iconName "label" .Params.iconTitle)}}
|
||||||
|
{{- end }}
|
||||||
|
{{ $name }}
|
||||||
|
</a>
|
||||||
|
{{- with .Children }}
|
||||||
|
<ul>
|
||||||
|
{{- partial "inline/menu/walk.html" (dict "page" $page "menuEntries" .) }}
|
||||||
|
</ul>
|
||||||
|
{{- end }}
|
||||||
|
</li>
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
10
themes/bcarlin/layouts/_partials/tags.html
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
{{- with .GetTerms "tags" }}
|
||||||
|
<div>
|
||||||
|
{{ partial "icon.html" (dict "icon" "hashtag" "label" "Tags") }}
|
||||||
|
<ul class="tags">
|
||||||
|
{{- range . }}
|
||||||
|
<li><a href="{{ .RelPermalink }}" rel="tag" class="p-category">{{ .LinkTitle }}</a></li>
|
||||||
|
{{- end }}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
{{- end }}
|
38
themes/bcarlin/layouts/baseof.html
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="{{ site.Language.LanguageCode }}" dir="{{ or site.Language.LanguageDirection `ltr` }}">
|
||||||
|
<head>
|
||||||
|
{{ partial "head.html" . }}
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<nav class="mobile-header" aria-label="Mobile menu">
|
||||||
|
<ul>
|
||||||
|
<li><a href="/"><img src="/static/logo.svg" alt="Logo"/></a></li>
|
||||||
|
</ul>
|
||||||
|
<ul>
|
||||||
|
<li><a href="/" class="contrast">Bruno Carlin</a></li>
|
||||||
|
</ul>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<a
|
||||||
|
id="menu-toggle"
|
||||||
|
role="button"
|
||||||
|
aria-controls="menu"
|
||||||
|
aria-expanded="false"
|
||||||
|
aria-label="Toggle the main menu"
|
||||||
|
>
|
||||||
|
{{partial "icon.html" (dict "icon" "menu-fill" "label" "Open the menu")}}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
<header id="menu" class="h-card">
|
||||||
|
{{ partial "header.html" . }}
|
||||||
|
</header>
|
||||||
|
<main class="container">
|
||||||
|
{{ block "main" . }}{{ end }}
|
||||||
|
</main>
|
||||||
|
<footer class="container">
|
||||||
|
{{ partial "footer.html" . }}
|
||||||
|
</footer>
|
||||||
|
</body>
|
||||||
|
</html>
|
29
themes/bcarlin/layouts/blog/list.html
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
{{ define "main" }}
|
||||||
|
<article class="h-feed">
|
||||||
|
<header>
|
||||||
|
<h1 class="p-name">{{ .Title }}</h1>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
{{ .Content }}
|
||||||
|
|
||||||
|
{{ range .Site.RegularPages.GroupByDate "2006" }}
|
||||||
|
<section>
|
||||||
|
<h2>{{ .Key }}</h2>
|
||||||
|
{{- range .Pages }}
|
||||||
|
{{ $dateMachine := .Date | time.Format "2006-01-02T15:04:05-07:00" }}
|
||||||
|
{{ $dateHuman := .Date | time.Format "2006-01-02" }}
|
||||||
|
<article class="h-entry">
|
||||||
|
<p>
|
||||||
|
<a class="u-url p-name" href="{{ .RelPermalink }}">{{ .LinkTitle }}</a>
|
||||||
|
(<time class="dt-published" datetime="{{ $dateMachine }}">{{ $dateHuman }}</time>)
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
{{ .Summary }}
|
||||||
|
</p>
|
||||||
|
</article>
|
||||||
|
{{- end }}
|
||||||
|
</section>
|
||||||
|
{{- end }}
|
||||||
|
</article>
|
||||||
|
{{ end }}
|
||||||
|
|
21
themes/bcarlin/layouts/blog/single.html
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
{{ define "main" }}
|
||||||
|
{{ $dateMachine := .Date | time.Format "2006-01-02T15:04:05-07:00" }}
|
||||||
|
{{ $dateHuman := .Date | time.Format "2006-01-02" }}
|
||||||
|
<article class="h-entry">
|
||||||
|
<header>
|
||||||
|
<h1 class="p-name">{{ .Title }}</h1>
|
||||||
|
<div class="metadata">
|
||||||
|
<p>
|
||||||
|
Posted on
|
||||||
|
<time class="dt-published" datetime="{{ $dateMachine }}">{{ $dateHuman }}</time>
|
||||||
|
</p>
|
||||||
|
<p> :: </p>
|
||||||
|
{{ partial "tags.html" . }}
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<section class="e-content">
|
||||||
|
{{ .Content }}
|
||||||
|
</section>
|
||||||
|
</article>
|
||||||
|
{{ end }}
|
27
themes/bcarlin/layouts/home.html
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
{{ define "main" }}
|
||||||
|
<article>
|
||||||
|
<header>
|
||||||
|
<h1>{{ .Title }}</h1>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
{{ .Content }}
|
||||||
|
|
||||||
|
<section class="h-feed">
|
||||||
|
<h2 class="p-name">Recent posts</h2>
|
||||||
|
{{- $posts := where .Site.RegularPages "Section" "blog" }}
|
||||||
|
{{- range first 5 $posts }}
|
||||||
|
{{ $dateMachine := .Date | time.Format "2006-01-02T15:04:05-07:00" }}
|
||||||
|
{{ $dateHuman := .Date | time.Format "2006-01-02" }}
|
||||||
|
<article class="h-entry">
|
||||||
|
<p>
|
||||||
|
<a class="u-url p-name" href="{{ .RelPermalink }}">{{ .LinkTitle }}</a>
|
||||||
|
(<time class="dt-published" datetime="{{ $dateMachine }}">{{ $dateHuman }}</time>)
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
{{ .Summary }}
|
||||||
|
</p>
|
||||||
|
</article>
|
||||||
|
{{- end }}
|
||||||
|
</section>
|
||||||
|
</article>
|
||||||
|
{{ end }}
|
18
themes/bcarlin/layouts/page.html
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
{{ define "main" }}
|
||||||
|
{{ $dateMachine := .Date | time.Format "2006-01-02T15:04:05-07:00" }}
|
||||||
|
{{ $dateHuman := .Date | time.Format "2006-01-02" }}
|
||||||
|
<article class="h-entry">
|
||||||
|
<header>
|
||||||
|
<h1 class="p-name">{{ .Title }}</h1>
|
||||||
|
<p>
|
||||||
|
Posted on
|
||||||
|
<time class="dt-published" datetime="{{ $dateMachine }}">{{ $dateHuman }}</time>
|
||||||
|
</p>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<section class="e-content">
|
||||||
|
{{ .Content }}
|
||||||
|
</section>
|
||||||
|
{{ partial "terms.html" (dict "taxonomy" "tags" "page" .) }}
|
||||||
|
</article>
|
||||||
|
{{ end }}
|
9
themes/bcarlin/layouts/section.html
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
{{ define "main" }}
|
||||||
|
<article>
|
||||||
|
<header>
|
||||||
|
<h1>{{ .Title }}</h1>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
{{ .Content }}
|
||||||
|
<article>
|
||||||
|
{{ end }}
|
59
themes/bcarlin/layouts/section.rss.xml
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
{{- $authorEmail := "" }}
|
||||||
|
{{- with site.Params.author }}
|
||||||
|
{{- if reflect.IsMap . }}
|
||||||
|
{{- with .email }}
|
||||||
|
{{- $authorEmail = . }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{- $authorName := "" }}
|
||||||
|
{{- with site.Params.author }}
|
||||||
|
{{- if reflect.IsMap . }}
|
||||||
|
{{- with .name }}
|
||||||
|
{{- $authorName = . }}
|
||||||
|
{{- end }}
|
||||||
|
{{- else }}
|
||||||
|
{{- $authorName = . }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{- $pctx := . }}
|
||||||
|
{{- if .IsHome }}{{ $pctx = .Site }}{{ end }}
|
||||||
|
{{- $pages := slice }}
|
||||||
|
{{- if or $.IsHome $.IsSection }}
|
||||||
|
{{- $pages = $pctx.RegularPages }}
|
||||||
|
{{- else }}
|
||||||
|
{{- $pages = $pctx.Pages }}
|
||||||
|
{{- end }}
|
||||||
|
{{- $limit := .Site.Config.Services.RSS.Limit }}
|
||||||
|
{{- if ge $limit 1 }}
|
||||||
|
{{- $pages = $pages | first $limit }}
|
||||||
|
{{- end }}
|
||||||
|
{{- printf "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\"?>" | safeHTML }}
|
||||||
|
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
|
||||||
|
<channel>
|
||||||
|
<title>{{ if eq .Title .Site.Title }}{{ .Site.Title }}{{ else }}{{ with .Title }}{{ . }} on {{ end }}{{ .Site.Title }}{{ end }}</title>
|
||||||
|
<link>{{ .Permalink }}</link>
|
||||||
|
<description>Recent content {{ if ne .Title .Site.Title }}{{ with .Title }}in {{ . }} {{ end }}{{ end }}on {{ .Site.Title }}</description>
|
||||||
|
<generator>Hugo</generator>
|
||||||
|
<language>{{ site.Language.LanguageCode }}</language>{{ with $authorEmail }}
|
||||||
|
<managingEditor>{{.}}{{ with $authorName }} ({{ . }}){{ end }}</managingEditor>{{ end }}{{ with $authorEmail }}
|
||||||
|
<webMaster>{{ . }}{{ with $authorName }} ({{ . }}){{ end }}</webMaster>{{ end }}{{ with .Site.Copyright }}
|
||||||
|
<copyright>{{ . }}</copyright>{{ end }}{{ if not .Date.IsZero }}
|
||||||
|
<lastBuildDate>{{ (index $pages.ByLastmod.Reverse 0).Lastmod.Format "Mon, 02 Jan 2006 15:04:05 -0700" | safeHTML }}</lastBuildDate>{{ end }}
|
||||||
|
{{- with .OutputFormats.Get "RSS" }}
|
||||||
|
{{ printf "<atom:link href=%q rel=\"self\" type=%q />" .Permalink .MediaType | safeHTML }}
|
||||||
|
{{- end }}
|
||||||
|
{{- range $pages }}
|
||||||
|
<item>
|
||||||
|
<title>{{ .Title }}</title>
|
||||||
|
<link>{{ .Permalink }}</link>
|
||||||
|
<pubDate>{{ .PublishDate.Format "Mon, 02 Jan 2006 15:04:05 -0700" | safeHTML }}</pubDate>
|
||||||
|
{{- with $authorEmail }}<author>{{ . }}{{ with $authorName }} ({{ . }}){{ end }}</author>{{ end }}
|
||||||
|
<guid>{{ .Permalink }}</guid>
|
||||||
|
<description>{{ .Content | transform.XMLEscape | safeHTML }}</description>
|
||||||
|
</item>
|
||||||
|
{{- end }}
|
||||||
|
</channel>
|
||||||
|
</rss>
|
4
themes/bcarlin/layouts/shortcodes/note.html
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
<aside class="admonition note">
|
||||||
|
<p class="title">{{partial "icon.html" (dict "icon" "information-fill" "label" "information")}} Note</p>
|
||||||
|
{{ .Inner | .Page.RenderString }}
|
||||||
|
</aside>
|
4
themes/bcarlin/layouts/shortcodes/warning.html
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
<aside class="admonition warning">
|
||||||
|
<p class="title">{{partial "icon.html" (dict "icon" "error-warning-fill" "label" "warning")}} Warning</p>
|
||||||
|
{{ .Inner | .Page.RenderString }}
|
||||||
|
</aside>
|
16
themes/bcarlin/layouts/taxonomy.html
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
{{ define "main" }}
|
||||||
|
<article>
|
||||||
|
<header>
|
||||||
|
<h1>{{ .Title }}</h1>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
{{ range .Data.Terms.Alphabetical }}
|
||||||
|
<li>
|
||||||
|
<a href="{{ .Page.RelPermalink }}">{{ .Page.LinkTitle }}</a>
|
||||||
|
({{ .Count }})
|
||||||
|
</li>
|
||||||
|
{{ end }}
|
||||||
|
</ul>
|
||||||
|
</article>
|
||||||
|
{{ end }}
|
18
themes/bcarlin/layouts/term.html
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
{{ define "main" }}
|
||||||
|
<article class="h-feed">
|
||||||
|
<header>
|
||||||
|
<h1 class="p-name">{{ .Title }}</h1>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
{{ .Content }}
|
||||||
|
|
||||||
|
{{ range .Pages.ByDate }}
|
||||||
|
{{ $dateMachine := .Date | time.Format "2006-01-02T15:04:05-07:00" }}
|
||||||
|
{{ $dateHuman := .Date | time.Format "2006-01-02" }}
|
||||||
|
<article class="h-entry">
|
||||||
|
<a class="u-url p-name" href="{{ .RelPermalink }}">{{ .LinkTitle }}</a>
|
||||||
|
(<time class="dt-published" datetime="{{ $dateMachine }}">{{ $dateHuman }}</time>)
|
||||||
|
</article>
|
||||||
|
{{ end }}
|
||||||
|
</article>
|
||||||
|
{{ end }}
|
BIN
themes/bcarlin/static/apple-touch-icon.png
Normal file
After Width: | Height: | Size: 5.7 KiB |
BIN
themes/bcarlin/static/favicon-96x96.png
Normal file
After Width: | Height: | Size: 3.4 KiB |
BIN
themes/bcarlin/static/favicon.ico
Normal file
After Width: | Height: | Size: 15 KiB |
22
themes/bcarlin/static/favicon.svg
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" width="80" height="80"><svg viewBox="0 0 80 80" version="1.1" id="SvgjsSvg1017" sodipodi:docname="logo.svg" inkscape:version="1.4.2 (ebf0e940d0, 2025-05-08)" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg">
|
||||||
|
<defs id="SvgjsDefs1016"></defs>
|
||||||
|
<sodipodi:namedview id="SvgjsSodipodi:namedview1015" pagecolor="#ffffff" bordercolor="#666666" borderopacity="1.0" inkscape:showpageshadow="2" inkscape:pageopacity="0.0" inkscape:pagecheckerboard="0" inkscape:deskcolor="#d1d1d1" inkscape:document-units="mm" inkscape:zoom="1.9270133" inkscape:cx="65.645629" inkscape:cy="72.39182" inkscape:window-width="1916" inkscape:window-height="1026" inkscape:window-x="0" inkscape:window-y="0" inkscape:window-maximized="0" inkscape:current-layer="layer1"></sodipodi:namedview>
|
||||||
|
<style id="SvgjsStyle1014">
|
||||||
|
:root { filter: none; }
|
||||||
|
path {
|
||||||
|
fill:none;
|
||||||
|
stroke:#000000;
|
||||||
|
stroke-width:3.5;
|
||||||
|
stroke-linecap:round;
|
||||||
|
stroke-linejoin:miter;
|
||||||
|
stroke-dasharray:none;
|
||||||
|
stroke-opacity:1
|
||||||
|
}
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
path { stroke: #f0f0f0 }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<g id="SvgjsG1013">
|
||||||
|
<path d="m 33.93812,57.35608 c -2.09127,-8.3238 -6.421596,-19.16538 -4.113261,-26.0517 1.134461,-3.38437 3.845371,-5.56463 7.977231,-5.73386 3.19716,-0.13095 8.72664,0.36824 9.59761,6.23246 0.76712,5.16491 -4.15506,6.39829 -7.22937,8.1022 6.50598,-1.93981 12.01867,1.47517 13.33694,7.105 1.31827,5.62984 -0.76053,10.88377 -3.67194,14.55887 -2.9114,3.67511 -7.14804,6.27136 -12.7811,6.50685 C 21.415486,68.72968 11.927485,54.67483 11.751441,39.19832 11.575406,23.72181 23.343582,11.494025 40.08723,11.290233 56.83088,11.086441 69.20544,25.91722 68.19167,42.02395 c -0.81502,12.94911 -5.57382,19.26544 -9.73003,22.70007 -1.66423,1.37528 -3.28127,3.98783 -3.28127,3.98783" id="SvgjsPath1012"></path>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2 KiB |
21
themes/bcarlin/static/site.webmanifest
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
{
|
||||||
|
"name": "bcarlin.net",
|
||||||
|
"short_name": "bcarlin.net",
|
||||||
|
"icons": [
|
||||||
|
{
|
||||||
|
"src": "/web-app-manifest-192x192.png",
|
||||||
|
"sizes": "192x192",
|
||||||
|
"type": "image/png",
|
||||||
|
"purpose": "maskable"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/web-app-manifest-512x512.png",
|
||||||
|
"sizes": "512x512",
|
||||||
|
"type": "image/png",
|
||||||
|
"purpose": "maskable"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"theme_color": "#ffffff",
|
||||||
|
"background_color": "#ffffff",
|
||||||
|
"display": "standalone"
|
||||||
|
}
|
88
themes/bcarlin/static/static/css/syntax-dark.css
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
/* Generated using: hugo gen chromastyles --style solarized-dark */
|
||||||
|
|
||||||
|
/* Background */ .bg { color:#93a1a1;background-color:#002b36; }
|
||||||
|
/* PreWrapper */ .chroma { color:#93a1a1;background-color:#002b36; }
|
||||||
|
/* Other */ .chroma .x { color:#cb4b16 }
|
||||||
|
/* Error */ .chroma .err { }
|
||||||
|
/* CodeLine */ .chroma .cl { }
|
||||||
|
/* LineLink */ .chroma .lnlinks { outline:none;text-decoration:none;color:inherit }
|
||||||
|
/* LineTableTD */ .chroma .lntd { vertical-align:top;padding:0;margin:0;border:0; }
|
||||||
|
/* LineTable */ .chroma .lntable { border-spacing:0;padding:0;margin:0;border:0; }
|
||||||
|
/* LineHighlight */ .chroma .hl { background-color:#19404a }
|
||||||
|
/* LineNumbersTable */ .chroma .lnt { white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#495050 }
|
||||||
|
/* LineNumbers */ .chroma .ln { white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#495050 }
|
||||||
|
/* Line */ .chroma .line { display:flex; }
|
||||||
|
/* Keyword */ .chroma .k { color:#719e07 }
|
||||||
|
/* KeywordConstant */ .chroma .kc { color:#cb4b16 }
|
||||||
|
/* KeywordDeclaration */ .chroma .kd { color:#268bd2 }
|
||||||
|
/* KeywordNamespace */ .chroma .kn { color:#719e07 }
|
||||||
|
/* KeywordPseudo */ .chroma .kp { color:#719e07 }
|
||||||
|
/* KeywordReserved */ .chroma .kr { color:#268bd2 }
|
||||||
|
/* KeywordType */ .chroma .kt { color:#dc322f }
|
||||||
|
/* Name */ .chroma .n { }
|
||||||
|
/* NameAttribute */ .chroma .na { }
|
||||||
|
/* NameClass */ .chroma .nc { color:#268bd2 }
|
||||||
|
/* NameConstant */ .chroma .no { color:#cb4b16 }
|
||||||
|
/* NameDecorator */ .chroma .nd { color:#268bd2 }
|
||||||
|
/* NameEntity */ .chroma .ni { color:#cb4b16 }
|
||||||
|
/* NameException */ .chroma .ne { color:#cb4b16 }
|
||||||
|
/* NameLabel */ .chroma .nl { }
|
||||||
|
/* NameNamespace */ .chroma .nn { }
|
||||||
|
/* NameOther */ .chroma .nx { }
|
||||||
|
/* NameProperty */ .chroma .py { }
|
||||||
|
/* NameTag */ .chroma .nt { color:#268bd2 }
|
||||||
|
/* NameBuiltin */ .chroma .nb { color:#b58900 }
|
||||||
|
/* NameBuiltinPseudo */ .chroma .bp { color:#268bd2 }
|
||||||
|
/* NameVariable */ .chroma .nv { color:#268bd2 }
|
||||||
|
/* NameVariableClass */ .chroma .vc { color:#268bd2 }
|
||||||
|
/* NameVariableGlobal */ .chroma .vg { color:#268bd2 }
|
||||||
|
/* NameVariableInstance */ .chroma .vi { color:#268bd2 }
|
||||||
|
/* NameVariableMagic */ .chroma .vm { color:#268bd2 }
|
||||||
|
/* NameFunction */ .chroma .nf { color:#268bd2 }
|
||||||
|
/* NameFunctionMagic */ .chroma .fm { color:#268bd2 }
|
||||||
|
/* Literal */ .chroma .l { }
|
||||||
|
/* LiteralDate */ .chroma .ld { }
|
||||||
|
/* LiteralString */ .chroma .s { color:#2aa198 }
|
||||||
|
/* LiteralStringAffix */ .chroma .sa { color:#2aa198 }
|
||||||
|
/* LiteralStringBacktick */ .chroma .sb { color:#586e75 }
|
||||||
|
/* LiteralStringChar */ .chroma .sc { color:#2aa198 }
|
||||||
|
/* LiteralStringDelimiter */ .chroma .dl { color:#2aa198 }
|
||||||
|
/* LiteralStringDoc */ .chroma .sd { }
|
||||||
|
/* LiteralStringDouble */ .chroma .s2 { color:#2aa198 }
|
||||||
|
/* LiteralStringEscape */ .chroma .se { color:#cb4b16 }
|
||||||
|
/* LiteralStringHeredoc */ .chroma .sh { }
|
||||||
|
/* LiteralStringInterpol */ .chroma .si { color:#2aa198 }
|
||||||
|
/* LiteralStringOther */ .chroma .sx { color:#2aa198 }
|
||||||
|
/* LiteralStringRegex */ .chroma .sr { color:#dc322f }
|
||||||
|
/* LiteralStringSingle */ .chroma .s1 { color:#2aa198 }
|
||||||
|
/* LiteralStringSymbol */ .chroma .ss { color:#2aa198 }
|
||||||
|
/* LiteralNumber */ .chroma .m { color:#2aa198 }
|
||||||
|
/* LiteralNumberBin */ .chroma .mb { color:#2aa198 }
|
||||||
|
/* LiteralNumberFloat */ .chroma .mf { color:#2aa198 }
|
||||||
|
/* LiteralNumberHex */ .chroma .mh { color:#2aa198 }
|
||||||
|
/* LiteralNumberInteger */ .chroma .mi { color:#2aa198 }
|
||||||
|
/* LiteralNumberIntegerLong */ .chroma .il { color:#2aa198 }
|
||||||
|
/* LiteralNumberOct */ .chroma .mo { color:#2aa198 }
|
||||||
|
/* Operator */ .chroma .o { color:#719e07 }
|
||||||
|
/* OperatorWord */ .chroma .ow { color:#719e07 }
|
||||||
|
/* Punctuation */ .chroma .p { }
|
||||||
|
/* Comment */ .chroma .c { color:#586e75 }
|
||||||
|
/* CommentHashbang */ .chroma .ch { color:#586e75 }
|
||||||
|
/* CommentMultiline */ .chroma .cm { color:#586e75 }
|
||||||
|
/* CommentSingle */ .chroma .c1 { color:#586e75 }
|
||||||
|
/* CommentSpecial */ .chroma .cs { color:#719e07 }
|
||||||
|
/* CommentPreproc */ .chroma .cp { color:#719e07 }
|
||||||
|
/* CommentPreprocFile */ .chroma .cpf { color:#719e07 }
|
||||||
|
/* Generic */ .chroma .g { }
|
||||||
|
/* GenericDeleted */ .chroma .gd { color:#dc322f }
|
||||||
|
/* GenericEmph */ .chroma .ge { font-style:italic }
|
||||||
|
/* GenericError */ .chroma .gr { color:#dc322f;font-weight:bold }
|
||||||
|
/* GenericHeading */ .chroma .gh { color:#cb4b16 }
|
||||||
|
/* GenericInserted */ .chroma .gi { color:#719e07 }
|
||||||
|
/* GenericOutput */ .chroma .go { }
|
||||||
|
/* GenericPrompt */ .chroma .gp { }
|
||||||
|
/* GenericStrong */ .chroma .gs { font-weight:bold }
|
||||||
|
/* GenericSubheading */ .chroma .gu { color:#268bd2 }
|
||||||
|
/* GenericTraceback */ .chroma .gt { }
|
||||||
|
/* GenericUnderline */ .chroma .gl { }
|
||||||
|
/* TextWhitespace */ .chroma .w { }
|
88
themes/bcarlin/static/static/css/syntax-light.css
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
/* Generated using: hugo gen chromastyles --style solarized-light */
|
||||||
|
|
||||||
|
/* Background */ .bg { color:#586e75;background-color:#eee8d5; }
|
||||||
|
/* PreWrapper */ .chroma { color:#586e75;background-color:#eee8d5; }
|
||||||
|
/* Other */ .chroma .x { }
|
||||||
|
/* Error */ .chroma .err { }
|
||||||
|
/* CodeLine */ .chroma .cl { }
|
||||||
|
/* LineLink */ .chroma .lnlinks { outline:none;text-decoration:none;color:inherit }
|
||||||
|
/* LineTableTD */ .chroma .lntd { vertical-align:top;padding:0;margin:0;border:0; }
|
||||||
|
/* LineTable */ .chroma .lntable { border-spacing:0;padding:0;margin:0;border:0; }
|
||||||
|
/* LineHighlight */ .chroma .hl { background-color:#d6d0bf }
|
||||||
|
/* LineNumbersTable */ .chroma .lnt { white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f }
|
||||||
|
/* LineNumbers */ .chroma .ln { white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f }
|
||||||
|
/* Line */ .chroma .line { display:flex; }
|
||||||
|
/* Keyword */ .chroma .k { color:#859900 }
|
||||||
|
/* KeywordConstant */ .chroma .kc { color:#859900;font-weight:bold }
|
||||||
|
/* KeywordDeclaration */ .chroma .kd { color:#859900 }
|
||||||
|
/* KeywordNamespace */ .chroma .kn { color:#dc322f;font-weight:bold }
|
||||||
|
/* KeywordPseudo */ .chroma .kp { color:#859900 }
|
||||||
|
/* KeywordReserved */ .chroma .kr { color:#859900 }
|
||||||
|
/* KeywordType */ .chroma .kt { color:#859900;font-weight:bold }
|
||||||
|
/* Name */ .chroma .n { color:#268bd2 }
|
||||||
|
/* NameAttribute */ .chroma .na { color:#268bd2 }
|
||||||
|
/* NameClass */ .chroma .nc { color:#cb4b16 }
|
||||||
|
/* NameConstant */ .chroma .no { color:#268bd2 }
|
||||||
|
/* NameDecorator */ .chroma .nd { color:#268bd2 }
|
||||||
|
/* NameEntity */ .chroma .ni { color:#268bd2 }
|
||||||
|
/* NameException */ .chroma .ne { color:#268bd2 }
|
||||||
|
/* NameLabel */ .chroma .nl { color:#268bd2 }
|
||||||
|
/* NameNamespace */ .chroma .nn { color:#268bd2 }
|
||||||
|
/* NameOther */ .chroma .nx { color:#268bd2 }
|
||||||
|
/* NameProperty */ .chroma .py { color:#268bd2 }
|
||||||
|
/* NameTag */ .chroma .nt { color:#268bd2;font-weight:bold }
|
||||||
|
/* NameBuiltin */ .chroma .nb { color:#cb4b16 }
|
||||||
|
/* NameBuiltinPseudo */ .chroma .bp { color:#cb4b16 }
|
||||||
|
/* NameVariable */ .chroma .nv { color:#268bd2 }
|
||||||
|
/* NameVariableClass */ .chroma .vc { color:#268bd2 }
|
||||||
|
/* NameVariableGlobal */ .chroma .vg { color:#268bd2 }
|
||||||
|
/* NameVariableInstance */ .chroma .vi { color:#268bd2 }
|
||||||
|
/* NameVariableMagic */ .chroma .vm { color:#268bd2 }
|
||||||
|
/* NameFunction */ .chroma .nf { color:#268bd2 }
|
||||||
|
/* NameFunctionMagic */ .chroma .fm { color:#268bd2 }
|
||||||
|
/* Literal */ .chroma .l { color:#2aa198 }
|
||||||
|
/* LiteralDate */ .chroma .ld { color:#2aa198 }
|
||||||
|
/* LiteralString */ .chroma .s { color:#2aa198 }
|
||||||
|
/* LiteralStringAffix */ .chroma .sa { color:#2aa198 }
|
||||||
|
/* LiteralStringBacktick */ .chroma .sb { color:#2aa198 }
|
||||||
|
/* LiteralStringChar */ .chroma .sc { color:#2aa198 }
|
||||||
|
/* LiteralStringDelimiter */ .chroma .dl { color:#2aa198 }
|
||||||
|
/* LiteralStringDoc */ .chroma .sd { color:#2aa198 }
|
||||||
|
/* LiteralStringDouble */ .chroma .s2 { color:#2aa198 }
|
||||||
|
/* LiteralStringEscape */ .chroma .se { color:#2aa198 }
|
||||||
|
/* LiteralStringHeredoc */ .chroma .sh { color:#2aa198 }
|
||||||
|
/* LiteralStringInterpol */ .chroma .si { color:#2aa198 }
|
||||||
|
/* LiteralStringOther */ .chroma .sx { color:#2aa198 }
|
||||||
|
/* LiteralStringRegex */ .chroma .sr { color:#2aa198 }
|
||||||
|
/* LiteralStringSingle */ .chroma .s1 { color:#2aa198 }
|
||||||
|
/* LiteralStringSymbol */ .chroma .ss { color:#2aa198 }
|
||||||
|
/* LiteralNumber */ .chroma .m { color:#2aa198;font-weight:bold }
|
||||||
|
/* LiteralNumberBin */ .chroma .mb { color:#2aa198;font-weight:bold }
|
||||||
|
/* LiteralNumberFloat */ .chroma .mf { color:#2aa198;font-weight:bold }
|
||||||
|
/* LiteralNumberHex */ .chroma .mh { color:#2aa198;font-weight:bold }
|
||||||
|
/* LiteralNumberInteger */ .chroma .mi { color:#2aa198;font-weight:bold }
|
||||||
|
/* LiteralNumberIntegerLong */ .chroma .il { color:#2aa198;font-weight:bold }
|
||||||
|
/* LiteralNumberOct */ .chroma .mo { color:#2aa198;font-weight:bold }
|
||||||
|
/* Operator */ .chroma .o { }
|
||||||
|
/* OperatorWord */ .chroma .ow { color:#859900 }
|
||||||
|
/* Punctuation */ .chroma .p { }
|
||||||
|
/* Comment */ .chroma .c { color:#93a1a1;font-style:italic }
|
||||||
|
/* CommentHashbang */ .chroma .ch { color:#93a1a1;font-style:italic }
|
||||||
|
/* CommentMultiline */ .chroma .cm { color:#93a1a1;font-style:italic }
|
||||||
|
/* CommentSingle */ .chroma .c1 { color:#93a1a1;font-style:italic }
|
||||||
|
/* CommentSpecial */ .chroma .cs { color:#93a1a1;font-style:italic }
|
||||||
|
/* CommentPreproc */ .chroma .cp { color:#93a1a1;font-style:italic }
|
||||||
|
/* CommentPreprocFile */ .chroma .cpf { color:#93a1a1;font-style:italic }
|
||||||
|
/* Generic */ .chroma .g { color:#d33682 }
|
||||||
|
/* GenericDeleted */ .chroma .gd { color:#d33682 }
|
||||||
|
/* GenericEmph */ .chroma .ge { color:#d33682 }
|
||||||
|
/* GenericError */ .chroma .gr { color:#d33682 }
|
||||||
|
/* GenericHeading */ .chroma .gh { color:#d33682 }
|
||||||
|
/* GenericInserted */ .chroma .gi { color:#d33682 }
|
||||||
|
/* GenericOutput */ .chroma .go { color:#d33682 }
|
||||||
|
/* GenericPrompt */ .chroma .gp { color:#d33682 }
|
||||||
|
/* GenericStrong */ .chroma .gs { color:#d33682 }
|
||||||
|
/* GenericSubheading */ .chroma .gu { color:#d33682 }
|
||||||
|
/* GenericTraceback */ .chroma .gt { color:#d33682 }
|
||||||
|
/* GenericUnderline */ .chroma .gl { color:#d33682 }
|
||||||
|
/* TextWhitespace */ .chroma .w { }
|
BIN
themes/bcarlin/static/static/favicon.ico
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
themes/bcarlin/static/static/fonts/GentiumPlus-Bold.woff2
Normal file
BIN
themes/bcarlin/static/static/fonts/GentiumPlus-BoldItalic.woff2
Normal file
BIN
themes/bcarlin/static/static/fonts/GentiumPlus-Italic.woff2
Normal file
BIN
themes/bcarlin/static/static/fonts/GentiumPlus-Regular.woff2
Normal file
BIN
themes/bcarlin/static/static/fonts/NunitoSans-Italic.woff2
Normal file
BIN
themes/bcarlin/static/static/fonts/NunitoSans.woff2
Normal file
BIN
themes/bcarlin/static/static/fonts/remixicon.woff2
Normal file
BIN
themes/bcarlin/static/static/logo.png
Normal file
After Width: | Height: | Size: 14 KiB |
57
themes/bcarlin/static/static/logo.svg
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
viewBox="0 0 80 80"
|
||||||
|
version="1.1"
|
||||||
|
id="svg1"
|
||||||
|
sodipodi:docname="logo.svg"
|
||||||
|
inkscape:version="1.4.2 (ebf0e940d0, 2025-05-08)"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg">
|
||||||
|
<defs
|
||||||
|
id="defs1" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="namedview1"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
inkscape:showpageshadow="2"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pagecheckerboard="0"
|
||||||
|
inkscape:deskcolor="#d1d1d1"
|
||||||
|
inkscape:document-units="mm"
|
||||||
|
inkscape:zoom="1.9270133"
|
||||||
|
inkscape:cx="65.645629"
|
||||||
|
inkscape:cy="72.39182"
|
||||||
|
inkscape:window-width="1916"
|
||||||
|
inkscape:window-height="1026"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="0"
|
||||||
|
inkscape:window-maximized="0"
|
||||||
|
inkscape:current-layer="layer1" />
|
||||||
|
<style
|
||||||
|
id="style1">
|
||||||
|
path {
|
||||||
|
fill:none;
|
||||||
|
stroke:#000000;
|
||||||
|
stroke-width:3.5;
|
||||||
|
stroke-linecap:round;
|
||||||
|
stroke-linejoin:miter;
|
||||||
|
stroke-dasharray:none;
|
||||||
|
stroke-opacity:1
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
path { stroke: #f0f0f0 }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<g
|
||||||
|
id="layer1">
|
||||||
|
<path
|
||||||
|
d="m 33.93812,57.35608 c -2.09127,-8.3238 -6.421596,-19.16538 -4.113261,-26.0517 1.134461,-3.38437 3.845371,-5.56463 7.977231,-5.73386 3.19716,-0.13095 8.72664,0.36824 9.59761,6.23246 0.76712,5.16491 -4.15506,6.39829 -7.22937,8.1022 6.50598,-1.93981 12.01867,1.47517 13.33694,7.105 1.31827,5.62984 -0.76053,10.88377 -3.67194,14.55887 -2.9114,3.67511 -7.14804,6.27136 -12.7811,6.50685 C 21.415486,68.72968 11.927485,54.67483 11.751441,39.19832 11.575406,23.72181 23.343582,11.494025 40.08723,11.290233 56.83088,11.086441 69.20544,25.91722 68.19167,42.02395 c -0.81502,12.94911 -5.57382,19.26544 -9.73003,22.70007 -1.66423,1.37528 -3.28127,3.98783 -3.28127,3.98783"
|
||||||
|
id="path1" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.1 KiB |
BIN
themes/bcarlin/static/web-app-manifest-192x192.png
Normal file
After Width: | Height: | Size: 7.2 KiB |
BIN
themes/bcarlin/static/web-app-manifest-512x512.png
Normal file
After Width: | Height: | Size: 24 KiB |
9
wrangler.toml
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
name = 'homepage'
|
||||||
|
compatibility_date = "2025-06-26"
|
||||||
|
|
||||||
|
#[build]
|
||||||
|
#command = "./build.sh"
|
||||||
|
|
||||||
|
[assets]
|
||||||
|
directory = "./public"
|
||||||
|
#not_found_handling = "404-page"
|