diff --git a/.gitignore b/.gitignore index adf8f72..9905a78 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,5 @@ # Go workspace file go.work +public/ +public-gg/ diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..f0a20ea --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "themes/Hugo-2-Gopher-and-Gemini"] + path = themes/Hugo-2-Gopher-and-Gemini + url = https://github.com/mkamarin/Hugo-2-Gopher-and-Gemini.git diff --git a/.hugo_build.lock b/.hugo_build.lock new file mode 100644 index 0000000..e69de29 diff --git a/archetypes/default.md b/archetypes/default.md new file mode 100644 index 0000000..c6f3fce --- /dev/null +++ b/archetypes/default.md @@ -0,0 +1,5 @@ ++++ +title = '{{ replace .File.ContentBaseName "-" " " | title }}' +date = {{ .Date }} +draft = true ++++ diff --git a/config-gg.toml b/config-gg.toml new file mode 100644 index 0000000..a820038 --- /dev/null +++ b/config-gg.toml @@ -0,0 +1,96 @@ +title = "sudoscientist" +theme = "Hugo-2-Gopher-and-Gemini" +languageCode = "en-us" +defaultcontentlanguage = "en-us" + +# Ignore content written in html files +ignoreFiles = [".html$"] + +[params] + author = "Asara" + copyright = "© [Asara](https://sudoscientist.com)" + info = "Asara's personal blog" + returnHome = "Return to main page" + +[params.gemini] + description = "Asara's blog as a gemblog/gemini capsule" + menuText = "## Site sections" + postText = "## Posts" + socialText = "## Social media links" + includeMenu = ["main", "lists", "pages" ] + includeCategories = ["main", "lists", "pages" ] + includeSocial = ["main", "lists", "pages" ] + includeReturnHome = ["main", "lists", "pages" ] + includeAuthor = ["main", "lists", "pages" ] + +[[menu.main]] + name = "Home" + weight = 1 + url = "/" +[[menu.main]] + name = "Posts" + weight = 2 + url = "/posts/" +[[menu.main]] + name = "About" + weight = 3 + url = "/about/" + +[[params.social]] + name = "personal git server" + weight = 1 + url = "https://git.minhas.io/Asara" +[[params.social]] + name = "github" + weight = 2 + url = "https://github.com/Asara" +[[params.social]] + name = "email" + weight = 3 + url = "mailto:amarpreet@minhas.io" + +####################################################### +# You may not need to touch the following configuration + +config = "config-gg.toml" +metaDataFormat = "toml" + +# Disable files not needed for Gopher and Gemini +disableRSS = true +disableSitemap = true +disable404 = true +enableEmoji = false + +# Directories used for Gopher and Gemini generation +layoutDir = "layouts-gg" +publishDir = "public-gg" + +uglyURLs = true +relativeURLs = false +canonifyURLs = true + +[mediaTypes] +[mediaTypes."text/gemini"] + suffixes = ["gmi"] +[mediaTypes."text/plain"] + suffixes = ["txt"] + +[outputs] + home = ["gemini",] + section = ["gemini",] + taxonomy = ["gemini",] + term = ["gemini",] + page = ["gemini",] + +[outputFormats] +[outputFormats.Gemini] + name = "gemini" + mediaType = "text/gemini" + baseName = "gemini-page" + isPlainText = true + permalinkable = true + isHTML = false + protocol = "gemini://" + noUgly = false + path = "gemini/" + diff --git a/config.toml b/config.toml new file mode 100644 index 0000000..dfabbb6 --- /dev/null +++ b/config.toml @@ -0,0 +1,75 @@ +baseURL = 'https://sudoscientist.com/' +languageCode = 'en-us' +title = 'sudoscientist' +author = 'Asara ' +theme = 'risotto' +copyright = "© [Asara](https://sudoscientist.com)" +paginate = 3 +DefaultContentLanguage = "en" +enableInlineShortcodes = true +sectionPagesMenu = "main" + +[Author] +name = "Asara" +email = "amarpreet@minhas.io" + +[params] +noindex = false + +[params.theme] +palette = "personal" + +# Sidebar: about/bio +[params.about] +description = "Asara's personal blog" +logo = "images/profile.jpg" + +[[params.socialLinks]] +icon = "fa fa-matrix-org" +title = "matrix" +url = "https://matrix.to/#/@Asara:devvul.com" + +[[params.socialLinks]] +icon = "fa fa-gitea" +title = "personal git" +url = "https://git.minhas.io/Asara" + +[[params.socialLinks]] +icon = "fa-brands fa-github-alt" +title = "GitHub" +url = "https://github.com/Asara" + +[[params.socialLinks]] +icon = "fa-solid fa-envelope" +title = "Email" +url = "mailto:amarpreet@minhas.io" + +[[params.socialLinks]] +icon = "fa-solid fa-rss" +title = "RSS feed" +url = "https://sudoscientist.com/feed.xml" + +[menu] + [[menu.main]] + identifier = "about" + name = "About" + url = "/about/" + weight = 10 + +[taxonomies] +category = "categories" +tag = "tags" + +# For hugo >= 0.60.0, enable inline HTML +[markup.goldmark.renderer] +unsafe = true +[markup] + [markup.tableOfContents] + startLevel = 2 + endLevel = 3 + ordered = true + +[outputFormats] + [outputFormats.RSS] + mediaType = "application/rss+xml" + baseName = "feed" diff --git a/content/_index.md b/content/_index.md new file mode 100644 index 0000000..12c293f --- /dev/null +++ b/content/_index.md @@ -0,0 +1,15 @@ ++++ +title = ':~/#' ++++ +## Welcome to my blog! +Allo! I go by Asara online, and I appreciate you visiting my site! + + +## Quick summary +I will primarily post about technology, personal projects, and other sudo-scientific and sudo-philosophical ramblings. + +Check out the [posts](https://sudoscientist.com/posts) tab above to read some of my blog posts, or the [about](https://sudoscientist.com/about) to learn a bit about the creation of the blog itself. + +If you'd like to subscribe to the blog, you can hit the RSS icon in the sidebar, or add the [feed.xml](https://sudoscientist.com/feed.xml) to your RSS client. + +You can also find my contact links on the sidebar. Get in touch if you want to chat! diff --git a/content/about.md b/content/about.md new file mode 100644 index 0000000..b2adc87 --- /dev/null +++ b/content/about.md @@ -0,0 +1,30 @@ ++++ +title = 'about' ++++ + +## History +This is the latest revision of my blog. + +The v1 of this was run using [Pelican](https://getpelican.com/), and the archived code can be found on [Github](https://github.com/asara/ampx). Originally it was running on a raspberry pi in my parent's house, on a domain long gone. + +The v2 of the website was created to learn Go and JavaScript. It is technically the first version of sudoscientist. The [backend](https://git.minhas.io/Asara/sudoscientist-go-backend) was written using go-chi, and the [frontend](https://git.minhas.io/Asara/sudoscientist-js-frontend) was written using the React/Redux framework. + +This is [v3](https://git.minhas.io/asara/sudoscientist.com) of my blog, which is powered by [Hugo](https://gohugo.io/), and uses a modified version of [risotto](https://risotto.joeroe.io/) as its theme. It has been extended to include [forkawesome](https://forkaweso.me) and some small cleanup. I made the decision to return to using a static site generator as I have mostly forgotten React/Redux. Along with this, using Go in multiple other projects, I do not feel like I need to run my blog with golang anymore. + +The inspiration for this theming, and returning to a static site generator was in part due to the awesome community over at [Ctrl-C](https://ctrl-c.club). The `tildeverse` in general is a great reminder that minimalism is not only useful, but sometimes more fulfilling. + +Moving to hugo also allowed me to quickly get my blog into the [Gemini](https://gemini.circumlunar.space/) protocol, and can be seen hosted at [ctrl-c](gemini://gemini.ctrl-c.club/~Asara)! + +At a v3, I hope this time around I actually write some blog posts! + +## Connect with me +Matrix: `@Asara:devvul.com` + +Email: `amarpreet@minhas.io` + +IRC: `Asara` on OFTC/Libra/tilde.chat + +## Lightning Network +Pubkey: `0214b1f6b48998b9eb19d8a756af39a027202cecfe608450109465bbccf3bb74ed` + +Node: `redwingxusk66wrrm4yyqyuyndvum4jsnloroxqmie6wv4wefadqgsqd.onion:9735` diff --git a/content/posts/_index.html b/content/posts/_index.html new file mode 100644 index 0000000..ddf004f --- /dev/null +++ b/content/posts/_index.html @@ -0,0 +1,3 @@ ++++ +title = 'posts' ++++ diff --git a/content/posts/docker-support-on-the-google-pixel.md b/content/posts/docker-support-on-the-google-pixel.md new file mode 100644 index 0000000..4a3c40b --- /dev/null +++ b/content/posts/docker-support-on-the-google-pixel.md @@ -0,0 +1,9 @@ ++++ +title = 'Docker Support on the Google Pixel' +date = 2014-03-28T00:00:00-00:00 +category = 'pixel' +tags = ['docker', 'howto'] ++++ + +## Docker +I've been fooling around with Docker and decided to upload a new kernel [config](http://pastebin.com/sqsJhBn2) that includes support for Docker. diff --git a/content/posts/gentoo-on-the-google-pixel-pt-1.md b/content/posts/gentoo-on-the-google-pixel-pt-1.md new file mode 100644 index 0000000..69c3635 --- /dev/null +++ b/content/posts/gentoo-on-the-google-pixel-pt-1.md @@ -0,0 +1,31 @@ ++++ +title = 'Gentoo on the Google Pixel Pt. 1' +date = 2013-12-02T00:00:00-00:00 +category = "pixel" ++++ + +## Notes +1) I would recommend getting a USB mouse because it is a pain otherwise. The modules are not built in for the touchscreen (atmel_mxt_ts nor chromeos_laptop), and as such you might get annoyed. If you can deal with working in term, ignore this. +2) This will remove ChromeOS from the SDD, but it is fairly straightforward recovering it if you get sick of Linux. [Here](http://support.google.com/chromeos/bin/answer.py?hl=en&answer=1080595) is how you restore your machine. + +## Enabling access to SeaBIOS +Before installing Linux to the SDD, you have to first enable access to the secondary BIOS installed on the device (currently no way to access this manually and you are stuck with a 5 second wait on each boot.) + +1) In order to put your Pixel into developer mode, hold the Escape and Refresh keys while booting the machine (ESC is F1, Refresh is F3). Press Ctrl-D followed by Enter, which will enable dev mode and wipe all user data from the Pixel. Dev mode also disables OS checking, so you will get a message at every boot. +2) Boot into ChromeOS after enabling dev mode (press Ctrl-D in order to get into the OS when the OS checking message appears). Log in as a guest, press Ctrl-Alt-T to bring up a crosh (ChromeOS Shell) window, and start a shell: + +```bash +# Get root +sudo bash +# enable USB booting in SeaBIOS +crossystem dev_boot_usb=1 +``` + +3) Reboot the machine, and once the OS check window comes up, press Ctrl-L to enter SeaBIOS. After this the process is simple. My next post will be about installing specifically Gentoo onto the SSD. For any other OS, just install onto /dev/sda1, with the bootloader installed to the MBR of the disk, and you should be good to go. On bootup, enable the two drivers I mentioned up there, and you will have a working system. + +## Aditional note +If you are unable to boot from the USB due to memory issues, add mem=4G to the kernel command line, and if that doesn't work, as a failsafe, mem=1G should get you into your distro's LiveOS. + + +## [Part 2](http://ampx.minhas.io/posts/2014/Jan/23/gentoo-on-the-google-pixel-pt-2.html) + diff --git a/content/posts/gentoo-on-the-google-pixel-pt-2.md b/content/posts/gentoo-on-the-google-pixel-pt-2.md new file mode 100644 index 0000000..33c9b69 --- /dev/null +++ b/content/posts/gentoo-on-the-google-pixel-pt-2.md @@ -0,0 +1,157 @@ ++++ +title = 'Gentoo on the Google Pixel Pt. 2' +date = 2014-01-23T00:00:00-00:00 +category = 'pixel' +tags = ['howto'] ++++ + +## Notes +I ended up slowing down and taking a while to fully explore all the options in building a Gentoo system on my Pixel. This included me reinstalling from scratch multiple times and learning along the way. This final product is a Pixel install with a 3.12.8 kernel, with the /,/home, and swap partitions sitting in a LVM encrypted with LUKS. Follow through and you will be able to get the same. Also, this install uses the SystemRescueCD (which is based on Gentoo). + +## Paritioning Drives +Since we are using a SSD for the install, GPT is recommended, which complicates things a tad bit. +Using parted, we have to create 3 partitions, the grub-bios partition, /boot, and the 3rd for the LVM. The grub-bios partition is 2MB, the /boot is 512MB (for multiple kernels), and the LVM will be the rest of the drive. + +```bash +parted -a optimal /dev/sda +mklabel gpt +(parted) unit mib +(parted) mkpart primary 1 3 +(parted) name 1 grub +(parted) set 1 bios_grub on +(parted) print +(parted) mkpart primary 3 515 +(parted) name 2 boot +(parted) mkpart primary 515 -1 +(parted) name 3 rootfs +``` + +## Encrypting and Creating LVM/File Systems +Encrypt /dev/sda3, mount it and create the LVM + +```bash +cryptsetup -y --cipher aes-cbc-essiv:sha256 --key-size 256 luksFormat /dev/sda3 +cryptsetup luksOpen /dev/sda3 rootfs +pvcreate /dev/mapper/rootfs +lvcreate -L1024m -nswap rootfs +lvcreate -L20480m -nroot rootfs +lvcreate -l 100%FREE home rootfs +``` + +Create file systems on the multiple logical volumes you've created and mount them to their proper locations: +```bash +mkswap -L SWAP /dev/mapper/rootfs-swap +swapon /dev/mapper/rootfs-swap +mkfs.ext4 -j /dev/mapper/rootfs-root -L ROOT +mount /dev/mapper/rootfs-root /mnt/gentoo +mkdir /mnt/gentoo/home +mkfs.ext4 -j /dev/mapper/rootfs-home -L HOME +mount /dev/mapper/rootfs-home /mnt/gentoo/home +mkdir /mnt/gentoo/boot +mkfs.ext4 -j /dev/sda2 -L BOOT +mount /dev/sda2 /mnt/gentoo/boot +``` + +## Download Gentoo Stage3 tarball +Change directories into /mnt/gentoo, download the stage3 and extract it. Copy over resolv.conf +```bash +cd /mnt/gentoo +elinks http://www.gentoo.org/main/en/mirror.xml +tar xvjpf stage3-.tar.bz2 +cp -L /etc/resolv.conf /mnt/gentoo/etc/ +``` + +## Prepare Portage +```bash +nano /mnt/gentoo/etc/portage/make.conf + CFLAGS="-march=k8 -O2 -pipe" + MAKEOPTS="-j2" + mirrorselect -i -o >> /mnt/gentoo/etc/portage/make.conf + mirrorselect -i -r -o >> /mnt/gentoo/etc/portage/make.conf +``` + +## Mount Virtual Filesystems +```bash +mount -t proc proc /mnt/gentoo/proc +mount --rbind /sys /mnt/gentoo/sys +mount --rbind /dev /mnt/gentoo/dev +``` + +## Chroot into your new system +```bash +chroot /mnt/gentoo /bin/bash +source /etc/profile +export PS1="(chroot) $PS1" +``` + +## Initialize Portage +```bash +emerge-webrsync +emerge --sync +``` + +## Set up localization information +```bash +echo "Continent/Country" > /etc/timezone +emerge --config sys-libs/timezone-data +nano -w /etc/locale.gen +locale-gen +eselect locale list +eselect locale set # Your locale here +env-update && source /etc/profile +``` + +## Notes +At this point I like to install a few apps I use, that way I won't have to worry about them later. I'd recommend installing vim, NetworkManager (for nmcli), and really anything else you expect to use. I also wanted to use systemd, so I had to prep for that. That includes installing udev with -systemd in make.conf, the installing systemd (obviously remove the - after installing udev), and then uninstalling udev, since systemd provides virtual/udev. + +## Kernel Setup +```bash + echo "=sys-kernel/gentoo-sources-3.12.8" >> /etc/portage/package.keywords + emerge gentoo-sources genkernel-next lvm2 cryptsetup grub vim +``` + +(As a side note, genkernel-next is required for a systemd install to include udev in the kernel) +```bash +vim /etc/genkernel.conf + LVM="yes" + LUKS="yes" + BUSYBOX="yes" + MENUCONFIG="yes" + DISKLABEL="yes" +``` + +## Create the kernel +```bash +genkernel --udev all +``` +Remember to enable support for crypto devices in the kernel, along with anything else you may need/want. +``` + Device Drivers + Multi-device support (RAID and LVM) + [*] Multiple devices driver support (RAID and LVM) + <*> Device mapper support + <*> Crypt target support + + Cryptographic API + <*> SHA256 digest algorithm + <*> AES cipher algorithms +``` + +## Installing grub2 +```bash +vim /etc/default/grub + GRUB_DISTRIBUTOR="Gentoo" + GRUB_DEFAULT=0 + GRUB_HIDDEN_TIMEOUT=0 + GRUB_HIDDEN_TIMEOUT_QUIET=true + GRUB_TIMEOUT=3 + GRUB_PRELOAD_MODULES=lvm + GRUB_CRYPTODISK_ENABLE=y + GRUB_DEVICE=/dev/sda1 + GRUB_CMDLINE_LINUX="real_init=/usr/lib/systemd/systemd quiet real_root=/dev/mapper/rootfs-root crypt_root=/dev/sda3 dolvm" +grub2-install --modules="configfile linux crypto search_fs_uuid luks lvm" --recheck /dev/sda +grub2-mkconfig -o /boot/grub/grub.cfg +``` +Reboot the machine and you should have a working Gentoo install on your Google Pixel! + +## [Part 3](http://ampx.minhas.io/posts/2014/Jan/29/gentoo-on-the-google-pixel-pt-3.html) diff --git a/content/posts/gentoo-on-the-google-pixel-pt-3.md b/content/posts/gentoo-on-the-google-pixel-pt-3.md new file mode 100644 index 0000000..5db22f7 --- /dev/null +++ b/content/posts/gentoo-on-the-google-pixel-pt-3.md @@ -0,0 +1,15 @@ ++++ +title = 'Gentoo on the Google Pixel Pt. 3' +date = 2014-01-29T00:00:00-00:00 +category = 'pixel' +tags = ['howto'] ++++ + +## Pixel Configs +Decided to upload some config files to pastebin for everyone to use and easily get gentoo up and running on their Pixel. + +[Kernel config](http://pastebin.com/bX4ayMEM) + +[grub2](http://pastebin.com/mhhPSVT3) + +## [Part 4](http://ampx.minhas.io/posts/2014/Feb/17/gentoo-on-the-google-pixel-pt-4.html) diff --git a/content/posts/gentoo-on-the-google-pixel-pt-4.md b/content/posts/gentoo-on-the-google-pixel-pt-4.md new file mode 100644 index 0000000..2e2c9d6 --- /dev/null +++ b/content/posts/gentoo-on-the-google-pixel-pt-4.md @@ -0,0 +1,48 @@ ++++ +title = 'Gentoo on the Google Pixel Pt. 2' +date = 2014-02-17T00:00:00-00:00 +category = 'pixel' +tags = ['howto'] ++++ + +## Tiny Fixes +These are really just fixes for the font rendering/zoom levels of everything. As it turns out the world/userspace tools aren't really ready for high DPI. As such these are some quick tweaks to get everything not looking ridiculous. I use Firefox as my browser, urxvt (with 256-color) as my terminal, and i3 as my window manager. + + +## Fonts +Enable these using eselect +``` +Pixel Sub-Rendering: 10-sub-pixel-rgb.conf +LCD Filter: 11-lcdfilter-default.conf +``` + +## Xdefaults +```bash +!-- Xft settings -- ! +Xft.dpi: 180 +Xft.rgba: rgb +Xft.hinting: true +Xft.hintstyle: hintfull +Xft.antialiasing: false + +! -- Fonts -- ! +urxvt.font:xft:DejaVu Sans Mono:size=8 +urxvt.boldfont:xft:DejaVu Sans Mono:size=8 + +! -- URxvt settings -- ! + +urxvt*geometry: 80x30 +!urxvt.font: 9x15 +urxvt*background: #212629 +urxvt*foreground: #FFFFFF +urxvt*scrollBar: false +urxvt*matcher.button: 1 +urxvt*cursorBlink: true +urxvt*cursorColor: #c1c8c9 +urxvt.transparent: false +urxvt*allow_bold: true +urxvt*inheritPixmap: false +``` + +## Firefox Settings +After some tinkering, I've realized the best bet is to get the Default Full Zoom add-on and set the default zoom between 130% and 140%. diff --git a/content/posts/hello-world-deuxieme-partie.md b/content/posts/hello-world-deuxieme-partie.md new file mode 100644 index 0000000..8b344ca --- /dev/null +++ b/content/posts/hello-world-deuxieme-partie.md @@ -0,0 +1,195 @@ ++++ +title = 'Hello World Deuxième Partie' +date = 2020-01-18T19:35:18.446638Z +category = 'sudoscientist' ++++ + +```bash +sudoscientist:~# apt install golang nodejs +``` + +## Building sudoscientist +With the languages for my project picked out, I began working on a simple RESTful backend in golang. Here, there were choices for which router I would use, and the decision came down to either the `gorilla/mux` or `go-chi/chi`. While I could have used just used `net/http`, I wanted something simple to organize my routes and not have to worry about starting entirely from scratch. A routing frameworks was something that I had no intention of writing from scratch, nor did I want to start going down the path of writing a simple routing utility only to have to replace it later. The ability to plug in middleware to handle authentication and other common roadblocks also made using a routing framework attractive. The decision to use `go-chi/chi` was mostly down to how easy it was to read the source code and grasp what was happening. The simplicity of the tools allowed me to easily understand what I was doing, and hack on whatever else I needed along the way. With this, and the decision to use React/Redux already in place, I started building. + +## Golang, and what makes a website + +The proverb measured twice, cut once is a useful philosophy when building websites, or applications in general. The foresight of knowing what needs to get done makes it much easier to actually build it. This project was made easier because of the iterations I went through when using a traditional model, view, and controller framework. I knew that I needed separate data stores for my various components, such as users, profiles, and posts. I also knew I had to implement some type of authentication and authorization, possibly using JavaScript Web Tokens (JTWs) to pass the data between the clients and servers. I knew if I could implement this process using cURL, it would be fairly easy to transition to a proper JavaScript frontend. To store data I would use PostgreSQL, since that was the database I was most comfortable with, and it has plugins for some of the future projects I want to work on. + +The first step was implementing a simple database connection. After reading some best practices for handling having multiple modules, connecting to the same database, I opted for using dependency injection to pass the database details around to my various sub-components. +```golang +func main() { + // initiate the database + db, _ := database.NewDB() + defer db.Close() + auth.DB = db + auth.Init() + users.DB = db + users.Init() + blog.DB = db + blog.Init() +``` + +This allowed me to have one database connection, and the ability to pass the reference of that connection to my sub-components. It would also make it a matter of adding two lines to main.go for each new component that I needed accessing the database. Additionally, until I set up migrations, the `Init()` calls would allow me to initialize my tables. + +The next step was building out a bare bones user system. This would be split into two separate but interrelated packages. The first would be the `auth` package, which would be designated to handle authentication and passwords. The second would be the `users` package, which would contain the user's profile and other associated information. This would allow for me to decouple the profiles of users from the user themselves. + +The primary information needed in the authentication package would be a user's name, email address, and password. The creation of this was a simple SQL command. +```golang +func Init() { + DB.Exec("CREATE TABLE IF NOT EXISTS users (username text primary key, email text, password text, admin boolean);") +} +``` + +Creating a `struct` for passing this data around was also straightforward. +```golang +type SignUpCredentials struct { + Username string `json:"username", db:"username"` + Email string `json:"email", db:"email"` + Password string `json:"password", db:"password"` +} +``` + +As was getting the data into code. +```golang +creds := &SignUpCredentials{} +err := json.NewDecoder(r.Body).Decode(creds) +``` + +This allowed me to push the data to the backend service via json data `curl -d @file.json` which would add the file.json to the request and that could be deserialized and processed in golang. Using a mix of `dgrijalva/jwt-go` and `go-chi/jwtauth` allowed me to quickly prototype and keep most of the authentication process in the background. This was until I learned about the inherent insecure nature of JWT tokens being stored in JavaScript's `sessionstorage` and `localstorage`, which we will come back to later. + +For now, authentication was handled, and I proceeded to build out the basic functionality of a blog. This meant user profiles, which was minimized to: + +```golang +type User struct { + Username string `json:"username",db:"username"` + Email string `json:"email",db:"email"` + Country string `json:"country",db:"country"` + Bio string `json:"bio",db:"bio"` +} +``` + +Keeping things simple and modular was good for the first iteration of this blog. I can add arbitrary data to the `Bio` field until I feel like more fields are needed. The `Email` and `Username` fields are currently redundant, as they exist in the `auth` package as well, and will probably be stripped out later in favor of foreign keys. The bulk of the work left was the `blog` package. As the rest of this project, this was heavily influenced by the Django Framework's handling of `Posts`. +```golang +type BlogPost struct { + ID int `json:"id",db:"id"` + Title string `json:"title",db:"title"` + Slug string `json:"slug",db:"slug"` + Author string `json:"author",db:"author"` + Content string `json:"content",db:"content"` + TimePublished time.Time `json:"time_published", db:"time_published"` + Modified bool `json:"modified", db:"modified"` + TimeModified time.Time `json:"last_modified", db:"last_modified"` +} +``` + +Eventually, I may add a string field for backlinks, which would be useful for this post, and possibly other things as required. I wrote some simple routes to GET posts, and some routes to POST and PATCH. +```golang +func Routes() *chi.Mux { + r := chi.NewRouter() + r.Group(func(r chi.Router) { + r.Use(jwtauth.Verifier(TokenAuth)) + r.Use(jwtauth.Authenticator) + r.Post("/", createBlogPost) + r.Patch("/by-id/{id}", updateBlogPostById) + }) + r.Get("/", getBlogPosts) + r.Get("/by-slug/{slug}", getBlogPostBySlug) + r.Get("/by-id/{id}", getBlogPostById) + r.Get("/by-tag/{tag}", getBlogPostsByTag) + r.Get("/by-author/{author}", getBlogPostsByAuthor) + return r +} +``` + +In a few lines, I was able to declare the routes I would need, as well as apply authentication to certain routes using `go-chi/jwtauth`. Being able to authenticate via cURL, and create new blog posts with cURL, I decided to start working on the frontend. I knew I would have to fiddle with the backend further along the process, but I was content with the base I had and decided to dive into my apprehensions and fears. + +## NodeJS, and how to show a website + +JavaScript has always been a language I avoided. Between the hellhole that is `npm`, my discomfort with visuals, and my lack of need in making websites, I was able to avoid it for the most part. I made some simple websites in the Web 1.0 days, and even then my aesthetic senses were sub par. Even now, the v1.0.0 of sudoscientist is very minimalistic. I prefer the simple look of white on black text in a terminal over most fancy graphics. NodeJS and JavaScript as a whole is a very different from most development related work I've done. The JavaScript world as a whole seems very different. The JavaScript world seems very framework heavy. Do you use bootstrap or semantic? Angular? Vue? Meteor? Ionic? My decision in picking React/Redux was straight forward, I have a good friend (thanks Mark!) who is a seasoned user and someone that I could turn to for help in my misadventures. If it wasn't for that fact, I would probably still be trying to figure out which framework would work best for this project, and the other projects I want to pursue later. Like the decision to use Postgres, I wanted to make sure I could use the same methods in the future, and reuse as much code as possible. + +Learning React/Redux, even now, feels very much like learning React/Redux and not learning JavaScript. There is a ton of knowledge I now have that very specific to one framework. I don't think I would feel comfortable working with any other framework, and I don't even feel comfortable with React itself yet. Regardless, the project’s goals were set to be straight forward. I would go public with the site once I was able to display the posts from my previous blog, and a v1.0.0 would be cut once I was able to post from the blog's UI itself. This meant I would leave authentication until the end, and just work on getting RESTful interactions working with the backend, and displaying markdown. + +While working with the frontend, I employed a similar pattern for building out the application. I first focused on getting the appropriate libraries installed, and then working with the browser console to figure out what would be needed to make REST requests against the backend. Once this was completed, the next task was actually displaying posts in the browser. This would become a challenging task because it involves understanding the difference between the states of React, and Redux, along with understanding the event loop of rendering in React. This lead me to a lot of confusion, between the commits of [I have no idea what i'm doing :(](https://git.minhas.io/Asara/sudoscientist-js-frontend/commit/fabf06b8a6a53a39e7812fcc5eb4f47f634e33cf) and [It makes more sense now](https://git.minhas.io/Asara/sudoscientist-js-frontend/commit/922299010c3853d59c8af29bd151bb4a4d864934), I finally understood the difference between states, and how to merge JavaScript Objects together to push them to the state and have React rerender the web page. +```javascript +const initialState = { + entities: {}, +}; + +const normalizeEntities = (entities, payloadData) => { + const entitiesMap = {} + payloadData.forEach(post => entitiesMap[post.id] = post) + return {...entities, ...entitiesMap} +} + +export default (state = initialState, action) => { + switch (action.type) { + case 'FETCH_POSTS': + const mergedEntities = normalizeEntities(state.entities, action.payload) + return {...state, ...{entities: mergedEntities}} +... +``` + +This little bit of (magic) code allowed me to progress further and actually get blog posts in the browser. With this, I was content on publishing the blog with my old blog's posts, and start working on the next steps, authentication and making POST requests. + +## Authentication, JWTs, and Cookies + +Once I posted the blog, the next step would be to allow myself to make POST requests via the browser. This would first require me to set up some form of authentication. The initial revision of this would be very straightforward. Using `go-chi/jwtauth` allowed to quickly add a few lines to set up a simple JWT verifier and authenticator around specific routes +```golang +r.Group(func(r chi.Router) { + r.Use(jwtauth.Verifier(TokenAuth)) + r.Use(jwtauth.Authenticator) + r.Post("/", createBlogPost) + r.Patch("/by-id/{id}", updateBlogPostById) +}) +``` + +While this was a simple solution, along with storing the JWT in `localstorage` or `sessionstorage`, I started reading security guidelines on how to handle secrets in the browser. Upon coming across the OWASP requirement that local or session storage should not contain sensitive information, I reevaluated my solution. The implementation that made most sense to me was that of split-cookie authentication. Implementing this on the backend is a fun task, and is always my first step, I started working on a system that would be able to function with cURL. This would require writing some middleware for `go-chi/jwtauth`, which was as simple as: +```golang +func TokenFromSplitCookie(r *http.Request) string { + dataCookie, err := r.Cookie("DataCookie") + if err != nil { + return "" + } + signatureCookie, err := r.Cookie("SignatureCookie") + if err != nil { + return "" + } + cookie := dataCookie.Value + "." + signatureCookie.Value + return cookie +} +``` + +In addition to this, I added a function on the backend to set cookies in a secure manner: +```golang +func setCookies(w http.ResponseWriter, jwt string, expiration time.Time) string { + splitToken := strings.Split(jwt, ".") + dataCookie := http.Cookie{Name: "DataCookie", Value: strings.Join(splitToken[:2], "."), Expires: expiration, HttpOnly: false, Path: "/", Domain: ".sudoscientist.com", MaxAge: 360, Secure: true} + http.SetCookie(w, &dataCookie) + signatureCookie := http.Cookie{Name: "SignatureCookie", Value: splitToken[2], Expires: expiration, HttpOnly: true, Path: "/", Domain: ".sudoscientist.com", MaxAge: 360, Secure: true} + http.SetCookie(w, &signatureCookie) + return strings.Join(splitToken[:2], ".") +} +``` + +This bit of code would take the cookies passed up by the browser, of which the DataCookie was accessible to the JavaScript frontend, and the SignatureCookie would be HttpOnly, functionally disallowed any JavaScrpit from having the entire contents of the cookie, and would disallow updating the cookie since the signature would be invalid on altered data. Furthermore, since most of this was being handled by cookies, the process to incorporate getting data out of the was as simple as `const datacookie = cookies.get('DataCookie');`. This allowed me to get the data required to render user information accurately in the UI. Finally, with this in place, I was able to have a secure authentication system, and all that was left was setting up a way to make new posts! + +## First POSTs and the future of sudoscientist +One thing that is understandable about the Node.js ecosystem is that the vast library of node modules is a bit of a necessity. There is a LOT of things that need to be done repeatedly to render data in a browser. One of these repeated functions is saving, loading, and rendering Markdown. In order to accomplish this in React, I ended up using React Markdown Editor. The code for this was as simple as: +```javascript +
+ + Promise.resolve()} + /> +
+``` + +The `react-mde` library made it simple to edit and view my markdown files, load it into a JSON blob, and POST it to the backend. This will also be leveraged for the PATCH function to update old posts in the future. + +Looking forward, the next steps for the blog will be implementing the PATCH feature, maybe adding some regions for backlinks, figuring out and implementing database migrations, and finally comments. Once that is done, I think I will continue to work on other projects and document them on sudoscientist. These two initial blog posts were primarily retrospectives, and as such were written with me just referencing old git commits and piecing the story together. In the future I plan on writing down notes and actually document what I'm doing while I do it. + +It's a pleasure to have you reading, and thanks! Hope to see you in the next one. diff --git a/content/posts/hello-world.md b/content/posts/hello-world.md new file mode 100644 index 0000000..eaddc43 --- /dev/null +++ b/content/posts/hello-world.md @@ -0,0 +1,35 @@ ++++ +title = 'Hello World!' +date = 2019-12-21T22:25:09-04:00 +category = 'sudoscientist' ++++ + +```bash +scientist:~$ sudo su - +sudoscientist:~# +``` + +## Welcome to my blog! +Nice to meet you. Thanks for being here. Here is a little bit about me, and my blog, sudoscientist. I go by Asara on the internet, and I am an aspiring engineer. I've been in the industry for a while, but it is an infinitely wide industry, and there is still much more to learn. This will be a pretty long blog post, but not a very technical one. The next post will deal with the actual process of building sudoscientist! The motivation for sudoscientist was twofold. + +The primary motivation was that I wanted to get back into blogging, and being that I haven't made any posts in about 5 years, I had to take action. I have learned a ton in that time period, and have shifted my focus from the general catchall of `Linux` to `architecture`, `the cloud`, and `automation`. Docker has absorbed the industry in this time, and from my humble dabbling in building docker back in 2014, to building Kubernetes in 2019, a lot has changed. I've flipped through a few jobs, built some really cool solutions to interesting problems, and it would be fun to write about the challenges that I will face going forward. + +The second motivation was technical development. My previous blog was built with Pelican, the static site generator. This was a workable solution, but left a lot to be desired. I had no feeling of ownership over it. Like most `things` in life, it seemed like a tool that was given to me, which I used. I realized I didn't truly understand what was going on under the hood. + +A static site generator is a simple tool, it will take some plain text files, applying some templating to them, and rendering them as HTML. This however wasn't interactive. I wouldn't be able to improve or extend this solution without adding other `tools` from other `providers`. The feeling that I was just using tools and not building my own solutions bothered me. That lead to the beginning of sudoscientist. + +## Iterations +Blog engines are easy right? Everyone has one! + +My personal experience has been that many engineers enter technology via content management systems. WordPress, Django, (insert your favorite content management system here), etc. This is a great start. Most of the `confusing` or `complicated` features are hidden from the developer. Model, view, and controller allows engineers to build solutions much faster, but leaves them tangentially dealing with databases, having a hands off approach to networking, and rarely working with serialization of data. Authentication? Already built in. Comments? A click away. + +This was my experience. I started with Python, the language I was most comfortable with, and hacked away at Django. Django was a nice starting point. I had always been apprehensive about programming, as I never had any real math training. I still don't know much about algorithms, or what the best data structures fit which situations, but that will come with due time. Django allowed me to rapidly build out a solution, but it didn't really allow me to fiddle with things. I always felt that building around `middleware` was cumbersome, and frankly annoying. Plug and play works great, but comes at the cost of understanding. + +The next step for me was moving from Django to Flask. Another Python framework, but a `micro-framework` this time. This again, was a nice comfortable median for me. I wasn't so deep in the weeds; I was overwhelmed, but I still didn't really need to learn what I was doing. Documentation such as `The Flask Mega-Tutorial` made working with Flask very straightforward. This was a good way to take off the training wheels, but I still felt as if I was selling myself short. Templating was all done server side, and the modern world was moving towards technologies such as Angular.js and the then, very young, React.js. Like Docker, JavaScript has taken over the world as well. This lead me to realize that I would have to bite the bullet eventually, and I decided to move away from the model/view/controller methodology, and towards the paradigm of RESTful APIs and static sites written in Javascript to access those APIs. + +## Enter sudoscientist +I began writing the back-end to sudoscientist in Python. Flask-restful is a fun tool, but I decided quickly that I wanted to move past Python for larger projects. Python is a great scripting tool, and I wanted to use it in that context. The decision to learn React was similar to that of choosing Go. Go has been gaining steam over the past few years and has a very mature HTTP standard library. While I didn't write sudoscientist with only the standard library, go-chi, the routing framework used, is a very simple one, and is fairly easy to read and understand. I have also over time come to avoid, and possibly dislike object oriented programming. Golang's hands off approach felt smooth. Along with this, many tools in my `${DAY_JOB}`` are built in Go. It made it a compelling language to work with. The focus on a low level of external dependencies when building, and the ethos of using the standard library for as much as you can also piqued my interest. With the tools chosen, I began working. + +I [started](https://git.minhas.io/Asara/sudoscientist-go-backend/commit/b7132ce2dc631f1b30c7f0e95b1f46ec6626081e) with the back-end in February, and hacked around with learning the language, the tools, and using curl to make requests. Once I had something I thought I could work with, I [began](https://git.minhas.io/Asara/sudoscientist-js-frontend/commit/79ca8e12975d4c2a8d61ec00d7a71f3987e6bc68) the frontend, with a read me stating `FML`. About 9 months after starting this project, I am now able to say Hello World! + +Thanks for being here, and I hope to catch you on the next one! diff --git a/content/posts/recovered-the-blog.md b/content/posts/recovered-the-blog.md new file mode 100644 index 0000000..dd3bf06 --- /dev/null +++ b/content/posts/recovered-the-blog.md @@ -0,0 +1,7 @@ ++++ +title = 'Recovered the Blog' +date = 2014-08-15T00:00:00-00:00 ++++ + +## Back up and running +Fixed most of the issues with the blog. Everything should be up and running now! Expect posts soon. diff --git a/content/posts/recovering-the-blog.md b/content/posts/recovering-the-blog.md new file mode 100644 index 0000000..e53f00c --- /dev/null +++ b/content/posts/recovering-the-blog.md @@ -0,0 +1,7 @@ ++++ +title = 'Recovering the Blog' +date = 2014-08-04T00:00:00-00:00 ++++ + +## Slowly but surely +After a failure of my SD card from a power surge, I've got my blog back up. Just have to get the theme and all working again, ignore the french! diff --git a/resources/_gen/assets/scss/css/base.scss_3b33337114e481782feeb60752452e17.content b/resources/_gen/assets/scss/css/base.scss_3b33337114e481782feeb60752452e17.content new file mode 100644 index 0000000..c1e0d86 --- /dev/null +++ b/resources/_gen/assets/scss/css/base.scss_3b33337114e481782feeb60752452e17.content @@ -0,0 +1,3 @@ +@font-face{font-display:swap;font-family:'Fira Code';font-style:normal;font-weight:400;src:url("../fonts/FiraCode-Regular.woff") format("woff")}@font-face{font-display:swap;font-family:'Fira Code';font-style:normal;font-weight:800;src:url("../fonts/FiraCode-Bold.woff") format("woff")}.button-container{display:table;margin-left:auto;margin-right:auto}button,.button,a.button{position:relative;display:flex;align-items:center;justify-content:center;padding:8px 18px;margin:5px 0;text-decoration:none;text-align:center;border-radius:8;border:1px solid #23B0FF;background:#23B0FF;color:#1d212c;font:inherit;font-weight:bold;appearance:none;cursor:pointer;outline:none}button:hover,.button:hover,a.button:hover{background:rgba(35,176,255,0.9)}button.outline,.button.outline,a.button.outline{background:transparent;box-shadow:none;padding:8px 18px}button.outline :hover,.button.outline :hover,a.button.outline :hover{transform:none;box-shadow:none}button.link,.button.link,a.button.link{background:none;font-size:1rem}button.small,.button.small,a.button.small{font-size:.8rem}button.wide,.button.wide,a.button.wide{min-width:200px;padding:14px 24px}a.read-more,a.read-more:hover,a.read-more:active{display:inline-flex;border:none;color:#23B0FF;background:none;box-shadow:none;padding:0;margin:20px 0;max-width:100%}.code-toolbar{margin-bottom:20px}.code-toolbar .toolbar-item a{position:relative;display:inline-flex;align-items:center;justify-content:center;padding:3px 8px;margin-bottom:5px;text-decoration:none;text-align:center;font-size:13px;font-weight:500;border-radius:8px;border:1px solid transparent;appearance:none;cursor:pointer;outline:none}input,textarea,select{background:transparent;color:#23B0FF;border:1px solid #23B0FF;border-radius:0;padding:10px;margin:5px 0;font:inherit;appearance:none}input:focus,input :active,textarea:focus,textarea :active,select:focus,select :active{border-color:#fff;outline:1px solid #fff}input:active,textarea:active,select:active{box-shadow:none}select{background:#1d212c}select option{background:#1d212c}::placeholder{color:rgba(35,176,255,0.5)}input[type="checkbox"]{vertical-align:middle;padding:10px;box-shadow:inset 0 0 0 3px #1d212c}input[type="checkbox"]:checked{background:#23B0FF}.header{display:flex;flex-direction:column;position:relative}@media print{.header{display:none}}.header__inner{display:flex;align-items:center;justify-content:space-between}.header__logo{display:flex;flex:1}.header__logo:after{content:'';background:repeating-linear-gradient(90deg, #23B0FF, #23B0FF 2px, transparent 0, transparent 10px);display:block;width:100%;right:10px}.header__logo a{flex:0 0 auto;max-width:100%;text-decoration:none}.navigation-menu{display:flex;align-items:flex-start;justify-content:space-between;margin:20px 1px}@media (max-width: 684px){.navigation-menu{margin:0}}.navigation-menu__inner{display:flex;flex:1;flex-wrap:wrap;list-style:none;margin:0;padding:0}.navigation-menu__inner>li{flex:0 0 auto;margin-bottom:10px;white-space:nowrap}.navigation-menu__inner>li:not(:last-of-type){margin-right:20px}@media (max-width: 684px){.navigation-menu__inner{flex-direction:column;align-items:flex-start;padding:0}.navigation-menu__inner li{margin:0;padding:5px}}.navigation-menu .spacer{flex-grow:1 !important}.menu{display:flex;flex-direction:column;position:relative;list-style:none;padding:0;margin:0}.menu__trigger{margin-right:0 !important;color:#23B0FF;user-select:none;cursor:pointer}.menu__dropdown{display:none;flex-direction:column;position:absolute;background:#1d212c;box-shadow:0 10px rgba(29,33,44,0.8),-10px 10px rgba(29,33,44,0.8),10px 10px rgba(29,33,44,0.8);color:white;border:2px solid;margin:0;padding:10px;top:10px;left:0;list-style:none;z-index:99}.open .menu__dropdown{display:flex}.menu__dropdown>li{flex:0 0 auto}.menu__dropdown>li:not(:last-of-type){margin-bottom:10px}.menu__dropdown>li a{display:flex;padding:5px}@media (max-width: 684px){.menu--desktop{display:none}}.menu--mobile .menu__trigger{color:#23B0FF;border:2px solid;margin-left:10px;height:100%;padding:3px 8px;margin-bottom:0 !important;position:relative;cursor:pointer;display:none}@media (max-width: 684px){.menu--mobile .menu__trigger{display:block}}@media (max-width: 684px){.menu--mobile .menu__dropdown{left:auto;right:0}}.menu--mobile li{flex:0 0 auto}.menu--mobile li:not(:last-of-type){margin-bottom:10px}.menu--language-selector .menu__trigger{color:#23B0FF;border:2px solid;margin-left:10px;height:100%;padding:3px 8px;margin-bottom:0 !important;position:relative;cursor:pointer}@media (max-width: 684px){.menu--language-selector .menu__trigger{display:none}}.menu--language-selector .menu__dropdown{left:auto;right:0}.logo{display:flex;align-items:center;text-decoration:none;background:#23B0FF;color:black;padding:5px 10px}html{box-sizing:border-box}*,*:before,*:after{box-sizing:inherit}body{margin:0;padding:0;font-family:'Fira Code', Monaco, Consolas, Ubuntu Mono, monospace;font-size:1rem;line-height:1.54;letter-spacing:-0.02em;background-color:#1d212c;color:#fff;text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased;font-feature-settings:"liga", "tnum", "zero", "ss01", "locl";font-variant-ligatures:contextual;-webkit-overflow-scrolling:touch;-webkit-text-size-adjust:100%}@media (max-width: 684px){body{font-size:1rem}}.headings--one-size h1,.headings--one-size h2,.headings--one-size h3,.headings--one-size h4,.headings--one-size h5,.headings--one-size h6{line-height:1.3}.headings--one-size h1:not(first-child),.headings--one-size h2:not(first-child),.headings--one-size h3:not(first-child),.headings--one-size h4:not(first-child),.headings--one-size h5:not(first-child),.headings--one-size h6:not(first-child){margin-top:40px}.headings--one-size h1,.headings--one-size h2,.headings--one-size h3{font-size:1.4rem}.headings--one-size h4,.headings--one-size h5,.headings--one-size h6{font-size:1.2rem}a{color:inherit}img{display:block;max-width:100%}img.left{margin-right:auto}img.center{margin-left:auto;margin-right:auto}img.right{margin-left:auto}p{margin-bottom:20px}figure{display:table;max-width:100%;margin:25px 0}figure.left{margin-right:auto}figure.center{margin-left:auto;margin-right:auto}figure.right{margin-left:auto}figure figcaption{font-size:14px;padding:5px 10px;margin-top:5px;background:#23B0FF;color:#1d212c}figure figcaption.left{text-align:left}figure figcaption.center{text-align:center}figure figcaption.right{text-align:right}code,kbd{font-family:'Fira Code', Monaco, Consolas, Ubuntu Mono, monospace !important;font-feature-settings:normal;background:rgba(35,176,255,0.2);color:#23B0FF;padding:1px 6px;margin:0 2px;font-size:.95rem}code code,code kbd,kbd code,kbd kbd{background:transparent;padding:0;margin:0}pre{background:transparent !important;padding:20px 10px;margin:40px 0;font-size:.95rem !important;overflow:auto;border-top:1px solid rgba(255,255,255,0.1);border-bottom:1px solid rgba(255,255,255,0.1)}pre+pre{border-top:0;margin-top:-40px}@media (max-width: 684px){pre{white-space:pre-wrap;word-wrap:break-word}}pre code{background:none !important;margin:0;padding:0;font-size:inherit;border:none}blockquote{border-top:1px solid #23B0FF;border-bottom:1px solid #23B0FF;margin:40px 0;padding:25px}@media (max-width: 684px){blockquote{padding-right:0}}blockquote p:first-of-type{margin-top:0}blockquote p:last-of-type{margin-bottom:0}blockquote p{position:relative}blockquote p:first-of-type:before{content:'>';display:block;position:absolute;left:-25px;color:#23B0FF}blockquote.twitter-tweet{position:relative;background:rgba(35,176,255,0.1);font:inherit;color:inherit;border:1px solid #23B0FF;padding-top:60px}blockquote.twitter-tweet p:before{content:''}blockquote.twitter-tweet:before{content:'> From Twitter:';position:absolute;top:20px;color:#23B0FF;font-weight:bold}blockquote.twitter-tweet a{color:#23B0FF}table{table-layout:auto;border-collapse:collapse;width:100%;margin:40px 0}table,th,td{border:1px dashed #23B0FF;padding:10px}th{color:#23B0FF}ul,ol{margin-left:22px;padding:0}ul li,ol li{position:relative}@media (max-width: 684px){ul,ol{margin-left:20px}}ol{list-style:none;counter-reset:li}ol>li{counter-increment:li}ol>li:before{content:counter(li);position:absolute;right:calc(100% + 10px);color:#23B0FF;display:inline-block;text-align:right}ol>li>ol{margin-left:38px}ol>li>ol>li{counter-increment:li}ol>li>ol>li:before{content:counters(li, ".") " "}mark{background:#23B0FF;color:#1d212c}.container{display:flex;flex-direction:column;padding:40px;max-width:864px;min-height:100vh;border-right:1px solid rgba(255,255,255,0.1)}.container.full,.container.center{border:none;margin:0 auto}.container.full{max-width:100%}@media (max-width: 684px){.container{padding:20px}}@media print{.container{display:initial}}.content{display:flex;flex-direction:column}@media print{.content{display:initial}}hr{width:100%;border:none;background:rgba(255,255,255,0.1);height:1px}.hidden{display:none}sup{line-height:0}.index-content{margin-top:20px}.framed{border:1px solid #23B0FF;padding:20px}.framed *:first-child{margin-top:0}.framed *:last-child{margin-bottom:0}.posts{width:100%}.post{width:100%;text-align:left;margin:20px auto;padding:20px 0}.post:not(:last-of-type){border-bottom:1px solid rgba(255,255,255,0.1)}.post-meta{font-size:1rem;margin-bottom:10px;color:rgba(35,176,255,0.7)}.post-meta>*:not(:first-child)::before{content:"::";display:inline-block;margin:0 8px}.post-title{position:relative;color:#23B0FF;margin:0 0 15px;padding-bottom:15px;border-bottom:3px dotted #23B0FF}.post-title:after{content:'';position:absolute;bottom:2px;display:block;width:100%;border-bottom:3px dotted #23B0FF}.post-title a{text-decoration:none}.post-tags{display:block;margin-bottom:20px;font-size:1rem;opacity:.5}.post-tags a{text-decoration:none}.post-content{margin-top:30px}.post-cover{border:20px solid #23B0FF;background:transparent;margin:40px 0;padding:20px}@media (max-width: 684px){.post-cover{padding:10px;border-width:10px}}.post ul{list-style:none}.post ul li:not(:empty):before{content:'-';position:absolute;left:-20px;color:#23B0FF}.post--regulation h1{justify-content:center}.post--regulation h2{justify-content:center;margin-bottom:10px}.post--regulation h2+h2{margin-top:-10px;margin-bottom:20px}.hanchor{color:rgba(35,176,255,0.9);text-decoration:none;margin-left:10px;visibility:hidden}h1:hover a,h2:hover a,h3:hover a,h4:hover a{visibility:visible}.footnotes{color:rgba(255,255,255,0.5)}.pagination{margin-top:50px}@media print{.pagination{display:none}}.pagination__title{display:flex;text-align:center;position:relative;margin:100px 0 20px}.pagination__title-h{text-align:center;margin:0 auto;padding:5px 10px;background:#1d212c;color:rgba(255,255,255,0.3);font-size:.8rem;text-transform:uppercase;text-decoration:none;letter-spacing:.1em;z-index:1}.pagination__title hr{position:absolute;left:0;right:0;width:100%;margin-top:15px;z-index:0}.pagination__buttons{display:flex;align-items:center;justify-content:center;flex-flow:row wrap;gap:10px}.pagination__buttons a{text-decoration:none}.button{position:relative;display:inline-flex;align-items:center;justify-content:center;font-size:1rem;padding:0;appearance:none}@media (max-width: 684px){.button{flex:1}}.button a{display:flex;justify-content:center;flex:1;padding:8px 16px;text-decoration:none;text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.button__text{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.button.next .button__icon{margin-left:8px}.button.previous .button__icon{margin-right:8px}.footer{padding:40px 0;flex-grow:0;opacity:.5}.footer__inner{display:flex;align-items:center;justify-content:space-between;margin:0;width:760px;max-width:100%}@media (max-width: 900px){.footer__inner{flex-direction:column}}.footer a{color:inherit}.footer .copyright{display:flex;flex-flow:row wrap;flex:1;align-items:center;font-size:1rem;justify-content:center}.footer .copyright--user{margin:auto;text-align:center}.footer .copyright>*:first-child:not(:only-child){margin-right:10px}.footer .copyright span{white-space:nowrap}code[class*="language-"],pre[class*="language-"]{color:#ccc;background:none;font-family:Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;font-size:1em;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;line-height:1.5;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-hyphens:none;-moz-hyphens:none;-ms-hyphens:none;hyphens:none}pre[class*="language-"]{padding:1em;margin:.5em 0;overflow:auto}:not(pre)>code[class*="language-"],pre[class*="language-"]{background:#2d2d2d}:not(pre)>code[class*="language-"]{padding:.1em;border-radius:.3em;white-space:normal}.token.comment,.token.block-comment,.token.prolog,.token.doctype,.token.cdata{color:#999}.token.punctuation{color:#ccc}.token.tag,.token.attr-name,.token.namespace,.token.deleted{color:#e2777a}.token.function-name{color:#6196cc}.token.boolean,.token.number,.token.function{color:#f08d49}.token.property,.token.class-name,.token.constant,.token.symbol{color:#f8c555}.token.selector,.token.important,.token.atrule,.token.keyword,.token.builtin{color:#cc99cd}.token.string,.token.char,.token.attr-value,.token.regex,.token.variable{color:#7ec699}.token.operator,.token.entity,.token.url{color:#67cdcc}.token.important,.token.bold{font-weight:bold}.token.italic{font-style:italic}.token.entity{cursor:help}.token.inserted{color:green}pre[data-line]{position:relative;padding:1em 0 1em 3em}.line-highlight{position:absolute;left:0;right:0;padding:inherit 0;margin-top:1em;background:rgba(153,122,102,0.08);background:linear-gradient(to right, rgba(153,122,102,0.1) 70%, rgba(153,122,102,0));pointer-events:none;line-height:inherit;white-space:pre}@media print{.line-highlight{-webkit-print-color-adjust:exact;color-adjust:exact}}.line-highlight:before,.line-highlight[data-end]:after{content:attr(data-start);position:absolute;top:.4em;left:.6em;min-width:1em;padding:0 .5em;background-color:rgba(153,122,102,0.4);color:#f5f2f0;font:bold 65%/1.5 sans-serif;text-align:center;vertical-align:.3em;border-radius:999px;text-shadow:none;box-shadow:0 1px white}.line-highlight[data-end]:after{content:attr(data-end);top:auto;bottom:.4em}.line-numbers .line-highlight:before,.line-numbers .line-highlight:after{content:none}pre[id].linkable-line-numbers span.line-numbers-rows{pointer-events:all}pre[id].linkable-line-numbers span.line-numbers-rows>span:before{cursor:pointer}pre[id].linkable-line-numbers span.line-numbers-rows>span:hover:before{background-color:rgba(128,128,128,0.2)}pre[class*="language-"].line-numbers{position:relative;padding-left:3.8em;counter-reset:linenumber}pre[class*="language-"].line-numbers>code{position:relative;white-space:inherit}.line-numbers .line-numbers-rows{position:absolute;pointer-events:none;top:0;font-size:100%;left:-3.8em;width:3em;letter-spacing:-1px;border-right:1px solid #999;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.line-numbers-rows>span{display:block;counter-increment:linenumber}.line-numbers-rows>span:before{content:counter(linenumber);color:#999;display:block;padding-right:0.8em;text-align:right}.command-line-prompt{border-right:1px solid #999;display:block;float:left;font-size:100%;letter-spacing:-1px;margin-right:1em;pointer-events:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.command-line-prompt>span:before{color:#999;content:' ';display:block;padding-right:0.8em}.command-line-prompt>span[data-user]:before{content:"[" attr(data-user) "@" attr(data-host) "] $"}.command-line-prompt>span[data-user="root"]:before{content:"[" attr(data-user) "@" attr(data-host) "] #"}.command-line-prompt>span[data-prompt]:before{content:attr(data-prompt)}div.code-toolbar{position:relative}div.code-toolbar>.toolbar{position:absolute;top:.3em;right:.2em;transition:opacity 0.3s ease-in-out;opacity:0}div.code-toolbar:hover>.toolbar{opacity:1}div.code-toolbar:focus-within>.toolbar{opacity:1}div.code-toolbar>.toolbar>.toolbar-item{display:inline-block}div.code-toolbar>.toolbar>.toolbar-item>a{cursor:pointer}div.code-toolbar>.toolbar>.toolbar-item>button{background:none;border:0;color:inherit;font:inherit;line-height:normal;overflow:visible;padding:0;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none}div.code-toolbar>.toolbar>.toolbar-item>a,div.code-toolbar>.toolbar>.toolbar-item>button,div.code-toolbar>.toolbar>.toolbar-item>span{color:#bbb;font-size:.8em;padding:0 .5em;background:#f5f2f0;background:rgba(224,224,224,0.2);box-shadow:0 2px 0 0 rgba(0,0,0,0.2);border-radius:.5em}div.code-toolbar>.toolbar>.toolbar-item>a:hover,div.code-toolbar>.toolbar>.toolbar-item>a:focus,div.code-toolbar>.toolbar>.toolbar-item>button:hover,div.code-toolbar>.toolbar>.toolbar-item>button:focus,div.code-toolbar>.toolbar>.toolbar-item>span:hover,div.code-toolbar>.toolbar>.toolbar-item>span:focus{color:inherit;text-decoration:none}code.language-css,code.language-scss,.token.boolean,.token.string,.token.entity,.token.url,.language-css .token.string,.language-scss .token.string,.style .token.string,.token.attr-value,.token.keyword,.token.control,.token.directive,.token.statement,.token.regex,.token.atrule,.token.number,.token.inserted,.token.important{color:#23B0FF !important}.token.tag-id,.token.atrule-id,.token.operator,.token.unit,.token.placeholder,.token.variable,.token.tag,.token.attr-name,.token.namespace,.token.deleted,.token.property,.token.class-name,.token.constant,.token.symbol{color:rgba(35,176,255,0.7) !important}.token.property,.token.function,.token.function-name,.token.deleted,code.language-javascript,code.language-html,.command-line-prompt>span:before{color:#98999a !important}.token.selector,.token.tag,.token.punctuation{color:white}.token.comment,.token.prolog,.token.doctype,.token.cdata{color:rgba(255,255,255,0.3) !important}.token.namespace{opacity:.7 !important}pre[data-line]{position:relative}pre[class*="language-"]{margin:0;padding:0;overflow:auto}.line-highlight{position:absolute;left:0;right:0;padding:0;margin:0;background:rgba(47,174,245,0.08);pointer-events:none;line-height:inherit;white-space:pre}.line-highlight:before,.line-highlight[data-end]:after{content:attr(data-start);position:absolute;left:.6em;min-width:1em;padding:0 .5em;background-color:rgba(153,122,102,0.4);color:#f5f2f0;font:bold 65%/1.5 sans-serif;text-align:center;vertical-align:.3em;border-radius:999px;text-shadow:none;box-shadow:0 1px white}.line-highlight[data-end]:after{content:attr(data-end);top:auto;bottom:.4em}.line-numbers .line-highlight:before,.line-numbers .line-highlight:after{content:none}.code-toolbar{position:relative;margin:40px 0;padding:20px;border:1px solid rgba(255,255,255,0.1)}.code-toolbar+.code-toolbar,.code-toolbar+.highlight,.code-toolbar+.highlight .code-toolbar{border-top:0;margin-top:calc(-1 * $code-margin)}.code-toolbar pre,.code-toolbar code{border:none}.code-toolbar code{display:block;color:inherit}.code-toolbar>.toolbar button{font-size:.8em !important;background:rgba(224,224,224,0.2) !important;color:#bbb !important;box-shadow:0 2px 0 0 rgba(0,0,0,0.2) !important;border-radius:0 !important;margin:6px !important;padding:10px !important;user-select:none}.collapsable-code{position:relative;width:100%;margin:40px 0}.collapsable-code input[type="checkbox"]{position:absolute;visibility:hidden}.collapsable-code input[type="checkbox"]:checked~pre,.collapsable-code input[type="checkbox"]:checked~.code-toolbar pre{height:0;padding:0;border-top:none}.collapsable-code input[type="checkbox"]:checked~.code-toolbar{padding:0;border-top:none}.collapsable-code input[type="checkbox"]:checked~.code-toolbar .toolbar{display:none}.collapsable-code input[type="checkbox"]:checked~label .collapsable-code__toggle:after{content:attr(data-label-expand)}.collapsable-code label{position:relative;display:flex;justify-content:space-between;min-width:30px;min-height:30px;margin:0;border-bottom:1px solid #2faef5;cursor:pointer}.collapsable-code__title{flex:1;color:#23B0FF;padding:3px 10px;text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.collapsable-code__language{color:#23B0FF;border:1px solid #2faef5;border-bottom:none;text-transform:uppercase;padding:3px 10px}.collapsable-code__toggle{color:#23B0FF;font-size:16px;padding:3px 10px}.collapsable-code__toggle:after{content:attr(data-label-collapse)}.collapsable-code pre{margin-top:0}.collapsable-code pre::first-line{line-height:0}.collapsable-code .code-toolbar{margin:0}.terms h3{font-size:initial}.terms ul{list-style:none}.terms ul li a{color:#23B0FF}.terms ul li:not(:empty):before{content:'-';position:absolute;left:-20px;color:#23B0FF}body .gist .blob-num,body .gist .blob-code-inner{border:none} + +/*# sourceMappingURL=styles.css.map */ \ No newline at end of file diff --git a/resources/_gen/assets/scss/css/base.scss_3b33337114e481782feeb60752452e17.json b/resources/_gen/assets/scss/css/base.scss_3b33337114e481782feeb60752452e17.json new file mode 100644 index 0000000..e7b8e7c --- /dev/null +++ b/resources/_gen/assets/scss/css/base.scss_3b33337114e481782feeb60752452e17.json @@ -0,0 +1 @@ +{"Target":"styles.css","MediaType":"text/css","Data":{}} \ No newline at end of file diff --git a/static/favicon.ico b/static/favicon.ico new file mode 100644 index 0000000..b7bd769 Binary files /dev/null and b/static/favicon.ico differ diff --git a/static/images/profile.jpg b/static/images/profile.jpg new file mode 100644 index 0000000..fc3d235 Binary files /dev/null and b/static/images/profile.jpg differ diff --git a/themes/Hugo-2-Gopher-and-Gemini b/themes/Hugo-2-Gopher-and-Gemini new file mode 160000 index 0000000..87ce232 --- /dev/null +++ b/themes/Hugo-2-Gopher-and-Gemini @@ -0,0 +1 @@ +Subproject commit 87ce2329dea2cf2903fb044b9eb6c9f14f7d77dd diff --git a/themes/risotto/.gitignore b/themes/risotto/.gitignore new file mode 100644 index 0000000..2a8645f --- /dev/null +++ b/themes/risotto/.gitignore @@ -0,0 +1 @@ +.hugo_build.lock diff --git a/themes/risotto/LICENSE b/themes/risotto/LICENSE new file mode 100644 index 0000000..5c93805 --- /dev/null +++ b/themes/risotto/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2021 Joe Roe + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/themes/risotto/config.toml b/themes/risotto/config.toml new file mode 100644 index 0000000..7c79705 --- /dev/null +++ b/themes/risotto/config.toml @@ -0,0 +1,3 @@ +[module] + [module.hugoVersion] + min = "0.41.0" diff --git a/themes/risotto/layouts/404.html b/themes/risotto/layouts/404.html new file mode 100644 index 0000000..e69de29 diff --git a/themes/risotto/layouts/_default/baseof.html b/themes/risotto/layouts/_default/baseof.html new file mode 100644 index 0000000..9ef13f5 --- /dev/null +++ b/themes/risotto/layouts/_default/baseof.html @@ -0,0 +1,36 @@ + + + + + {{- partial "head.html" . -}} + + + +
+ + + +
+ {{- block "main" . }}{{- end }} +
+ +
+
+ {{- partial "about.html" . -}} +
+
+
+ {{- block "aside" . }}{{- end }} +
+
+ +
+ {{- partial "footer.html" . -}} +
+ +
+ + + diff --git a/themes/risotto/layouts/_default/index.rss.xml b/themes/risotto/layouts/_default/index.rss.xml new file mode 100644 index 0000000..3261903 --- /dev/null +++ b/themes/risotto/layouts/_default/index.rss.xml @@ -0,0 +1,34 @@ +{{- printf "" | safeHTML }} + + + {{ .Site.Title }} + {{ .Permalink }} + Recent content on {{ .Site.Title }} + Hugo -- gohugo.io{{ with .Site.LanguageCode }} + {{.}}{{end}}{{ with .Site.Author.email }} + {{.}}{{ with $.Site.Author.name }} ({{.}}){{end}}{{end}}{{ with .Site.Author.email }} + {{.}}{{ with $.Site.Author.name }} ({{.}}){{end}}{{end}}{{ with .Site.Copyright }} + {{.}}{{end}}{{ if not .Date.IsZero }} + {{ .Date.Format "Mon, 02 Jan 2006 15:04:05 -0700" | safeHTML }}{{ end }} + {{ with .OutputFormats.Get "RSS" }} + {{ printf "" .Permalink .MediaType | safeHTML }} + {{ end }} + + + {{ $pages := (where .Site.RegularPages "Section" "posts") }} + {{ range .Translations }} + {{ $pages = $pages | lang.Merge (where .Site.RegularPages "Section" "posts") }} + {{ end }} + + {{ range $pages }} + + {{ .Title }} + {{ .Permalink }} + {{ .Date.Format "Mon, 02 Jan 2006 15:04:05 -0700" | safeHTML }} + {{ with .Site.Author.email }}{{.}}{{ with $.Site.Author.name }} ({{.}}){{end}}{{end}} + {{ .Permalink }} + {{ " + + {{ end }} + + diff --git a/themes/risotto/layouts/_default/li.html b/themes/risotto/layouts/_default/li.html new file mode 100644 index 0000000..c85e091 --- /dev/null +++ b/themes/risotto/layouts/_default/li.html @@ -0,0 +1 @@ +
  • {{ .Title | markdownify }}
  • diff --git a/themes/risotto/layouts/_default/list.html b/themes/risotto/layouts/_default/list.html new file mode 100644 index 0000000..6966c31 --- /dev/null +++ b/themes/risotto/layouts/_default/list.html @@ -0,0 +1,16 @@ +{{ define "main" }} +

    {{ .Title | markdownify }}

    + + {{ .Content }} + + + +{{ end }} + +{{define "aside" }} + {{ if .Params.description }}

    {{ .Params.description }}

    {{ end }} +{{ end }} diff --git a/themes/risotto/layouts/_default/single.html b/themes/risotto/layouts/_default/single.html new file mode 100644 index 0000000..77abf32 --- /dev/null +++ b/themes/risotto/layouts/_default/single.html @@ -0,0 +1,29 @@ +{{ define "main" }} +
    +

    {{ .Title | markdownify }}

    +
    + {{ if .Params.toc }} + + {{ end }} +
    + {{ .Content }} +
    + +{{ end }} + +{{define "aside" }} + {{ if .Params.description }}

    {{ .Params.description }}

    {{ end }} + {{ if or (.Params.author) (.Params.date) }} +

    + {{ if .Params.author }}By {{ .Params.author }}{{ if .Date }}, {{ end }}{{ end }} + {{ if .Date }}{{ .Date.Format "2006-01-02" }}{{ end }} +

    + {{ end }} + + {{ if and (.Params.toc) (.TableOfContents) }} +
    + On this page: + {{ .TableOfContents }} + {{ end }} +{{ end }} diff --git a/themes/risotto/layouts/index.html b/themes/risotto/layouts/index.html new file mode 100644 index 0000000..c346e42 --- /dev/null +++ b/themes/risotto/layouts/index.html @@ -0,0 +1,4 @@ +{{ define "main" }} + {{ .Content }} +{{ end }} + diff --git a/themes/risotto/layouts/partials/about.html b/themes/risotto/layouts/partials/about.html new file mode 100644 index 0000000..6c6e1bc --- /dev/null +++ b/themes/risotto/layouts/partials/about.html @@ -0,0 +1,15 @@ +{{ with .Site.Params.about }} +
    + {{ with .logo }}{{ end }} +

    {{ .title }}

    +{{ with .description }}

    {{ . | markdownify }}

    {{ end }} +
    +{{ end }} + + diff --git a/themes/risotto/layouts/partials/footer.html b/themes/risotto/layouts/partials/footer.html new file mode 100644 index 0000000..d3df4ff --- /dev/null +++ b/themes/risotto/layouts/partials/footer.html @@ -0,0 +1,2 @@ +{{- partial "lang.html" . -}} + diff --git a/themes/risotto/layouts/partials/head.html b/themes/risotto/layouts/partials/head.html new file mode 100644 index 0000000..d613bd8 --- /dev/null +++ b/themes/risotto/layouts/partials/head.html @@ -0,0 +1,36 @@ +{{ with .Title }}{{ . }} {{end}} +{{ with .Site.Params.about }}{{ end }} + + + +{{ if .Site.Params.noindex }} {{ end }} + + + + + + + + + + + + + + + + +{{ if os.FileExists "static/favicon.ico" }}{{ end }} +{{ if os.FileExists "static/favicon-32x32.png" }}{{ end }} +{{ if os.FileExists "static/favicon-16x16.png" }}{{ end }} +{{ if os.FileExists "static/apple-touch-icon.png" }}{{ end }} +{{ if os.FileExists "static/site.webmanifest" }}{{ end }} + +{{ range .AlternativeOutputFormats -}} + +{{ end -}} diff --git a/themes/risotto/layouts/partials/header.html b/themes/risotto/layouts/partials/header.html new file mode 100644 index 0000000..45c46ce --- /dev/null +++ b/themes/risotto/layouts/partials/header.html @@ -0,0 +1,10 @@ + + diff --git a/themes/risotto/layouts/partials/lang.html b/themes/risotto/layouts/partials/lang.html new file mode 100644 index 0000000..43c93d4 --- /dev/null +++ b/themes/risotto/layouts/partials/lang.html @@ -0,0 +1,28 @@ +

    + {{ $siteLanguages := .Site.Languages }} + {{ $pageLang := .Page.Lang }} + + {{ $currentPage := . }} + {{ $pageName := "" }} + {{ range .Site.Menus.main }} + {{ if eq ($currentPage.Permalink) (.URL | absLangURL) }} + {{ $pageName = .Name }} + {{ end }} + {{ end }} + + {{ range .Page.AllTranslations }} + {{ $translation := .}} + {{ range $siteLanguages }} + {{ if eq $translation.Lang .Lang }} + {{ $selected := false }} + {{ if eq $pageLang .Lang }} +
    $ echo $LANG
    {{ .LanguageName }}

    + + {{ else }} +
    export LANG={{ .LanguageName }}; ./{{ $pageName }}
    + {{ end }} + {{ end }} + {{ end }} + {{ end }} +

    +

    diff --git a/themes/risotto/layouts/post/list.html b/themes/risotto/layouts/post/list.html new file mode 100644 index 0000000..658b1eb --- /dev/null +++ b/themes/risotto/layouts/post/list.html @@ -0,0 +1,26 @@ +{{ define "main" }} +
    +

    {{ .Title | markdownify }}

    + {{ .Content }} +
    + + {{ range .Pages }} +
    +
    +

    {{ .Title | markdownify }}

    + +
    + +
    + {{ .Summary }} +
    +
    + {{ end }} +{{ end }} + +{{define "aside" }} + {{ if .Params.description }}

    {{ .Params.description }}

    {{ end }} +{{ end }} diff --git a/themes/risotto/static/css/about.css b/themes/risotto/static/css/about.css new file mode 100644 index 0000000..6c12ba4 --- /dev/null +++ b/themes/risotto/static/css/about.css @@ -0,0 +1,26 @@ +/* About/bio section */ +.about__logo { + height: 1.5rem; +} + +.about__title { + display: inline; + vertical-align: top; +} + +.about__title::before { + content: none; +} + +/* Social media links */ +.aside__social-links { + padding: 0; +} + +.aside__social-links li { + display: inline-block; +} + +.aside__social-links li::marker { + content: none; +} diff --git a/themes/risotto/static/css/colours.css b/themes/risotto/static/css/colours.css new file mode 100644 index 0000000..4326985 --- /dev/null +++ b/themes/risotto/static/css/colours.css @@ -0,0 +1,17 @@ +:root { + /* Background */ + --bg: var(--base00); + --off-bg: var(--base01); + --inner-bg: var(--base02); + + /* Text */ + --fg: var(--base05); + --off-fg: var(--base04); + --muted: var(--base03); + --link: var(--base0D); + --hover: var(--base0C); + --highlight: var(--base0A); + + /* Logo */ + --logo: var(--base0B); +} diff --git a/themes/risotto/static/css/custom.css b/themes/risotto/static/css/custom.css new file mode 100644 index 0000000..71949ed --- /dev/null +++ b/themes/risotto/static/css/custom.css @@ -0,0 +1 @@ +/* Override this file to customise the theme's CSS for your site */ diff --git a/themes/risotto/static/css/footer.css b/themes/risotto/static/css/footer.css new file mode 100644 index 0000000..3e84188 --- /dev/null +++ b/themes/risotto/static/css/footer.css @@ -0,0 +1,7 @@ +.page__footer { + color: var(--off-fg); +} + +.page__footer p { + margin: 0; +} diff --git a/themes/risotto/static/css/header.css b/themes/risotto/static/css/header.css new file mode 100644 index 0000000..cab735b --- /dev/null +++ b/themes/risotto/static/css/header.css @@ -0,0 +1,20 @@ +/* Main menu */ +.main-nav ul { + display: flex; + flex-flow: row wrap; + justify-content: flex-start; + margin: 0; + padding: 0 0 0.25rem 0; + gap: 0rem 1.5rem; +} + +.main-nav li { + padding-top: 0.25rem; + margin-left: 1rem; + text-transform: lowercase; +} + +.main-nav li::marker { + content: "./"; +} + diff --git a/themes/risotto/static/css/layout.css b/themes/risotto/static/css/layout.css new file mode 100644 index 0000000..d249166 --- /dev/null +++ b/themes/risotto/static/css/layout.css @@ -0,0 +1,57 @@ +/* 1rem = 16px by default */ + +.page { + max-width: 64rem; + margin: 1rem auto; + display: grid; + grid-template-areas: + "header" + "body" + "aside" + "footer"; + grid-template-columns: minmax(0, 1fr); /* https://css-tricks.com/preventing-a-grid-blowout/ */ + grid-row-gap: 2rem; +} + +@media (min-width: 45rem) { + .page { + grid-template-areas: + "header header" + "body aside" + "footer footer"; + grid-template-columns: minmax(0, 1fr) 15rem; + grid-column-gap: 2rem; + } +} + +/* Header */ +.page__header { + grid-area: header; + display: flex; +} + +.page__logo { + flex-shrink: 0; +} + +.page__nav { + flex-grow: 1; +} + +/* Body + aside */ +.page__body { + grid-area: body; + background-color: var(--off-bg); + box-shadow: 0 0 0 1rem var(--off-bg); + overflow-wrap: break-word; +} + +.page__aside { + grid-area: aside; + color: var(--off-fg); +} + +/* Footer */ +.page__footer { + grid-area: footer; +} diff --git a/themes/risotto/static/css/logo.css b/themes/risotto/static/css/logo.css new file mode 100644 index 0000000..ce296e3 --- /dev/null +++ b/themes/risotto/static/css/logo.css @@ -0,0 +1,37 @@ +.page__logo { + padding: 0; + margin: 0; + font-weight: inherit; + color: var(--bg); +} + +.page__logo:before { + content: none; +} + +.page__logo-inner { + display: block; + background: var(--logo); + opacity: 0.90; + padding: 0.25rem; +} + +a.page__logo-inner:link, a.page__logo-inner:visited { + color: inherit; + text-decoration: inherit; +} + +a.page__logo-inner:hover, +a.page__logo-inner:active { + opacity: 1; +} + +.page__logo-inner:before { + content: ""; + color: var(--bg); +} + +.page__logo-inner:after { + content: ":~#"; + color: var(--bg); +} diff --git a/themes/risotto/static/css/palettes/personal.css b/themes/risotto/static/css/palettes/personal.css new file mode 100644 index 0000000..7bff2fd --- /dev/null +++ b/themes/risotto/static/css/palettes/personal.css @@ -0,0 +1,20 @@ +/* Dracula by Mike Barkmin (http://github.com/mikebarkmin) based on Dracula Theme (http://github.com/dracula) */ + +:root { + --base00: #111313; + --base01: #212629; + --base02: #282424; + --base03: #986c9e; + --base04: #6c8b9e; + --base05: #c1c8c9; + --base06: #6b9e98; + --base07: #b8baba; + --base08: #9e6b71; + --base09: #424446; + --base0A: #cdcfce; + --base0B: #b290b6; + --base0C: #90b6b3; + --base0D: #90a7b6; + --base0E: #986b9e; + --base0F: #b5b18f; +} diff --git a/themes/risotto/static/css/risotto.css b/themes/risotto/static/css/risotto.css new file mode 100644 index 0000000..dcb5a96 --- /dev/null +++ b/themes/risotto/static/css/risotto.css @@ -0,0 +1,12 @@ +@import 'colours.css'; +@import 'typography.css'; +@import 'layout.css'; +@import 'header.css'; +@import 'logo.css'; +@import 'about.css'; +@import 'footer.css'; + +body { + background-color: var(--bg); + color: var(--fg); +} diff --git a/themes/risotto/static/css/syntax.css b/themes/risotto/static/css/syntax.css new file mode 100644 index 0000000..59074be --- /dev/null +++ b/themes/risotto/static/css/syntax.css @@ -0,0 +1,86 @@ +/* Background */ .bg { color: #fab387; background-color: #1e1e2e; } +/* PreWrapper */ .chroma { color: #fab387; background-color: #1e1e2e; } +/* Other */ .chroma .x { } +/* Error */ .chroma .err { color: #f38ba8 } +/* 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: #ffffcc } +/* 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: #7d5943 } +/* 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: #7d5943 } +/* Line */ .chroma .line { display: flex; } +/* Keyword */ .chroma .k { color: #cba6f7 } +/* KeywordConstant */ .chroma .kc { color: #cba6f7; font-style: italic } +/* KeywordDeclaration */ .chroma .kd { color: #cba6f7 } +/* KeywordNamespace */ .chroma .kn { color: #cba6f7 } +/* KeywordPseudo */ .chroma .kp { color: #cba6f7; font-weight: bold } +/* KeywordReserved */ .chroma .kr { color: #cba6f7 } +/* KeywordType */ .chroma .kt { color: #f9e2af } +/* Name */ .chroma .n { color: #b4befe } +/* NameAttribute */ .chroma .na { color: #f9e2af } +/* NameBuiltin */ .chroma .nb { font-style: italic } +/* NameBuiltinPseudo */ .chroma .bp { color: #b4befe } +/* NameClass */ .chroma .nc { color: #f9e2af } +/* NameConstant */ .chroma .no { color: #f9e2af } +/* NameDecorator */ .chroma .nd { color: #f5c2e7 } +/* NameEntity */ .chroma .ni { color: #f5c2e7 } +/* NameException */ .chroma .ne { color: #eba0ac } +/* NameFunction */ .chroma .nf { color: #89dceb } +/* NameFunctionMagic */ .chroma .fm { color: #b4befe } +/* NameLabel */ .chroma .nl { color: #f9e2af } +/* NameNamespace */ .chroma .nn { color: #f9e2af } +/* NameOther */ .chroma .nx { } +/* NameProperty */ .chroma .py { color: #b4befe } +/* NameTag */ .chroma .nt { color: #cba6f7 } +/* NameVariable */ .chroma .nv { } +/* NameVariableClass */ .chroma .vc { color: #b4befe } +/* NameVariableGlobal */ .chroma .vg { color: #b4befe } +/* NameVariableInstance */ .chroma .vi { color: #b4befe } +/* NameVariableMagic */ .chroma .vm { color: #b4befe } +/* Literal */ .chroma .l { } +/* LiteralDate */ .chroma .ld { } +/* LiteralString */ .chroma .s { color: #a6e3a1 } +/* LiteralStringAffix */ .chroma .sa { color: #a6e3a1 } +/* LiteralStringBacktick */ .chroma .sb { color: #a6e3a1 } +/* LiteralStringChar */ .chroma .sc { color: #a6e3a1 } +/* LiteralStringDelimiter */ .chroma .dl { color: #a6e3a1 } +/* LiteralStringDoc */ .chroma .sd { color: #a6e3a1 } +/* LiteralStringDouble */ .chroma .s2 { color: #a6e3a1 } +/* LiteralStringEscape */ .chroma .se { color: #89b4fa } +/* LiteralStringHeredoc */ .chroma .sh { color: #a6e3a1 } +/* LiteralStringInterpol */ .chroma .si { color: #a6e3a1 } +/* LiteralStringOther */ .chroma .sx { color: #a6e3a1 } +/* LiteralStringRegex */ .chroma .sr { color: #89b4fa } +/* LiteralStringSingle */ .chroma .s1 { color: #a6e3a1 } +/* LiteralStringSymbol */ .chroma .ss { color: #a6e3a1 } +/* LiteralNumber */ .chroma .m { } +/* LiteralNumberBin */ .chroma .mb { } +/* LiteralNumberFloat */ .chroma .mf { } +/* LiteralNumberHex */ .chroma .mh { } +/* LiteralNumberInteger */ .chroma .mi { } +/* LiteralNumberIntegerLong */ .chroma .il { } +/* LiteralNumberOct */ .chroma .mo { } +/* Operator */ .chroma .o { color: #89dceb } +/* OperatorWord */ .chroma .ow { color: #89dceb; font-weight: bold } +/* Punctuation */ .chroma .p { color: #cdd6f4 } +/* Comment */ .chroma .c { color: #585b70; font-style: italic } +/* CommentHashbang */ .chroma .ch { color: #585b70; font-style: italic } +/* CommentMultiline */ .chroma .cm { color: #585b70; font-style: italic } +/* CommentSingle */ .chroma .c1 { color: #585b70; font-style: italic } +/* CommentSpecial */ .chroma .cs { color: #585b70; font-style: italic } +/* CommentPreproc */ .chroma .cp { color: #89b4fa; font-style: italic } +/* CommentPreprocFile */ .chroma .cpf { color: #89b4fa; font-style: italic } +/* Generic */ .chroma .g { } +/* GenericDeleted */ .chroma .gd { color: #eba0ac } +/* GenericEmph */ .chroma .ge { font-style: italic } +/* GenericError */ .chroma .gr { color: #eba0ac } +/* GenericHeading */ .chroma .gh { color: #89dceb; font-weight: bold } +/* GenericInserted */ .chroma .gi { color: #a6e3a1 } +/* GenericOutput */ .chroma .go { } +/* GenericPrompt */ .chroma .gp { color: #6c7086; font-weight: bold } +/* GenericStrong */ .chroma .gs { font-weight: bold } +/* GenericSubheading */ .chroma .gu { color: #89dceb; font-weight: bold } +/* GenericTraceback */ .chroma .gt { color: #eba0ac } +/* GenericUnderline */ .chroma .gl { } +/* TextWhitespace */ .chroma .w { color: #313244 } diff --git a/themes/risotto/static/css/typography.css b/themes/risotto/static/css/typography.css new file mode 100644 index 0000000..fcf63b6 --- /dev/null +++ b/themes/risotto/static/css/typography.css @@ -0,0 +1,221 @@ +/* Fonts */ +:root { + --font-monospace: "Fira Mono", monospace; +} + +body { + font-family: var(--font-monospace); + font-size: 16px; + line-height: 1.5rem; +} + +/* Headings */ +h1, +h2, +h3, +h4, +h5, +h6 { + font-size: 1rem; + margin: 1.5rem 0 0 0; + font-weight: 600; +} + +h1+h2, +h1+h3, +h1+h4, +h1+h5, +h1+h6, +h2+h3, +h2+h4, +h2+h5, +h2+h6, +h3+h4, +h3+h5, +h3+h6, +h4+h5, +h4+h6, +h5+h6 { + margin: 0; +} + +h1:before { content: "# "; } +h2:before { content: "## "; } +h3:before { content: "### "; } +h4:before { content: "#### "; } +h5:before { content: "##### "; } +h6:before { content: "###### "; } + +h1:before, +h2:before, +h3:before, +h4:before, +h5:before, +h6:before { + color: var(--muted); +} + +h1:first-child { + margin-top: 0; +} + +/* Paragraphs */ +p { + margin: 0 0 1.5rem 0; +} + +/* Links */ + +a:link, a:visited { + color: var(--link); +} + +a:hover, a:active, a.active { + color: var(--hover); +} + +/* Lists */ +ul { + margin: 0 0 1.5rem 0; + padding-left: 1.25rem; +} + +ol { + margin: 0 0 1.5rem 0; + padding-left: 1.75rem; +} + +ul ul, +ul ol, +ol ul, +ol ol { + margin: 0; +} + +ul li::marker { + content: '∗\00A0'; + color: var(--muted); +} + +ol li::marker { + color: var(--muted); +} + +dt { + margin: 0; + font-weight: bold; +} + +dd { + margin: 0 0 0 1.5rem; + font-style: italic; +} + +dd + dt { + margin-top: 1.5rem; +} + +dl { + margin: 0 0 1.5rem 0; +} + +/* Blockquotes */ +blockquote { + position: relative; + margin: 0 0 1.5rem 1.5rem; +} + +blockquote::before { + position: absolute; + left: -1.5rem; + content: ">"; + color: var(--muted); +} + +.twitter-tweet::before { + content: "\f099"; + font-family: "Font Awesome 5 Brands"; + font-weight: 400; +} + +/* Code */ +pre, +code, +kbd, +samp { + background: var(--inner-bg) !important; + font-family: var(--font-monospace); + color: var(--off-fg); +} + +pre { + overflow-x: auto; + padding: 1.5rem; + margin: 0 0 1.5rem 0; +} + +/* Fix overflow when config markup.highlight.lineNos is true */ +/* See https://github.com/joeroe/risotto/issues/41 */ +.highlight div { + overflow-x: auto; +} + +/* Emphasis */ +b, +strong { + font-weight: 600; +} + +/* Highlighting */ +::selection, +mark { + background-color: var(--highlight); + color: var(--bg); +} + +/* Other typographic elements */ +hr { + border: 0; + margin-bottom: 1.5rem; +} + +hr:after { + content: '---'; + color: var(--muted); +} + + +/* Prevent super/sub from affecting line height */ +sup, sub { + vertical-align: baseline; + position: relative; + top: -0.25rem; + font-size: unset; +} +sub { + top: 0.25rem; +} + +/* Tables */ +table { + border-spacing: 0; + margin: 0 0 1.5rem 0; + overflow-wrap: anywhere; +} +th, td { + padding: 0 .75rem; + vertical-align: top; +} +th:first-child, td:first-child { + padding-left: 0; +} +th { + text-align: inherit; +} + +/* Figures */ +img { + max-width: 100%; + height: auto; +} +