commit 50b459cd6d822d87d5c9981389689e1fa235487e parent fa5aa8cb2b713e28c75e3c0ae2884c4515c4d08e Author: Michael Neuper <michael@michaelneuper.com> Date: Mon, 12 Oct 2026 15:55:23 +0200 transfer posts Diffstat:
50 files changed, 3259 insertions(+), 120 deletions(-)
diff --git a/README.md b/README.md @@ -0,0 +1,52 @@ +# michaelneuper.com + +My personal site built with [Astro](https://astro.build). + +## Running it + +You need Node 22.12 or newer. + +```sh +npm install +npm run dev +``` + +To check the version Netlify will deploy: + +```sh +npm run build # writes the site to dist/ +npm run preview # serves dist/ at http://localhost:4321 +``` + +## Where things are + +| Path | What it is | +|--------------------------------|------------------------------------------------------------------------------| +| `src/pages/index.astro` | the home page | +| `src/pages/posts/[slug].astro` | the page every post uses | +| `src/layouts/Base.astro` | head, top line, modeline and `*Help*`, shared by all pages | +| `src/content/posts/` | the posts, in Markdown | +| `src/scripts/` | the JavaScript: `main.js` for the home page, `page.js` for the others | +| `src/styles/style.css` | all the styles, including the themes | +| `public/` | files served as they are: fonts, favicon, GPG key, `robots.txt`, post videos | + +## Writing a post + +Add `src/content/posts/<slug>.md`, or `src/content/posts/<slug>/index.md` with its images next to it. It appears at `/posts/<slug>/` and in the posts list on the home page. + +```yaml +--- +title: "The title" +date: 2026-10-12T15:00:00+02:00 +tags: [emacs, linux] # colours are in src/scripts/tags.js +cat: sw # colour on the home page: hw, sw, math or sec +toc: true # show the *Ilist* outline of headings +draft: false # true keeps it off the site +--- +``` + +Maths goes between `$...$` or `$$...$$` and is rendered with KaTeX when the site is built. Videos and other files a post links to go in `public/posts/<slug>/`. + +## Deploying + +Netlify builds the site with the settings in `netlify.toml`: it runs `npm run build` and publishes `dist/`. diff --git a/astro.config.mjs b/astro.config.mjs @@ -1,10 +1,35 @@ // @ts-check import { defineConfig } from "astro/config"; +import { satteri } from "@astrojs/markdown-satteri"; +import katex from "katex"; + +// Renders $...$ and $$...$$ in posts with KaTeX at build time, so pages ship +// the maths as HTML and need no script for it. +const renderMath = { + name: "katex", + math(node, ctx) { + ctx.replaceNode(node, { type: "html", value: katex.renderToString(node.value, { displayMode: true, throwOnError: false }) }); + }, + inlineMath(node, ctx) { + ctx.replaceNode(node, { type: "html", value: katex.renderToString(node.value, { throwOnError: false }) }); + }, +}; export default defineConfig({ + site: "https://michaelneuper.com", // The default ("jsx") drops line breaks between text and inline elements, which // glues words to links. true only removes whitespace that doesn't render. compressHTML: true, // The toolbar would sit on top of the modeline and echo area. devToolbar: { enabled: false }, + markdown: { + processor: satteri({ features: { math: true }, mdastPlugins: [renderMath] }), + // Code takes its colours from the current Emacs theme (see .astro-code in style.css). + shikiConfig: { theme: "css-variables" }, + }, + // Pages from the Hugo site that now live on the home page. + redirects: { + "/posts": "/#posts", + "/pages/contact": "/#contact", + }, }); diff --git a/package-lock.json b/package-lock.json @@ -8,7 +8,10 @@ "name": "personal-site", "version": "0.0.1", "dependencies": { - "astro": "^7.3.8" + "@astrojs/markdown-satteri": "^0.4.3", + "@astrojs/rss": "^4.0.19", + "astro": "^7.3.8", + "katex": "^0.19.0" } }, "node_modules/@astrojs/compiler-binding": { @@ -258,6 +261,17 @@ "node": ">=22.12.0" } }, + "node_modules/@astrojs/rss": { + "version": "4.0.19", + "resolved": "https://registry.npmjs.org/@astrojs/rss/-/rss-4.0.19.tgz", + "integrity": "sha512-e+z5wYeYtffQdHQO8c2tkSd2JEBdAuRXJV4ZEU5IxkYeE6e39woDd7nw1PH1Kk2tEYNCYuKdylnnbhGmt61awA==", + "license": "MIT", + "dependencies": { + "fast-xml-parser": "^5.5.7", + "piccolore": "^0.1.3", + "zod": "^4.3.6" + } + }, "node_modules/@astrojs/telemetry": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/@astrojs/telemetry/-/telemetry-3.3.3.tgz", @@ -1131,6 +1145,18 @@ "@emnapi/runtime": "^1.7.1 || ^2.0.0-alpha.4" } }, + "node_modules/@nodable/entities": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@nodable/entities/-/entities-3.1.0.tgz", + "integrity": "sha512-LsS/DjHr+uDM647Gru/cA8+J3a3HfhttwCKLyuoyN7yXTFCxKBtSgMQBvrW9yNPa2/zDRUNuGPaH5QUFoa4arQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/nodable" + } + ], + "license": "MIT" + }, "node_modules/@oslojs/encoding": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@oslojs/encoding/-/encoding-1.1.0.tgz", @@ -1611,6 +1637,18 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/anynum": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/anynum/-/anynum-1.0.1.tgz", + "integrity": "sha512-N6//FLET/tXYNM/F6ABca1oH6fWB+KlTt909Le28WMDBk8oaT4vY17DCrwg2MvmuqUKt3Ni4N5dGJ/EoBgcO6A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT" + }, "node_modules/aria-query": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", @@ -2103,6 +2141,45 @@ "fast-string-width": "^3.0.2" } }, + "node_modules/fast-xml-builder": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.3.1.tgz", + "integrity": "sha512-pIM/1n3ntFXKYrUZwW7QCK0gAW7XY+wzj1YMIV3tLDvPj/V+zTGJK5e3/4WJfwj0qWw2ElNXiTixda/R+3YSug==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "dependencies": { + "path-expression-matcher": "^1.6.2", + "xml-naming": "^0.3.0" + } + }, + "node_modules/fast-xml-parser": { + "version": "5.11.2", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.11.2.tgz", + "integrity": "sha512-R9iDuNrQYeQut46cn2r2wHKn4HYzVDvDm5J1wW+koZewykv0yuO2HChTYeZtrULyHIDM9cj9TUXloCsueCQUog==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "dependencies": { + "@nodable/entities": "^3.0.1", + "fast-xml-builder": "^1.2.0", + "is-unsafe": "^2.0.0", + "path-expression-matcher": "^1.6.2", + "strnum": "^2.4.2", + "xml-naming": "^0.3.0" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, "node_modules/fdir": { "version": "6.5.0", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", @@ -2305,12 +2382,49 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-unsafe": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-unsafe/-/is-unsafe-2.0.2.tgz", + "integrity": "sha512-HgbIHPBH0KHHCcjLfGsCvhtPTVxjaAZlXjwdz7/GQC40SjSe4sfQsar8J5VFo8JOSbarkpV0OLG95bbaNd9aAQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT" + }, "node_modules/jsonc-parser": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", "license": "MIT" }, + "node_modules/katex": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/katex/-/katex-0.19.0.tgz", + "integrity": "sha512-v6Tznz3zJ7u3niRCoDTsumM2+HA2XXcCu+WAacCeHD2z3p9A9Ks987o5FzfTGBN0e8A0vjEgIDvLbICrXpdw/Q==", + "funding": [ + "https://opencollective.com/katex", + "https://github.com/sponsors/katex" + ], + "license": "MIT", + "dependencies": { + "commander": "^15.0.0" + }, + "bin": { + "katex": "cli.js" + } + }, + "node_modules/katex/node_modules/commander": { + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-15.0.0.tgz", + "integrity": "sha512-z67u4ZhzCL/Tydu1lJARtEZYWbWaN7oYLHbsuzocr6y4N6WZAagG3RQ4FW61V1/0+jImpj293XfrcYnd1qxtPg==", + "license": "MIT", + "engines": { + "node": ">=22.12.0" + } + }, "node_modules/lightningcss": { "version": "1.33.0", "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.33.0.tgz", @@ -2895,6 +3009,21 @@ "integrity": "sha512-zAMfbta9nPA8AKkA4WdmNAT8R8s6Dk0/xwPsqNnRidMFcLo1YkLuPDTgs2f64XdzS8vbLNnoJ+uUIkgbz7oRHA==", "license": "MIT" }, + "node_modules/path-expression-matcher": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.6.2.tgz", + "integrity": "sha512-enSlaiat05iasnzmgNxRj8reFdj3puY2QpNgP1aPIaVfT6nn9ICuPoFlKHk8EN22HcwewshO+mN2DGbkCEOtqQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/piccolore": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/piccolore/-/piccolore-0.1.3.tgz", @@ -3240,6 +3369,21 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/strnum": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.5.1.tgz", + "integrity": "sha512-UM4nTLWzQiDBseVxUGxN0wJzIk0UU89Qi7pKXz9qISo+lhviJikTuikBsK/oFRlpvYHvSdz0TVoLyQ+sEpYwxA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "dependencies": { + "anynum": "^1.0.1" + } + }, "node_modules/svgo": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/svgo/-/svgo-4.1.0.tgz", @@ -3689,6 +3833,21 @@ } } }, + "node_modules/xml-naming": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/xml-naming/-/xml-naming-0.3.0.tgz", + "integrity": "sha512-ghig2TBE/H11aOVgmahA3MhimvkBr6JIYknH/Dhdk10nXwdbIqBJsbfMxpvFPG8bAw77gN29aQWvKpmVoPlvPQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "engines": { + "node": ">=16.0.0" + } + }, "node_modules/xxhash-wasm": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/xxhash-wasm/-/xxhash-wasm-1.1.0.tgz", diff --git a/package.json b/package.json @@ -10,6 +10,9 @@ "astro": "astro" }, "dependencies": { - "astro": "^7.3.8" + "@astrojs/markdown-satteri": "^0.4.3", + "@astrojs/rss": "^4.0.19", + "astro": "^7.3.8", + "katex": "^0.19.0" } } diff --git a/public/posts/efficient-latex-editing-with-emacs/abbreviations.webm b/public/posts/efficient-latex-editing-with-emacs/abbreviations.webm Binary files differ. diff --git a/public/posts/efficient-latex-editing-with-emacs/accents.webm b/public/posts/efficient-latex-editing-with-emacs/accents.webm Binary files differ. diff --git a/public/posts/efficient-latex-editing-with-emacs/evil-tex.webm b/public/posts/efficient-latex-editing-with-emacs/evil-tex.webm Binary files differ. diff --git a/public/posts/efficient-latex-editing-with-emacs/gnu-calc.webm b/public/posts/efficient-latex-editing-with-emacs/gnu-calc.webm Binary files differ. diff --git a/public/posts/efficient-latex-editing-with-emacs/latexmk.webm b/public/posts/efficient-latex-editing-with-emacs/latexmk.webm Binary files differ. diff --git a/public/posts/efficient-latex-editing-with-emacs/math-symbols.webm b/public/posts/efficient-latex-editing-with-emacs/math-symbols.webm Binary files differ. diff --git a/public/posts/efficient-latex-editing-with-emacs/preview.webm b/public/posts/efficient-latex-editing-with-emacs/preview.webm Binary files differ. diff --git a/public/posts/efficient-latex-editing-with-emacs/snippets.webm b/public/posts/efficient-latex-editing-with-emacs/snippets.webm Binary files differ. diff --git a/public/posts/efficient-latex-editing-with-emacs/tex-folding.webm b/public/posts/efficient-latex-editing-with-emacs/tex-folding.webm Binary files differ. diff --git a/public/posts/efficient-latex-editing-with-emacs/xenops-paste.webm b/public/posts/efficient-latex-editing-with-emacs/xenops-paste.webm Binary files differ. diff --git a/public/posts/efficient-latex-editing-with-emacs/xenops-render.webm b/public/posts/efficient-latex-editing-with-emacs/xenops-render.webm Binary files differ. diff --git a/public/posts/modelling-population-growth-in-python/logistic.py b/public/posts/modelling-population-growth-in-python/logistic.py @@ -0,0 +1,57 @@ +import matplotlib.pyplot as plt +import numpy as np + +p_0 = 5 # initial population +r = 0.1 # intrinsic growth rate +K = 500 # carrying capacity +n = 100 # number of cycles + +def p_n(): + populations = [p_0] + + for i in range(n): + p_n = populations[i] + # Pn+1 = Pn + r * Pn * (1 – Pn / K) + p_n1 = p_n + r * p_n * (1 - p_n / K) + populations.append(p_n1) + + return populations + +def plot_p_n(): + x = list(range(n + 1)) + y = p_n() + + plt.figure(1) + plt.plot(x, y, 'o') + plt.xlabel('n') + plt.ylabel('Pn') + plt.title('Pn+1 = Pn + r * Pn * (1 - Pn / K)') + +def delta_p_p(P): + return r * (1 - P / K) + +def plot_delta_p_p(): + x = p_n() + y = [] + for i in range(1, n): + delta_p = (x[i + 1] - x[i - 1]) / 2 + y.append(delta_p / x[i]) + + del x[0] + del x[-1] + + x_reg = np.linspace(0, x[-1], 2) + y_reg = delta_p_p(x_reg) + + plt.figure(2) + plt.plot(x, y, 'o') + plt.plot(x_reg, y_reg) + plt.xlim(0, K) + plt.ylim(0, r) + plt.xlabel('Pn') + plt.ylabel('∆P/P') + plt.title('ΔP/P = r * (1 - P / K)') + +plot_p_n() +plot_delta_p_p() +plt.show() +\ No newline at end of file diff --git a/public/posts/modelling-population-growth-in-python/predator-prey.py b/public/posts/modelling-population-growth-in-python/predator-prey.py @@ -0,0 +1,66 @@ +import matplotlib.pyplot as plt +import numpy as np + +cycles = 3000 # number of cycles + +# Prey +R_0 = 50 # initial population +a = 0.04 # intrinsic growth rate +K = 1200 # carrying capacity +b = 0.003 # per-capita attack rate of predators on prey + +# Predators +F_0 = 20 # initial population +c = 0.01 # natural death rate +f = 0.02 + +############################################################## + +R_e = c / (b * f) +F_e = (a / b) * (1 - R_e / K) + +prey_population = [R_0] +predator_population = [F_0] +marker_size = 2 + +def R(n): + R_n = prey_population[n - 1] + F_n = predator_population[n - 1] + + return R_n + a * R_n * (1 - R_n / K) - b * R_n * F_n + +def F(n): + R_n = prey_population[n - 1] + F_n = predator_population[n - 1] + + return F_n + f * b * R_n * F_n - c * F_n + +def model(): + for i in range(1, cycles): + prey_population.append(R(i)) + predator_population.append(F(i)) + +def plot_predator_prey(): + plt.figure(1) + plt.axvline(x=R_e, color='red', linestyle='--') + plt.axhline(y=F_e, color='red', linestyle='--') + plt.plot(prey_population, predator_population, markersize=marker_size) + plt.xlabel('Prey Population') + plt.ylabel('Predator Population') + plt.title('Predator vs Prey Population') + +def plot_population_time(): + plt.figure(2) + plt.axhline(y=F_e, color='red', linestyle='--') + plt.axhline(y=R_e, color='red', linestyle='--') + plt.plot(np.arange(cycles), prey_population, label='Prey Population', markersize=marker_size) + plt.plot(np.arange(cycles), predator_population, label='Predator Population', markersize=marker_size) + plt.ylabel('Population') + plt.xlabel('Time') + plt.title('Predator and Prey Populations over Time') + plt.legend() + +model() +plot_predator_prey() +plot_population_time() +plt.show() +\ No newline at end of file diff --git a/src/content.config.ts b/src/content.config.ts @@ -0,0 +1,21 @@ +import { defineCollection } from "astro:content"; +import { glob } from "astro/loaders"; +import { z } from "astro/zod"; + +// Posts carried over from the Hugo site, at the same URLs (/posts/<slug>/). +// A post is either <slug>.md or <slug>/index.md with its images beside it. +const posts = defineCollection({ + loader: glob({ pattern: "**/*.md", base: "./src/content/posts" }), + schema: z.object({ + title: z.string(), + date: z.coerce.date(), + // Each tag gets its colour from TAG_COLORS in src/scripts/tags.js. + tags: z.array(z.string()).default([]), + // The legend colour on the home page: hw, sw, math or sec. + cat: z.enum(["hw", "sw", "math", "sec"]).optional(), + toc: z.boolean().default(false), + draft: z.boolean().default(false), + }), +}); + +export const collections = { posts }; diff --git a/src/content/posts/best-firefox-extensions.md b/src/content/posts/best-firefox-extensions.md @@ -0,0 +1,173 @@ +--- +title: "Best Firefox Extensions" +date: 2023-04-08T08:18:16+02:00 +tags: [productivity, privacy] +draft: true +--- + +Welcome to my comprehensive guide on the best Firefox extensions to supercharge your browsing experience! +As one of the most popular web browsers, Firefox offers a remarkable level of flexibility and customization through its vast extension ecosystem. +These extensions are like small add-ons that can enhance various aspects of your browsing, ranging from productivity and privacy to aesthetics and tailored experiences on platforms like YouTube and Reddit. + +## Productivity + +### [Tree Style Tab](https://addons.mozilla.org/en-US/firefox/addon/tree-style-tab/) +This extension provides the ability to work with tabs as "trees". + +New tabs opened from the current tab are automatically organized as "children" of the current tab. +Such "branches" are easily folded (collapsed) by clicking on the arrow shown in the "parent" tab, so you no longer need to suffer from too many visible tabs. +If you want, you can restructure the tree via drag and drop. + + + +### [Simple Tab Groups](https://addons.mozilla.org/en-US/firefox/addon/simple-tab-groups/) +Simple Tab Groups works across browser instances/windows too. +If you select a group in another window, the selected window will jump to the foreground with the chosen group selected. You can even select the specific tab within that group in background browser windows. + + + +### [Firefox Multi-Account Containers](https://addons.mozilla.org/en-US/firefox/addon/multi-account-containers/) +The Firefox Multi-Account Containers extension lets you carve out a separate box for each of your online lives – no more opening a different browser just to check your work email! + + + +### [DownThemAll!](https://addons.mozilla.org/en-US/firefox/addon/downthemall/) +DownThemAll will help you select, queue, sort and run your downloads faster. +It comes with advanced ways to select what links to download, and will remember your previous decisions so that you can queue more downloads with just One Click! + +Advanced users will also like using renaming masks, which let you automatically assign names (and even sub-folders within your downloads folder), fast-filtering or even custom, re-usable filters. + + + +### [SingleFile](https://addons.mozilla.org/en-US/firefox/addon/single-file/) +SingleFile is an add-on for Firefox Desktop and Mobile that helps you to *save an entire webpage** including images, styling, frames, fonts etc. *as a single HTML file**. + + + +### [To Google Translate](https://addons.mozilla.org/en-US/firefox/addon/to-google-translate/) +This extension creates a context menu item in Firefox. +When you click on this menu item, the previously selected text is sent to Google Translate for translating or to Google TTS for listening pronunciation. +You can setup the default languages for translation on the options page. + + + +## Privacy + +### [uBlock Origin](https://addons.mozilla.org/en-US/firefox/addon/ublock-origin/) +An efficient wide-spectrum content blocker. +Easy on CPU and memory. + + + +### [User-Agent Switcher and Manager](https://addons.mozilla.org/en-US/firefox/addon/user-agent-string-switcher/) +Spoof websites trying to gather information about your web navigation—like your browser type and operating system—to deliver distinct content you may not want. + + + +### [Privacy Badger](https://addons.mozilla.org/en-US/firefox/addon/privacy-badger17/) +Privacy Badger automatically learns to block invisible trackers. +Instead of keeping lists of what to block, Privacy Badger automatically discovers trackers based on their behavior. + + + +### [ClearURLs](https://addons.mozilla.org/en-US/firefox/addon/clearurls/) +This extension will automatically remove tracking elements from URLs to help protect your privacy when browsing through the Internet. + + + +### [LocalCDN](https://addons.mozilla.org/en-US/firefox/addon/localcdn-fork-of-decentraleyes/) +LocalCDN is a web browser extension that emulates Content Delivery Networks to improve your online privacy. +It intercepts traffic, finds supported resources locally, and injects them into the environment. +All of this happens automatically, so no prior configuration is required. + + + +### [Facebook Container](https://addons.mozilla.org/en-US/firefox/addon/facebook-container/) +Facebook Container works by isolating your Facebook identity into a separate container that makes it harder for Facebook to track your visits to other websites with third-party cookies. + + + +### [Google Container](https://addons.mozilla.org/en-US/firefox/addon/google-container/) +Google Container works by isolating your Google identity into a separate container that makes it harder for Google to track your visits to other websites with third-party cookies. + +It is a fork of the Facebook Container addon. + +## Quality of Life + +### [Auto Tab Discard](https://addons.mozilla.org/en-US/firefox/addon/auto-tab-discard/) +"Auto Tab Discard" is a lightweight browser extension that automatically reduces the amount of memory used by open but inactive tabs, helping to conserve battery life on portable devices by disabling JavaScript code and DOM events on discarded tabs. + + + +### [I don't care about cookies](https://addons.mozilla.org/en-US/firefox/addon/istilldontcareaboutcookies/) +The EU regulations require that any website using tracking cookies must get user's permission before installing them. +These warnings appear on most websites until the visitor agrees with the website's terms and conditions. +Imagine how irritating that becomes when you surf anonymously or if you delete cookies automatically every time you close the browser. + +This add-on will remove these cookie warnings from almost all websites! You can report any website which still warns you about cookies: just click 'Report a cookie warning' from the extension menu (the brown cookie image next to your address bar). + +In most cases, the add-on just blocks or hides cookie related pop-ups. +When it's needed for the website to work properly, it will automatically accept the cookie policy for you (sometimes it will accept all and sometimes only necessary cookie categories, depending on what's easier to do). +It doesn't delete cookies. + +## Aesthetics + +### [Dark Reader](https://addons.mozilla.org/en-US/firefox/addon/darkreader/) +This eye-care extension enables night mode creating dark themes for websites on the fly. +Dark Reader inverts bright colors making them high contrast and easy to read at night. + +You can adjust brightness, contrast, sepia filter, dark mode, font settings and ignore-list. + + + +## Social Platforms + +### YouTube + +#### [Enhancer for Youtube™](https://addons.mozilla.org/en-US/firefox/addon/enhancer-for-youtube/) +Built to get the most out of YouTube, this extension comes packed with all sorts of features that allow you, among other things, to manage ads as you wish, control the playback speed and the volume level with the mouse wheel, automate repetitive tasks such as selecting the appropriate playback quality, configure dozens of keyboard shortcuts to control YouTube like a pro, and much more... +Give it a try! +But be aware that you will never want to use YouTube without it again ;) + + + +#### [PocketTube](https://addons.mozilla.org/en-US/firefox/addon/youtube-subscription-groups/) +Using this simple extension you can create collections that seamlessly fit into YouTube layout. This is easy way to manage Youtube subscription / channels. + +This extension enables you to: +- Create group collections list on YouTube. +- Group YouTube subscription / channels to collections / group. One YouTube subscription / channel can has few collections +- Add custom icon for collection. Choice prepare icons or upload own one +- Show last YouTube videos by collections / group. Play all video by collections using YouTube playlist +- Auto sync your collections on Android/iOS/Firefox using Google drive and Chrome profile +- Integration in Youtube main page +- Filter video by category on Youtube feed subscriptions page +- Hide watched YouTube video +- Order by last YouTube activity, a-z, subscription count and custom +- Catch up your Youtube subscriptions + + + +#### [Return YouTube Dislike](https://addons.mozilla.org/en-US/firefox/addon/return-youtube-dislikes/) +Returns ability to see dislike statistics on youtube. + + + +#### [SponsorBlock](https://addons.mozilla.org/en-US/firefox/addon/sponsorblock/) +Easily skip YouTube video sponsors. +When you visit a YouTube video, the extension will check the database for reported sponsors and automatically skip known sponsors. +You can also report sponsors in videos. + + + +### Reddit + +#### [Reddit Enhancement Suite](https://addons.mozilla.org/en-US/firefox/addon/reddit-enhancement-suite/) +Reddit Enhancement Suite (RES) is a suite of tools to enhance your Reddit browsing experience. + + + +#### [Unreddit](https://addons.mozilla.org/en-US/firefox/addon/unreddit/) +Read removed Reddit comments. + + diff --git a/src/content/posts/buffer-overflow.md b/src/content/posts/buffer-overflow.md @@ -0,0 +1,83 @@ +--- +title: "Buffer Overflows: Keep Your Code Safe" +date: 2022-12-14T18:59:28+02:00 +tags: [security, c] +cat: sec +draft: false +--- + +A buffer overflow attack is a type of cyber attack in which an attacker attempts to write more data to a buffer (a temporary data storage area) in a computer's memory than the buffer is designed to hold. +This can cause the buffer to overflow, or exceed its maximum capacity, which can corrupt other parts of the computer's memory and allow the attacker to gain unauthorized access to the system. + +In this post we will explore more about how a buffer overflow attack works and how to prevent it. + +## Programming a Vulnerable Program + +Buffer overflows are a common security vulnerability in computer programs, especially those written in the C programming language. +In C, buffers are often used to store strings of characters, such as user input or file contents. +When a program receives input from a user or reads data from a file, it writes that data to a buffer. +If the amount of data is larger than the size of the buffer, the excess data will overflow into other parts of the computer's memory, potentially overwriting or corrupting other data. + +Lets write a basic program in C that is vulnerable to a buffer overflow attack. Load up your favourite text editor and create a file called `vuln.c`, then copy and paste the following: +``` c +#include <stdio.h> +#include <string.h> + +void vulnerable_function(char* input) { + char buffer[8]; + strcpy(buffer, input); + printf("The input is: %s\n", buffer); +} + +int main(int argc, char* argv[]) { + vulnerable_function(argv[1]); + + return 0; +} + +``` + +This program contains a function called `vulnerable_function()` that takes a string of characters as input. +The function declares a buffer called 'buffer' with a size of 8 characters. +It then uses the `strcpy()` function to copy the input string into the buffer. +Finally, it prints the contents of the buffer using the `printf()` function. + +To compile this code, we need a C compiler. +GCC (GNU Compiler Collection) is a fantastic C compiler and what I will use in this post. +<!-- , it is the standard C compiler for Unix-like systems like Linux and macOS. --> + +To compile the code and generate an executable file called `vuln`, you can run the following command: + +``` shell +gcc -o vuln vuln.c +``` + +To use gdb to debug the vuln program, you can use the following steps: + +1. Start gdb by running the `gdb` command. + +2. Load the vuln program into gdb by running the `file vuln` command. + +3. Run the program by running the `run` command. + +4. When the program stops at the beginning of the `main()` function, you can set a breakpoint at the beginning of the `vulnerable_function()` function by running the `break vulnerable_function` command. + +5. Run the program again by running the `run` command. This time, the program will stop at the breakpoint in the `vulnerable_function()` function. + +6. Step through the code line by line by running the `next` command. You can use the print command to print the values of variables and the `x` command to examine the contents of memory. + +7. When the program reaches the `strcpy()` function, you can see that the buffer overflows if the input string is longer than 8 characters. + +## Preventing Buffer Overflow Attacks + +To prevent buffer overflow attacks, it is important to carefully design and implement your code to avoid such vulnerabilities. Here are some best practices to follow: + +1. Use safe functions for handling strings and other data. In C, the `strncpy()` and `strncat()` functions are safer alternatives to `strcpy()` and `strcat()` because they allow you to specify the maximum number of characters to copy or concatenate. + +2. Avoid using `gets()`, which is a particularly dangerous function because it does not check the size of the input and can therefore easily cause a buffer overflow. Instead, use `fgets()` or `getline()`, which allow you to specify the maximum size of the input. + +3. Always check the return value of functions that handle strings or other data. If a function returns a value indicating that an error occurred or that the input was too large, you can take appropriate action, such as truncating the input or displaying an error message. + +4. Use secure coding practices, such as avoiding the use of hard-coded passwords, sanitizing user input to prevent SQL injection attacks, and using encryption to protect sensitive data. + +By following these best practices, you can help protect your code from buffer overflow attacks and keep your computer systems secure. diff --git a/src/content/posts/efficient-latex-editing-with-emacs/index.md b/src/content/posts/efficient-latex-editing-with-emacs/index.md @@ -0,0 +1,261 @@ +--- +title: "Efficient LaTeX Editing With Emacs" +date: 2023-11-15T13:26:32+02:00 +tags: [emacs, latex] +cat: math +toc: true +math: false +draft: false +--- + +The aim of this post is to show my customised LaTeX setup with Emacs and the packages I used to achieve it. +The configurations I'll be showing will be tailored to [Doom Emacs](https://github.com/doomemacs/doomemacs), as that's what I'm using. +However, all of this is possible in vanilla Emacs. +This guide assumes basic knowledge of both Emacs and LaTeX. +<!--more--> + +## AUCTeX + +[AUCTeX](https://www.gnu.org/software/auctex/) can be enabled by enabling `:lang latex` in your `doom` block. + +It has a lot of features, however, many of these features are replaced by other packages mentioned later in this guide. +Here are some of the notable features that I use: + +### Live Preview + +You can enable the live preview by typing <kbd>C-c C-c</kbd> and then selecting the `View` option. +This will open the file in your preferred pdf-viewer and automatically update when you recompile your document (more on compiling later in the guide). + +Since I prefer to stay inside emacs, I set my pdf viewer to `pdf-tools` by adding the following to my `config.org`: + +```emacs-lisp +(setq +latex-viewers '(pdf-tools)) +``` + +Here's what it looks like: + +<video autoplay loop muted playsinline width="100%"> + <source src="preview.webm" type="video/webm"> + <source src="preview.mp4" type="video/mp4"> + Your browser does not support the video tag or the file format of this video. +</video> + +### Pretty Symbols + +This is a minor mode that displays text like `\gamma` with their unicode equivalents (γ) in math mode. +The underlying text does not change, only the way they are displayed in the editor changes to make equations more readable. +It is enabled by default in Doom Emacs, otherwise you can run `M-x prettify-symbols-mode`. + +Here's what it looks like: + + + +You'll notice that the superscripts and subscripts are also displayed as such in the editor, this is also done by _AUCTeX_. +_AUCTeX_ can also automatically insert braces when you type `^` or `_`, this is done by setting the `TeX-electric-sub-and-superscript` variable, it is set to `t` by default in Doom Emacs. + +### Text Folding + +You can remove some of the visual noise in a LaTeX buffer by enabling `TeX-fold-mode`. +This is also enabled by default in Doom Emacs. + +This is easier to show than to explain: + +<video autoplay loop muted playsinline width="100%"> + <source src="tex-folding.webm" type="video/webm"> + <source src="tex-folding.mp4" type="video/mp4"> + Your browser does not support the video tag or the file format of this video. +</video> + +## CDLaTeX + +[CDLaTeX](https://github.com/cdominik/cdlatex) is a minor mode supporting fast insertion of environment templates and math stuff in LaTeX.[^1] + +[^1]: <https://github.com/cdominik/cdlatex/blob/master/README> + +You can enable this by adding the `+cdlatex` flag to the latex section of your `doom` block, like: + +```emacs-lisp +(latex +cdlatex) +``` + +_CDLaTeX_ also has a ton of features, but here are the ones I use the most: + +### Abbreviations + +Abbreviation expansion is triggered with <kbd>TAB</kbd>, for example: + +<video autoplay loop muted playsinline width="100%"> + <source src="abbreviations.webm" type="video/webm"> + <source src="abbreviations.mp4" type="video/mp4"> + Your browser does not support the video tag or the file format of this video. +</video> + +Use `M-x cdlatex-command-help` to see a full list of abbreviations. + +### Mathematical Symbols + +Backquote followed by any character inserts a LaTeX math macro into the buffer. +If necessary, a pair of `\(` is inserted to switch to math mode. +For example, typing `` `a`` inserts `\(\alpha\)`. + +You can wait a second after pressing `` ` `` and a list of all the available macros will pop up. +The macros can also be customised with the `cdlatex-math-symbol-alist` variable. + +<video autoplay loop muted playsinline width="100%"> + <source src="math-symbols.webm" type="video/webm"> + <source src="math-symbols.mp4" type="video/mp4"> + Your browser does not support the video tag or the file format of this video. +</video> + +### Accents and Fonts + +Putting accents on mathematical characters and/or changing the font of a character uses key combinations with the quote character `'` as a prefix. +The accent or font change is applied to the character or LaTeX macro **before** point. + +<video autoplay loop muted playsinline width="100%"> + <source src="accents.webm" type="video/webm"> + <source src="accents.mp4" type="video/mp4"> + Your browser does not support the video tag or the file format of this video. +</video> + +## LatexMk + +[Latexmk](https://ctan.org/pkg/latexmk) automates the process of compiling a LaTeX document. +Essentially, it is like a specialized relative of the general make utility, but one which determines dependencies automatically and has +some other very useful features.[^2] + +[^2]: https://mirror.ufs.ac.za/ctan/support/latexmk/latexmk.pdf + +You can enable it by adding the `+latexmk` flag to `latex` in your `doom` block. +So it should look something like this if you're using [cdlatex](#cdlatex) as well: + +```emacs-lisp +(latex +cdlatex +latexmk) +``` + +<!--TODO ass continuous build--> + +When you build your LaTeX project for the first time using _LatexMk_, press <kbd>C-c C-c</kbd> and select the `LatexMk` option. +After that, you can build it with `C-c C-a`. + +<video autoplay loop muted playsinline width="100%"> + <source src="latexmk.webm" type="video/webm"> + <source src="latexmk.mp4" type="video/mp4"> + Your browser does not support the video tag or the file format of this video. +</video> + +## LSP + +[LSP](https://github.com/Microsoft/language-server-protocol/ "Language Server Protocol") is a communication protocol used between an integrated development environment (IDE) or code editor and a language server that provides language-specific analysis, autocompletion, diagnostics, and other language-related features for programming. + +You can enable [lsp-mode](https://github.com/emacs-lsp/lsp-mode/) for Doom Emacs by enabling the `:tools lsp` module in your `doom` block. + +You can view a list of useful _lsp-mode_ features **[here](https://emacs-lsp.github.io/lsp-mode/tutorials/how-to-turn-off/)**. + +To enable lsp-mode for LaTeX, add the `+lsp` flag to `latex` in your `doom` block. +Your `doom` block will look like this by now: + +```emacs-lisp +(latex +cdlatex +latexmk +lsp) +``` + +Additionally, you'll also need a language server for LaTeX installed. +The two most popular ones are [TexLab](https://github.com/latex-lsp/texlab) and [Digestif](https://github.com/astoff/digestif). +The choice between the two ultimately comes down to personal preference, and you'll have to look at their features and decide for yourself which one you prefer. +I prefer _TexLab_, but it you want to use _Digestif_ instead, you can add the following line to your `config.el`: + +```emacs-lisp +(setq lsp-tex-server 'digestif) +``` + +## Xenops + +[Xenops](https://github.com/dandavison/xenops) is a LaTeX editing environment for mathematical documents in Emacs. + +You can install it by adding `(package! xenops)` to your `packages.el`. +To enable it in a `LaTeX` buffer, run `M-x xenops-mode`. +If you would like to enable it automatically in `LaTeX` buffers, add the following line to your `config.el`: + +```emacs-lisp +(add-hook LaTeX-mode-hook #'xenops-mode) +``` + +Here are some the notable features of Xenops: + +### Render Maths, Tables and TikZ Diagrams + +_Xenops_ replaces the `preview-latex` functionality in _AUCTeX_, which does not seem to support rendering images as SVGs. + +<video autoplay loop muted playsinline width="100%"> + <source src="xenops-render.webm" type="video/webm"> + <source src="xenops-render.mp4" type="video/mp4"> + Your browser does not support the video tag or the file format of this video. +</video> + +### Paste Images from the Clipboard + +Simply run `M-x xenops-image-handle-paste` to paste an image from the clipboard into your document. + +<video autoplay loop muted playsinline width="100%"> + <source src="xenops-paste.webm" type="video/webm"> + <source src="xenops-paste.mp4" type="video/mp4"> + Your browser does not support the video tag or the file format of this video. +</video> + +## YASnippet + +[YASnippet](https://github.com/joaotavora/yasnippet) is a template system for Emacs. +It allows you to type an abbreviation and automatically expand it into function templates when pressing <kbd>TAB</kbd>. + +You can install _YASnippet_ by enabling `:editor snippets` in your `doom` block. + +Some useful snippets can be found [here](https://github.com/doomemacs/snippets). + +<video autoplay loop muted playsinline width="100%"> + <source src="snippets.webm" type="video/webm"> + <source src="snippets.mp4" type="video/mp4"> + Your browser does not support the video tag or the file format of this video. +</video> + +Note that _[CDLaTeX](#cdlatex)_ also has snippet functionaly for LaTeX. +If the same snippet is defined in both _CDLaTeX_ and _YASnippet_, you'll probably want to use the _CDLaTeX_ snippet, so add the following line to your `config.el`: + +```emacs-lisp +(map! :map cdlatex-mode-map + :i "TAB" #'cdlatex-tab) +``` + +## GNU Calc + +[Calc](https://www.gnu.org/savannah-checkouts/gnu/emacs/manual/html_node/calc/index.html#SEC_Contents) is a calculator that's part of Emacs. +It can do symbolic differentiation, linear algebra, taylor series expansions, among many other things. + +You can start calc in embedded mode by running `M-x calc-embedded` and perform operations on LaTeX equations. +Here's an example of me finding the taylor series expansion of _e<sup>x</sup>_ to 6 terms: + +<video autoplay loop muted playsinline width="100%"> + <source src="gnu-calc.webm" type="video/webm"> + <source src="gnu-calc.mp4" type="video/mp4"> + Your browser does not support the video tag or the file format of this video. +</video> + +## evil-tex + +[evil-tex](https://github.com/iyefrat/evil-tex) provides a number of useful features for [evil](https://github.com/emacs-evil/evil) users. +This should also be installed by default in Doom Emacs. + +Here's an example of me using it: + +<video autoplay loop muted playsinline width="100%"> + <source src="evil-tex.webm" type="video/webm"> + <source src="evil-tex.mp4" type="video/mp4"> + Your browser does not support the video tag or the file format of this video. +</video> + +## Ackowledgement + +Thanks to these articles for inspiring this post: + +- <https://castel.dev/post/lecture-notes-1/> +- <https://karthinks.com/software/latex-input-for-impatient-scholars/> +- <https://ejenner.com/post/latex-emacs/> diff --git a/src/content/posts/efficient-latex-editing-with-emacs/symbols.png b/src/content/posts/efficient-latex-editing-with-emacs/symbols.png Binary files differ. diff --git a/src/content/posts/enable-monitor-mode-on-tp-link.md b/src/content/posts/enable-monitor-mode-on-tp-link.md @@ -0,0 +1,123 @@ +--- +title: "Enable Monitor Mode on TP-Link TL-WN722N" +date: 2022-12-16T13:07:05+02:00 +tags: [linux, security] +cat: sec +draft: false +--- + +## TP-Link TL-WN722N _v1 vs v2/v3_ + +The TP-Link TL-WN722N is a wireless network interface controller (NIC) that allows a computer to connect to a wireless network. +There are three versions of this NIC: v1, v2, and v3. While all three versions are similar in many ways, there are some notable differences between them. + +One of the main differences between the TP-Link TL-WN722N v1 and v2/v3 is the chipset they use. +- The v1 version uses a `Atheros AR9271` chipset +- The v2/v3 versions use a `Realtek RTL8188EUS` chipset + +The Atheros chip supports monitor mode and packet injection out of the box, but the Realtek chip requires changing the drivers. + +## Enabling Monitor Mode + +### v1 + +Run the following command: +``` shell +sudo airmon-ng start wlan0 +``` + +That's it, you should see that your card is in monitor mode when running the following: +``` shell +sudo iwconfig | grep "Mode" +``` + +### v2/v3 + +#### Upgrade your system + +On Debian-based systems (like Kali): +``` shell +sudo apt update && apt upgrade +``` + +On Arch-based systems (like Blackarch): +``` shell +sudo pacman -Syu +``` + +Then reboot: +``` shell +sudo reboot +``` + +#### Install headers + +Debian-based: +``` shell +sudo apt install linux-headers-$(uname -r) +``` + +Arch-based: +``` shell +sudo pacman -S install linux-headers-$(uname -r) +``` + +#### Change the kernel module + +Remove the previous driver module: +``` shell +sudo rmmod r8188eu.ko +``` + +Blacklist the old module: +``` shell +sudo echo "blacklist r8188eu" > "/etc/modprobe.d/realtek.conf" +``` + +Clone the new driver: +``` shell +git clone https://github.com/aircrack-ng/rtl8188eus +``` + +Install the new driver module: +``` shell +cd rtl8188eus +``` +``` shell +make +``` +``` shell +sudo make install +``` + +Enable the new module: +``` shell +sudo modprobe 8188eu +``` + +Reboot: +``` shell +sudo reboot +``` + +#### Enable monitor mode + +Kill all airmon-ng processes: +``` shell +sudo airmon-ng check kill +``` + +Set adapter to monitor mode: +``` shell +sudo iwconfig wlan0 mode monitor +``` + +Bring the interface up: +``` shell +sudo ifconfig wlan0 up +``` + +Test to see if packet injection works: +``` shell +sudo aireplay-ng --test wlan0 +``` diff --git a/src/content/posts/how-i-use-org-roam-to-takes-notes-for-cs/graph.png b/src/content/posts/how-i-use-org-roam-to-takes-notes-for-cs/graph.png Binary files differ. diff --git a/src/content/posts/how-i-use-org-roam-to-takes-notes-for-cs/index.md b/src/content/posts/how-i-use-org-roam-to-takes-notes-for-cs/index.md @@ -0,0 +1,251 @@ +--- +title: "How I Use Org-Roam to Take Notes for CS" +date: 2024-11-22T16:52:54+02:00 +tags: [emacs, org] +cat: sw +toc: true +math: false +draft: false +--- + +The aim of this post is to show how I use [Doom Emacs](https://github.com/doomemacs/doomemacs) +and [org-roam](https://www.orgroam.com/) to take notes for my Computer Science degree. +<!--more--> + +## Introduction + +This system has become an essential part of my workflow, and I hope sharing it might help others with +studies or gain new ideas for managing information. +There’s a comment section at the bottom of this post where you can share your questions or feedback. +You’ll need a GitHub account to sign in, but your comments could be valuable to others who come across this post. +If you’d rather not comment there, that’s fine too --- I appreciate your engagement regardless. + +<s> + +Some people have expressed interest in actually seeing my notes, so I've decided to +make [all of my notes](https://notes.michaelneuper.com/) publicly available for free. +However, please note that there are no images or attachments included due to copyright considerations +involving university materials, textbooks, and other online resources. +There also seems to be an issue where LaTeX is not displaying correctly. +I have opened [an issue on GitHub](https://github.com/ikoamu/publish-org-roam-ui/issues/34) about this +and am also working on trying to fix it myself. + +These notes are far from perfect and they almost certainly contain errors, inconsistencies, +or things I simply missed. +They are all stored on [this GitHub repo](https://github.com/michaelneuper/notes), +so feel free to submit a pull request if you spot any. +Collaboration is always welcome! + +</s> + +Unfortunately they had to be made private because of copyright concerns. + +## Why Org-Roam? + +### Integration with Emacs + +As someone deeply embedded in the emacs ecosystem, I rely on it for almost everything: +from writing code and managing tasks to organising my academic and personal life. +It’s my one-stop shop for productivity, and I find its flexibility unmatched. +Naturally, when it came to note-taking, I needed something that could integrate seamlessly into my +existing workflow without breaking the context-switching-free experience that emacs offers. + +### Org Mode + +Using org-roam means that I get all the benefits of org mode, more about this in [5](#code-and-latex-in-notes). + +### Plain Text + +One of my core requirements for a note-taking system is that my notes must be stored in plain text format. +Plain text is _timeless_, _portable_, and _future-proof_. +Unlike proprietary formats or specialised software, plain text notes can be read, edited, and processed +by almost any text editor or tool, so that my work remains accessible decades from now. + +## How I Structure my Notes + +At the heart of my system is a main index node, which serves as a centralised hub containing links to all my classes. +For example: +```org +* Year 1 +** Semester 1 +*** Class 1 +*** Class 2 +** Semester 2 +... +``` + +Each class has its own dedicated node, linked from the main index. +The structure within each class node depends on how the lecturer organises their material. +I tailor the hierarchy of my notes to match the specific course structure, so that I can easily +relate my notes to the lectures' notes/slides. +Here are two common examples of how I structure my class notes: + +### Week -> Lecture -> Topics + +Some professors organise their lecture notes/slides by week, with a separate pdf for each lecture. +So a typical class note might look like: +```org +* Week 1 +** Lecture 1 +*** Topic 1 +*** Topic 2 +** Lecture 2 +... +``` + +### Chapter -> Topics + +Some professors follow the structure of the textbook, with a pdf for every chapter. +So a class note might look like: +```org +* Chapter 1 +** Topic 1 +** Topic 2 +... +``` + +Topic nodes then link to other topic nodes, or other nodes which are not topic nodes (a node need not be connected to a class node). +Here is what a topic node might look like: + + + +## When I Take Notes + +I’ve found that taking notes during the lecture itself offers little benefit, at least for me. +During lectures, you’re often just scrambling to keep up with the lecturer, copying down what they’re writing without +really processing or understanding the material. +Since lecture notes are usually available afterward, this feels like a pointless exercise. + +Instead, I prefer to **listen attentively during the lecture** and focus on understanding the concepts being taught. +By concentrating on the explanations and examples, I can grasp the material more effectively without the distraction of +trying to transcribe everything in real-time. + +At the end of the day, once I’m home and have some quiet time, I sit down to make my own notes. +By then, I’ve already formed a basic understanding of the topic from the lecture. +I start by writing down what I remember from class, which helps reinforce the key points. +If I realise I’ve forgotten something or need clarification, I consult the textbook, lecture notes, or other resources to fill in the gaps. + +This approach has several benefits: +1. **Reinforcement of Knowledge**: Revisiting the material shortly after hearing it helps solidify what I learned during the lecture. + It’s like giving my brain a second chance to process the information. +2. **Preparation for Homework**: Writing the notes serves as a quick refresher on the day’s work, + which is particularly helpful when tackling homework or assignments based on the lecture content. +3. **Deeper Understanding**: By writing notes in my own words and structuring them in a way that makes sense to me, + I engage more actively with the material. + This process helps me gain a better understanding of the concepts than if I had simply copied the lecturer’s words verbatim. + +Ultimately, this method allows me to focus on comprehension during the lecture and use my note-taking time to consolidate +and deepen my understanding of the material. +It’s a system that not only works well for my learning style but also ensures that my notes are meaningful and useful for future review. + +## Code and LaTeX in Notes + +When it comes to incorporating code and LaTeX into my notes, org-roam combined with the functionality of org mode provides +everything I need for a seamless and powerful workflow. + +### LaTeX + +For LaTeX rendering, I use the [Xenops](https://github.com/dandavison/xenops) package instead of org mode’s default LaTeX preview system. +Here’s why: +1. **Asynchronous Rendering**: Xenops renders LaTeX fragments asynchronously, meaning I can continue writing without being interrupted by rendering delays. This is especially useful when working with complex equations that take longer to render. +2. **Caching for Efficiency**: Once a LaTeX fragment is rendered, Xenops caches the result. This means fragments that have already been rendered don’t need to be processed again, saving time and avoiding unnecessary CPU usage. + +For those interested in setting up and configuring LaTeX for emacs, I’ve written a [separate blog post](https://michaelneuper.com/posts/efficient-latex-editing-with-emacs/) that goes into greater detail about working with LaTeX in emacs, +most of it applies to using LaTeX in org mode as well. + +### Code + +Org mode’s native functionality makes it ideal for including and executing code directly within my notes. +Here’s how I leverage it: +1. **Code Blocks**: I use org mode’s code block syntax to include code snippets directly in my notes. + This allows me to document algorithms, explain concepts with examples, and even run the code inline when needed. + + ```python + #+begin_src python + def binary_search(arr, target): + left, right = 0, len(arr) - 1 + while left <= right: + mid = (left + right) // 2 + if arr[mid] == target: + return mid + elif arr[mid] < target: + left = mid + 1 + else: + right = mid - 1 + return -1 + #+end_src + ``` + +2. **Tangle Feature**: With org mode’s tangling functionality, I can export code snippets into standalone files. + This is useful for assignments or projects where I need to test or submit my code outside of emacs. + +3. **Syntax Highlighting and Evaluation**: org mode supports syntax highlighting for various programming languages and allows for inline execution, making it easy to test small snippets of code directly within my notes. + +The ability to use both code and LaTeX within org mode gives me a significant advantage when documenting complex topics. +For example, I can create a note on an algorithm like Dynamic Programming, where: +- A code block demonstrates the implementation. +- LaTeX fragments explain the mathematical formulation and recurrence relations. +- All of this is interconnected with links to related notes, such as "Time Complexity". + +## How I View my Notes + +When it comes to reviewing and navigating my notes, I’ve experimented with both [org-roam-ui](https://github.com/org-roam/org-roam-ui) (ORUI) and emacs itself. +Over time, my workflow has shifted toward using emacs almost exclusively, but ORUI was an important part of my note-viewing process for a long time. + +ORUI generates a pretty graph based on the links between nodes; here is my graph after one year: + + + +ORUI also generates previews of your notes, and this is what I initially used to review my notes. +The main reason was because of how slow LaTeX previews in org mode are, but xenops has fixed that issue. + +But ORUI also has limitations that made me gravitate toward emacs for reviewing notes: +- **No Search Functionality**: Unlike emacs, ORUI doesn’t offer a way to search through notes. + Instead, you have to find notes by clicking on nodes on the graph, which becomes impractical as the graph grows. +- **Improper Table Rendering**: Tables in Org mode didn’t render well in ORUI, which made it harder to use tables effectively. +- **No Code Highlighting:** As a computer science student, code snippets are a big part of my notes, and + ORUI doesn’t support syntax highlighting for code blocks. + +## Setting up Doom Emacs for Org-Roam + +For xenops to be able to render the mathematical symbols I need, I have to add the following LaTeX packages: + +```elisp +(after! org + (add-to-list 'org-latex-packages-alist '("" "amsmath" t)) + (add-to-list 'org-latex-packages-alist '("" "amssymb" t)) + (add-to-list 'org-latex-packages-alist '("" "mathtools" t)) + (add-to-list 'org-latex-packages-alist '("" "mathrsfs" t))) +``` + +I let xenops automatically render LaTeX fragments when opening an org file and set the scale factor according to my display: + +```elisp +(add-hook 'org-mode-hook #'xenops-mode) +(setq xenops-math-image-scale-factor 1.7 + xenops-reveal-on-entry t) +``` + +I use the following keybindings for Org-roam: + +```elisp +(map! :map evil-org-mode-map + :leader + (:prefix ("r") + :desc "Insert node" + "i" #'org-roam-node-insert + :desc "Find node" + "f" #'org-roam-node-find + :desc "Capture to node" + "c" #'org-roam-capture + :desc "Toggle roam buffer" + "b" #'org-roam-buffer-toggle + :desc "Open random note" + "r" #'org-roam-node-random + :desc "Visit node" + "v" #'org-roam-node-visit + :desc "Open ORUI" + "u" #'org-roam-ui-open)) +``` + +If you're interested in seeing my entire config, it is available in [this GitHub repo](https://github.com/michaelneuper/doom). diff --git a/src/content/posts/how-i-use-org-roam-to-takes-notes-for-cs/topic-node.png b/src/content/posts/how-i-use-org-roam-to-takes-notes-for-cs/topic-node.png Binary files differ. diff --git a/src/content/posts/how-to-create-your-own-website.md b/src/content/posts/how-to-create-your-own-website.md @@ -0,0 +1,122 @@ +--- +title: "How to Create Your Own Static Website" +date: 2022-12-03T19:39:46+02:00 +tags: [web] +cat: sw +draft: false +--- + +Setting up your own static website can be a great way to showcase your personal brand, portfolio, or business online. +In this post, we'll walk through the process of setting up a static website using Hugo as the static site generator and Netlify as the hosting service. + +## Building The Site + +First, let's talk about what a static website is and why it's a good choice for many people. +A static website is a website that is built using only HTML, CSS, and JavaScript files. +These files are served to the user's web browser as-is, without the need for any server-side processing. +This makes static websites fast, secure, and easy to maintain. + +Hugo is a popular open-source static site generator that makes it easy to create and manage a static website. +It has a simple, easy-to-use interface and comes with a wide range of customizable templates and themes. + +To get started with Hugo, you'll need to download and install Hugo on your computer. + +1. Once you have Hugo installed, you can create a new website by running the following command in your terminal: + ``` shell + hugo new site SITE-NAME + ``` + This will create a new Hugo project in the current directory. + +2. Next, install git and initialize the directory as a git repository: + ``` shell + git init + ``` + Initializing the directory as a git repository is a useful way to keep track of changes and it will also be needed later when we upload our site to GitHub. + +3. Download a theme for you website from https://themes.gohugo.io/. A hugo theme is a template that defines the look and feel of your site. Add a theme to your site by running the following command in the root directory of your site: + ``` shell + git submodule add https://github.com/THEME_NAME/ themes/ + ``` + This will create a folder in the `/themes/` directory where your theme is stored. + +3. Open the `config.toml` file and edit according to your theme. + +4. Create content for your website by using the `hugo new` command. e.g. + ``` shell + hugo new posts/my-first-post.md + ``` + This will create a markdown file under the `/content/posts/` directory where you can edit your content. Hugo uses Markdown files for content, which makes it easy to write and format your content using a simple, readable syntax. + +5. Run the following command in the directory of your site: + ``` shell + hugo server + ``` + Hugo will build a local version of your site that you can view by going to `http://localhost:1313/` inside your browser. Hugo will also automatically show any changes you make to the site, no need to rerun the command or refresh the page. + +## Hosting Your Site + +Hosting a static website refers to the process of making a website accessible to the internet by storing its files on a web server. +A static website is one that contains static content, meaning that the content does not change unless manually updated by the website owner. +This is in contrast to a dynamic website, which generates content on the fly using server-side programming languages such as PHP or ASP.NET. + +To host a static website, you need to sign up for a web hosting service that provides the necessary storage space and bandwidth to host your website. +There are many web hosting providers available, each offering different plans and pricing options. S +ome of the most popular options include *shared hosting*, *VPS hosting*, and *dedicated hosting*. + +One solution for hosting a static website is to use a platform like Netlify. +Netlify is a cloud-based service that allows you to easily deploy and host static websites for free. +It provides a simple interface for uploading your website files, and automatically takes care of the necessary tasks such as setting up DNS records and configuring a CDN to ensure fast loading times for your website. + +In addition to hosting your static website for free, Netlify also offers a range of advanced features such as continuous deployment, split testing, and form handling. +This makes it a great option for teams and individuals looking to quickly and easily host and manage their static websites. + +To host a Hugo site on Netlify, you need to follow these steps: + +1. Create a new repository on GitHub with the name of your site + +2. On you local machine, navigate to the root directory of you Hugo site and run the following commands to create a new repository and push it to GitHub: + + ``` shell + ## Add all files in the Hugo site to the Git repository + $ git add . + + ## Commit the changes to the repository + $ git commit -m "Initial commit of Hugo site" + + ## Add the GitHub repository as a remote to your local Git repository + $ git remote add origin <GitHub repository URL> + + ## Push the Hugo site to the GitHub repository + $ git push -u origin master + ``` + +3. Once your Hugo site has been pushed to GitHub, you can connect your GitHub account to Netlify by signing in to Netlify and following the instructions on the Netlify website. + +4. Once your GitHub account is connected to Netlify, you can set up Netlify to deploy your Hugo site by clicking the "New site from Git" button and selecting your Hugo site's repository from the list of available repositories. + +5. In the next step, you need to configure the build settings for your Hugo site. This includes specifying the version of Hugo to use, the command to build your site, and the directory where the built site should be stored. + +6. After configuring the build settings, you can click the "Deploy site" button to deploy your Hugo site to Netlify. Netlify will automatically build your site using Hugo and make it available at a unique URL. For now, you can access your site by visiting the URL provided by Netlify, but in the next section we will setup a custom domain. + +That's it! Your Hugo site is now hosted on Netlify and available to the world. + +## Getting a Custom Domain (Not Free) + +Setting up a custom domain for your website involves purchasing a domain name and configuring it to point to your website's files on the web server. +A domain name is the unique address that users can type in their web browser to access your website, such as www.google.com. + +Purchasing a domain name typically costs money, as domain names are registered on a yearly basis and need to be renewed periodically to maintain ownership. +The exact cost of a domain name can vary depending on the domain registrar and the specific top-level domain (TLD) you choose (e.g. .com, .net, .org, etc.). + +There are many domain registrars available where you can purchase a domain name, such as GoDaddy, Namecheap, and Google Domains. +When choosing a domain registrar, it's important to consider factors such as pricing, customer support, and additional services offered (such as privacy protection and email hosting). + +Since we're already hosting a site on Netlify we can just purchase the domain from them. +One advantage of purchasing a domain name from Netlify is that it makes the process of setting up your custom domain easier. +Netlify provides a simple interface for purchasing and configuring your domain, and automatically sets up the necessary DNS records to point your domain to your website on Netlify's servers. A +dditionally, Netlify offers free SSL certificates for custom domains, which helps to secure your website and improve its ranking on search engines. + + +In conclusion, building your own website is a rewarding and empowering experience that allows you to share your ideas, passions, and creations with the world. +By using a static site generator like Hugo, you can quickly and easily create a professional-looking website without needing to write any complex code. +Overall, building and hosting your own website is a great way to share your creativity and ideas with the world, and can be a fun and rewarding experience. diff --git a/src/content/posts/modelling-population-growth-in-python/Figure_1.png b/src/content/posts/modelling-population-growth-in-python/Figure_1.png Binary files differ. diff --git a/src/content/posts/modelling-population-growth-in-python/Figure_2.png b/src/content/posts/modelling-population-growth-in-python/Figure_2.png Binary files differ. diff --git a/src/content/posts/modelling-population-growth-in-python/Figure_3.png b/src/content/posts/modelling-population-growth-in-python/Figure_3.png Binary files differ. diff --git a/src/content/posts/modelling-population-growth-in-python/Figure_4.png b/src/content/posts/modelling-population-growth-in-python/Figure_4.png Binary files differ. diff --git a/src/content/posts/modelling-population-growth-in-python/index.md b/src/content/posts/modelling-population-growth-in-python/index.md @@ -0,0 +1,343 @@ +--- +title: "Modelling Population Growth in Python" +date: 2023-11-12T13:25:23+02:00 +tags: [python, maths] +cat: math +toc: true +math: true +draft: false +--- + +In this guide we will be modelling population growth using both the Logistic Model[^1] and the Lotka–Volterra (or Predator-Prey) Model[^2] in python and plotting it with `matplotlib`. +If you would just like the python code, you can download the code for the Logistic Model [here](logistic.py) and the code for the Lotka-Volterra Model [here](predator-prey.py). + +[^1]: Logistic function <https://en.wikipedia.org/wiki/Logistic_function> +[^2]: Lotka–Volterra equations <https://en.wikipedia.org/wiki/Lotka-Volterra_equations> +<!--more--> + +## Logistic Growth + +### The Logistic Equation + +The logistic equation is commonly expressed as a differential equation: + +$$ +\frac{\text{d}P}{\text{dt}} = rP\left(1 - \frac{P}{K}\right) +$$ + +However, for our purposes, we'll use the recurrence relation[^3] form: + +[^3]: Recurrence relation <https://en.wikipedia.org/wiki/Recurrence_relation> + +$$ +P_{n+1} = P_n + rP_n\left(1 - \frac{P_n}{K}\right) +$$ + +where: + +- $n$ is the cycle +- $P_n$ is the population after $n$ cycles +- $r$ is the intrinsic growth rate +- $K$ is the carrying capacity + +The recurrence relation is suitable for scenarios where population counts are measured at discrete intervals, aligning well with real-world data. +It is also easier to implement in Python. + +### Variables + +Let's set up the variables and import the necessary libraries in Python: + +```py +import matplotlib.pyplot as plt +import numpy as np + +p_0 = 5 # initial population +r = 0.1 # intrinsic growth rate +K = 500 # carrying capacity +n = 100 # number of cycles +``` + +Feel free to experiment with these variables to observe changes in the graphs. + +### Model the Population + +Now, let's create a Python function based on the logistic equation: + +```py +def p_n(): + populations = [p_0] + + for i in range(n): + p_n = populations[i] + p_n1 = p_n + r * p_n * (1 - p_n / K) + populations.append(p_n1) + + return populations +``` + +This function generates an array, `populations`, storing the population density after each cycle. + +### Plotting the Population vs Time Graph + +Utilising the `matplotlib` library, we can create a graph where $x$ represents each cycle (`n`) and $y$ represents the population density after $n$ cycles: + +```py +def plot_p_n(): + x = list(range(n + 1)) + y = p_n() + + plt.figure(1) + plt.plot(x, y, 'o') + plt.xlabel('n') + plt.ylabel('Pn') + plt.title('Pn+1 = Pn + r * Pn * (1 - Pn / K)') +``` + +This code produces a plot illustrating population growth over time. + + + +Note that the graph is not continuous, since we are plotting the graph over discrete time intervals ($n$). + +### Plotting the Change in Population vs Population Graph + +Next, we calculate the change in population after each cycle using the logistic equation: + +$$ +rP_n\left(1 - \frac{P_n}{K}\right) +$$ + +```py +def delta_p_p(P): + return r * (1 - P / K) +``` + +We then create a function to plot $\Delta P / P$ against $P$, including a regression line: + +```py +def plot_delta_p_p(): + x = p_n() + y = [] + for i in range(1, n): + delta_p = (x[i + 1] - x[i - 1]) / 2 + y.append(delta_p / x[i]) + + del x[0] + del x[-1] + + x_reg = np.linspace(0, x[-1], 2) + y_reg = delta_p_p(x_reg) + + plt.figure(2) + plt.plot(x, y, 'o') + plt.plot(x_reg, y_reg) + plt.xlim(0, K) + plt.ylim(0, r) + plt.xlabel('Pn') + plt.ylabel('∆P/P') + plt.title('∆P/P = r * (1 - P / K)') +``` + +This code generates a plot depicting the relationship between the change in population and the population size. + + + +### Showing the Plots + +Finally, we display our graphs by calling the functions: + +```py +plot_p_n() +plot_delta_p_p() +plt.show() +``` + +## Lotka–Volterra + +### The Lotka-Volterra Equations + +The Lotka-Volterra equations are also commonly expressed in their differential form: + +$$ +\frac{\text{d}x}{\text{d}t} = \alpha x - \beta xy +$$ + +$$ +\frac{\text{d}y}{\text{d}t} = \delta xy - \gamma y +$$ + +However, for the same reasons, I'll use their recurrence relation counterparts. +I'll also use $a$, $b$, $c$ and $f$ instead of $\alpha$, $\beta$, $\delta$ and $\gamma$. +Additionally, I'll use $R_n$ (for "rabbits") to represent the prey population, and $F_n$ (for "foxes") to represent the predator population. +This is more intuitive to use than $x$ and $y$ in my opinion. + +$$ +R_{n+1} = R_n + aR_n\left(1-\frac{R_n}{K}\right) - bR_nF_n +$$ + +$$ +F_{n+1} = F_n + fbR_nF_n - cF_n +$$ + +where: + +- $n$ is the cycle +- $R_n$ is the prey population after $n$ cycles +- $F_n$ is the predator population after $n$ cycles +- $a$ is the intrinsic growth rate of the prey +- $K$ is the carrying capacity of the prey +- $b$ is the rate at which the predators kill the prey +- $f$ is the effectiveness with which the predators can turn the prey into food +- $c$ is the natural per-capita death rate of the predators + +### Assumptions + +We'll assume that: +- The predators only die from natural causes + - i.e. the predators are not hunted by a bigger predator +- The prey only die from predators killing them + - i.e. the prey will be killed before dying of natural causes such as age + + +### Variables + +Let's implement these variables in Python, along with necessary libraries for graph plotting: + +```py +import matplotlib.pyplot as plt +import numpy as np + +cycles = 3000 # number of cycles + +# Prey +R_0 = 50 # initial population +a = 0.04 # intrinsic growth rate +K = 1200 # carrying capacity +b = 0.003 # per-capita attack rate of predators on prey + +# Predators +F_0 = 20 # initial population +c = 0.01 # natural death rate +f = 0.02 +``` + +Feel free to play around with these variables to see how the graphs change. + +Additionally, we'll initialise arrays for the prey populations and predators populations with the value of their initial populations. These are called `prey_population` and `predator_population`, respectively. + +We'll also create a variable called `marker_size`, which will be the size of the marker in our plots. This will make it easier to change the marker size of all our plots down the line. + +```py +prey_population = [R_0] +predator_population = [F_0] +marker_size = 2 +``` + +### Model the Prey Population + +Let's create a function to model the prey population according to the Lotka-Volterra equation: + +```py +def R(n): + R_n = prey_population[n - 1] + F_n = predator_population[n - 1] + + return R_n + a * R_n * (1 - R_n / K) - b * R_n * F_n +``` + +### Model the Predator Population + +Similarly, we model the predator population: + +```py +def F(n): + R_n = prey_population[n - 1] + F_n = predator_population[n - 1] + + return F_n + f * b * R_n * F_n - c * F_n +``` + +### Cycle Over the Populations + +A function to model both predator and prey populations over `n` cycles: + +```py +def model(): + for i in range(1, cycles): + prey_population.append(R(i)) + predator_population.append(F(i)) +``` + +### Getting the Equilibrium Populations + +To show how each of the populations tend toward equilibria on our graphs, we might want to define some variables for the equilibrium populations. + +```py +R_e = c / (b * f) +F_e = (a / b) * (1 - R_e / K) +``` + +where `R_e` is the equilibrium prey population and `F_e` is the equilibrium predator population + +We can derive these formulae by setting $R_{n+1}=R_n$ and $F_{n+1}=F_n$, which will yield the following: + +$$ +R_e = \frac{c}{bf} +$$ + +$$ +F_e = \frac{a}{b}\left(1 - \frac{R_e}{K}\right) +$$ + +### Plotting the Predator vs Prey Population Graph + +Using `matplotlib`, we plot the graph with their equilibria: + +```py +def plot_predator_prey(): + plt.figure(1) + plt.axvline(x=R_e, color='red', linestyle='--') + plt.axhline(y=F_e, color='red', linestyle='--') + plt.plot(prey_population, predator_population, markersize=marker_size) + plt.xlabel('Prey Population') + plt.ylabel('Predator Population') + plt.title('Predator vs Prey Population') +``` + +This produces a graph depicting the predator-prey interaction: + + + +As can be seen on the plot, the graph spirals toward the intersection of the equilibria. + +### Plotting the Populations vs Time Graph + +Plot the populations against time, including horizontal lines for equilibrium: + +```py +def plot_population_time(): + plt.figure(2) + plt.axhline(y=F_e, color='red', linestyle='--') + plt.axhline(y=R_e, color='red', linestyle='--') + plt.plot(np.arange(cycles), prey_population, label='Prey Population', markersize=marker_size) + plt.plot(np.arange(cycles), predator_population, label='Predator Population', markersize=marker_size) + plt.ylabel('Population') + plt.xlabel('Time') + plt.title('Predator and Prey Populations over Time') + plt.legend() +``` + +The resulting graph illustrates the populations approaching equilibrium: + + + +### Showing the Plots + +Call the functions to display the plots: + +```py +model() +plot_predator_prey() +plot_population_time() +plt.show() +``` diff --git a/src/content/posts/read-more.md b/src/content/posts/read-more.md @@ -0,0 +1,51 @@ +--- +title: "Unlock the Joy of Reading: Simple Strategies to Read More" +date: 2023-03-13T20:36:16+02:00 +tags: [reading, productivity] +draft: true +--- + +In today's fast-paced world, finding time to read can be challenging. +However, reading provides numerous benefits, such as boosting mental stimulation, reducing stress, and expanding our knowledge. +If you've been struggling to incorporate more reading into your life, don't worry – you're not alone. +In this blog post, we'll explore some easy and effective strategies to help you read more and enjoy the process. + +## Set Small, Achievable Goals + +When setting reading goals, it's crucial to avoid overwhelming yourself with unrealistic expectations. +Large goals, like reading for 30 minutes every day, can be challenging to maintain consistently. +Once we fail to meet such a goal, it can quickly spiral into a habit of not reading at all. +To counteract this, establish micro-goals that are almost impossible to miss, such as reading just one page a day. +This achievable target ensures you never fall into the habit of skipping your daily reading, and more often than not, you'll find yourself exceeding the goal and reading even more. By focusing on micro-goals, you build a sustainable reading habit that becomes second nature. + +## Focus on the Chapters That Intrigue You + +This tip is particularly relevant for non-fiction books, such as popular science titles, but can apply to other genres as well. +When diving into a book, you may find that only certain chapters capture your interest. +Instead of forcing yourself to read every single page, feel empowered to focus on the sections that truly engage you. +This approach maximizes your enjoyment and allows you to extract the most valuable information from the book. + +And yes, even if you haven't read every chapter, you can still claim that you've read the book. +After all, reading is a personal journey, and there's no rule stating that you must consume every word from cover to cover. +Embrace a tailored reading experience that caters to your interests and curiosity. + +## Embrace the Art of Moving On + +It's common to feel obliged to finish a book, especially if we've invested money in purchasing it. +This sense of obligation can sometimes lead us to stop reading altogether when we're struggling with an uninteresting book. +It's important to recognize this as the sunk cost fallacy – the misconception that we must continue an activity because of the resources we've already spent on it. + +To break free from this mindset, give yourself permission to move on from a book that isn't enjoyable. +Remember, reading is meant to be a pleasurable experience that enriches our lives, not a burdensome obligation. +By letting go of books that don't engage you, you create space for discovering new, captivating stories that reignite your passion for reading. +Don't let the sunk cost fallacy dictate your reading habits – embrace the freedom to explore new literary worlds without guilt. + +## Read Multiple Books at Once + +Our mood and energy levels can significantly influence our reading preferences. +Sometimes, we might be in the midst of a complex book, such as Kant's Critique of Pure Reason, which requires substantial thought and concentration. +However, after a long day at work or school, we might crave something lighter and more accessible. + +To ensure that your reading doesn't come to a halt due to mood fluctuations, keep a selection of books on hand that cater to different mental states and preferences. +This approach allows you to switch between books depending on your mood and energy levels, ensuring that you always have something enjoyable to read. +By maintaining a diverse reading list, you'll keep the momentum going and continue to reap the benefits of reading, no matter how you're feeling. diff --git a/src/content/posts/replace-jupyter-notebook-with-emacs-org-mode.md b/src/content/posts/replace-jupyter-notebook-with-emacs-org-mode.md @@ -0,0 +1,293 @@ +--- +title: "Replace Jupyter Notebook With Emacs Org Mode" +date: 2023-02-27T15:06:40+02:00 +tags: [emacs, org] +cat: sw +draft: false +--- + +## Introduction + +The popularity of Jupyter Notebook has surged in recent years as a powerful tool for interactive data analysis and visualization. +However, there is an alternative that has been around for a longer time and is proving to be a valuable resource for many programmers: Emacs Org Mode. +In this blog post, we will explore how you can replace Jupyter Notebook with Emacs Org Mode for a seamless and efficient literate programming experience. + +## What is Emacs Org Mode? + +Emacs Org Mode is an extension of the versatile Emacs text editor, offering a powerful platform for note-taking, project management, and literate programming. +Literate programming is an approach in which code and documentation are intertwined, allowing you to create executable documents that can be easily understood by others. +Org Mode excels at this by providing a simple and intuitive markup language that supports rich text formatting, embedded code, and exporting to various formats, such as HTML, PDF, and more. + +## Why Replace Jupyter Notebook with Emacs Org Mode? + +- Unified Environment: Emacs provides a unified environment for all your programming and text editing needs. With Org Mode, you can manage code, documentation, and project planning all within a single tool. This eliminates the need to switch between multiple applications, streamlining your workflow. + +- Extensibility: Emacs is known for its extensibility, and Org Mode is no exception. There are countless plugins and extensions available that can enhance your literate programming experience, such as `org-babel` for code execution, `org-ref` for bibliography management, and `org-present` for presentation support. + +- Version Control: Emacs integrates seamlessly with version control systems like Git, allowing you to track changes and collaborate with others easily. This is particularly useful for literate programming, as both code and documentation can be managed in the same repository. + +- Customization: Emacs and Org Mode can be tailored to suit your needs, from key bindings and appearance to functionality. This flexibility allows you to create a personalized environment that aligns with your preferences and workflow. + +## Getting Started with Emacs Org Mode for Literate Programming + +### Installing Emacs + +1. Choose your platform: + - Linux: Use your distribution's package manager to install Emacs (e.g., `sudo apt-get install emacs` for Ubuntu) + - macOS: Install Emacs using Homebrew (https://brew.sh/) with the command: `brew install --cask emacs` + - Windows: Download the installer from the official GNU Emacs website (https://www.gnu.org/software/emacs/) + +2. Launch Emacs and familiarize yourself with the interface. + +### Configuring Org Mode + +1. Ensure Org Mode is installed and up-to-date by checking your Emacs package list (`M-x list-packages`) or manually updating it. + +2. Add Org Mode configuration settings to your Emacs init file (usually `.emacs` or `init.el`). For example: + ```elisp + (require 'org) + (setq org-src-fontify-natively t) + (setq org-confirm-babel-evaluate nil) + ``` + +### Installing essential packages for data science + +Install the following packages using the built-in Emacs package manager (`M-x list-packages`): +- `org-babel`: For code block execution and literate programming +- `ess`: For R programming language support +- `elpy`: For Python programming language support + +Configure the installed packages by adding appropriate settings to your Emacs init file. + +### Setting up a Python environment in Emacs + +1. Ensure Python is installed on your system and available in your PATH. + +2. Install the ipython packages using pip: + ```shell + pip install ipython + ``` + +3. Configure Emacs to use IPython as the default Python interpreter by adding the following to your Emacs init file: + ```elisp + (setq python-shell-interpreter "ipython" + python-shell-interpreter-args "-i --simple-prompt") + ``` + +### Creating and organizing Org files for literate programming + +1. Create a new Org file with the extension `.org` (e.g., `my_project.org`). + +2. Learn the basics of Org Mode markup, such as headings, lists, and links. + +2. Write and execute code blocks within your Org file: + - Inline code execution: Use `src_<language>{<code>}` to insert inline code (e.g., `src_python{2 + 3}`). + - Code block execution: Use the following syntax to create a code block: + + ```org + #+BEGIN_SRC <language> + <code> + #+END_SRC + ``` +For example: + +```org +#+BEGIN_SRC python +print("Hello, Emacs Org Mode!") +#+END_SRC +``` + +- Execute code blocks using `C-c C-c` and display the results directly in your Org file. +- Export your Org file to various formats (e.g., HTML, PDF) for sharing and publishing. + +## Advanced Features of Emacs Org Mode for Data Science + +### Working with multiple languages in a single Org file + +Org Mode's support for various programming languages allows you to work with multiple languages in a single document. + +Configure additional languages in your Emacs init file by adding the appropriate org-babel settings, such as: + +```elisp +(org-babel-do-load-languages + 'org-babel-load-languages + '((python . t) + (R . t) + (shell . t) + ;; Add other languages here + )) +``` + +### Integrating with external tools and libraries + +Use Org Mode's `#+CALL` syntax to integrate external tools or libraries into your Org document. + +Example: Integrating Pandoc for document conversion: + +```org +#+NAME: pandoc +#+BEGIN_SRC sh :results output :var input="input.md" output="output.pdf" +pandoc $input -o $output +#+END_SRC + +#+CALL: pandoc(input="my_markdown_file.md", output="my_converted_file.pdf") +``` + +### Debugging and profiling code within Org Mode + +Leverage the debugging and profiling capabilities of your programming language within Org Mode. + +Example: Debugging Python code with pdb: + +```org +#+BEGIN_SRC python :results output +import pdb + +def my_function(a, b): + pdb.set_trace() + return a + b + +my_function(3, 5) +#+END_SRC +``` + +### Customizing code block execution and display options + +Customize code block execution with header arguments. + +Example: Exporting code block results as a separate file: + +```org +#+BEGIN_SRC python :results file :file output.png +import matplotlib.pyplot as plt + +x = [1, 2, 3, 4] +y = [2, 4, 6, 8] + +plt.plot(x, y) +plt.savefig('output.png') +#+END_SRC +``` + +### Utilizing Org Mode for project management and collaboration + +- Take advantage of Org Mode's task management and agenda features to manage data science projects. +- Create TODO items, set deadlines, and track progress using Org Mode. +- Collaborate with teammates by sharing Org files and using Git for version control. +- Example: Project management in Org Mode: + + ```org + * TODO Prepare dataset + DEADLINE: <2023-04-01> + * TODO Train machine learning model + DEADLINE: <2023-04-15> + * DONE Write report + CLOSED: [2023-03-25] + ``` + +## Tips and Tricks for Optimizing Your Emacs Org Mode Workflow + +### Keyboard shortcuts for Org Mode + +Learn and utilize Org Mode's keyboard shortcuts to improve your productivity: +- `C-c C-t`: Toggle TODO states +- `C-c C-s`: Schedule a task +- `C-c C-d`: Set a deadline +- `C-c C-o`: Open a link +- `TAB`: Fold/unfold headings +- `S-TAB`: Fold/unfold all headings + +Customize your own keyboard shortcuts in your Emacs init file, if needed. + +### Customizing your Emacs theme + +Choose from various built-in and community-provided Emacs themes to personalize your workspace. + +Install a new theme using the Emacs package manager (`M-x list-packages`), and activate it by adding the following to your Emacs init file: + +```elisp +(load-theme 'theme-name t) +``` +Adjust the appearance of Org Mode elements (e.g., headings, links, and code blocks) by customizing their faces. + +### Integrating with other Emacs packages for productivity + +Enhance your Org Mode experience by integrating it with other Emacs packages: +- `org-ref`: Manage citations and references within Org documents +- `org-brain`: Create a personal wiki or knowledge base using Org Mode +- `magit`: Integrate Git version control with your Org workflow +- `org-present`: Create presentations using Org Mode + +Install and configure these packages according to their respective documentation. + +### Using Org Mode for note-taking and knowledge management + +Use Org Mode as a powerful note-taking tool to complement your data science projects. + +Organize and search your notes using Org Mode's hierarchical structure and built-in search capabilities. + +Example: Organizing notes in Org Mode: + +```org +* Data Science Concepts +** Supervised Learning +*** Regression +- Linear Regression +- Polynomial Regression +*** Classification +- Logistic Regression +- Decision Trees +** Unsupervised Learning +*** Clustering +- K-means Clustering +- Hierarchical Clustering +``` +## Transitioning from Jupyter Notebook to Emacs Org Mode + +### Importing existing Jupyter Notebooks into Org Mode + +Use jupytext to convert Jupyter Notebooks to Org Mode files: +- Install jupytext with the command: `pip install jupytext` +- Convert your Jupyter Notebook (`.ipynb`) to an Org Mode file (`.org`) using the following command: + ```shell + jupytext --to org my_notebook.ipynb + ``` +-Review the converted Org Mode file to ensure proper formatting and correct any issues. + +### Exporting Org files to Jupyter Notebook format + +- Use jupytext to convert Org Mode files back to Jupyter Notebooks, if needed: +- Convert your Org Mode file (`.org`) to a Jupyter Notebook (`.ipynb`) using the following command: + ```shell + jupytext --to ipynb my_org_file.org + ``` +- Open the converted Jupyter Notebook with your preferred Jupyter environment (e.g., JupyterLab, Jupyter Notebook) and verify its contents. + +### Overcoming common challenges during the transition + +- Familiarize yourself with Emacs key bindings and commands, which may differ significantly from traditional text editors. +- Take the time to explore and customize your Emacs settings to create a comfortable and productive workspace. +- Seek help from the Emacs and Org Mode communities through online forums, mailing lists, and chat rooms. Many users are happy to share their experiences and offer support. + +## Conclusion + +### Recap of the benefits of using Emacs Org Mode for data science + +- Emacs Org Mode offers a powerful, flexible, and customizable environment for data science tasks, addressing many limitations of Jupyter Notebook. +- Org Mode's support for literate programming enables seamless integration of code, results, and documentation, promoting reproducible research and collaboration. +- The extensible nature of Emacs allows users to tailor their workflow to their specific needs, integrating various programming languages, tools, and productivity-enhancing packages. + +### Emphasizing the flexibility and customizability of Emacs Org Mode + +- One of the most significant advantages of Emacs Org Mode is its customizability, allowing users to create a personalized workspace that suits their needs and preferences. +- Users can choose from a wide variety of packages, themes, and settings to create an environment that enhances their productivity and enjoyment of the data science process. + +### Encouragement for readers to give Emacs Org Mode a try + +- If you're intrigued by the benefits of Emacs Org Mode for data science, consider giving it a try and experiencing its capabilities firsthand. +- Transitioning from Jupyter Notebook to Emacs Org Mode may require some initial effort, but the long-term gains in productivity and flexibility can be well worth the investment. + +### Invitation to join the Emacs Org Mode community and share experiences + +- As you embark on your journey with Emacs Org Mode, don't hesitate to reach out to the vibrant community of users and developers for help, support, and inspiration. +- Share your experiences, tips, and tricks with others, contributing to the collective knowledge and strengthening the community of Emacs Org Mode enthusiasts in the data science world. diff --git a/src/content/posts/stop-using-proprietary-software.md b/src/content/posts/stop-using-proprietary-software.md @@ -0,0 +1,49 @@ +--- +title: "Free Yourself from the Shackles of Proprietary Software: A Rant" +date: 2023-03-27T21:19:13+02:00 +tags: [open-source, software] +draft: true +--- + +## Introduction + +Have you ever found yourself frustrated by the limitations, restrictions, and lack of control you have when using proprietary software? +Welcome to the club. +So, let's dive into the many reasons why proprietary software is terrible and why we, as users and developers, should put our collective foot down and demand change. + +## The Greed of Corporations + +Proprietary software is the epitome of corporate greed, the embodiment of a money-hungry system that thrives on the limitations and frustrations it imposes upon users. +It's time to unshackle ourselves from the chains of expensive licensing fees, forced upgrades, and the never-ending cycle of subscription-based models that drain our wallets and limit our freedom to innovate. + +## Lack of Customization and Flexibility + +How many times have you used a piece of software and thought, "If only I could change this one feature or tweak this setting, it would be perfect?" Well, with proprietary software, that's just a pipe dream. +The rigidity of proprietary software stifles creativity and innovation, leaving you stuck with whatever features the software company deems necessary or, more likely, profitable. +Open-source software, on the other hand, empowers users and developers to modify, improve, and adapt the software to meet their unique needs. + +## The Inevitable Obsolescence + +Ever had a software program you loved, only to have it rendered useless when the company decided to discontinue support or force an unwanted upgrade? +It's frustrating, to say the least. +With proprietary software, you're completely at the mercy of the company's whims and decisions. +Open-source software, however, offers longevity and security, as it can be maintained and updated by a dedicated community of developers, even if the original creator moves on. + +## A Breeding Ground for Security Risks + +When it comes to proprietary software, you're essentially putting blind trust in the company to keep your data safe and protect you from security threats. +But we've all heard the horror stories of data breaches and hackers exploiting vulnerabilities in widely-used software. +Open-source software provides a level of transparency and peer review that can help identify and fix security issues faster and more efficiently. + +## Stifling Collaboration and Knowledge Sharing + +Proprietary software creates a culture of secrecy and competition, where companies guard their trade secrets and intellectual property like a dragon hoarding gold. +This prevents the sharing of knowledge and ideas that could lead to better software and technology for everyone. +Open-source software, conversely, fosters collaboration, community, and knowledge sharing that benefits the entire industry and its users. + +## Conclusion + +The time has come to break free from the limitations and frustrations of proprietary software. +We must recognize the value and potential of open-source alternatives and demand transparency, flexibility, and collaboration from the software industry. +By embracing the open-source movement, we can help create a world where software is not only more accessible and customizable but also more secure and innovative. +So, next time you're considering a software purchase or download, remember: there's a whole world of open-source alternatives out there, just waiting for you to explore. diff --git a/src/content/posts/turning-a-window-manager-into-a-desktop-environment.md b/src/content/posts/turning-a-window-manager-into-a-desktop-environment.md @@ -0,0 +1,228 @@ +--- +title: "A Guide to Building Your Own Desktop Environment" +date: 2022-12-11T21:31:27+02:00 +tags: [linux] +cat: sw +draft: false +--- + +<!-- TODO add notification daemon--> + +## Introduction + +If you're a Linux user, you're probably familiar with the concept of window managers and desktop environments. +These programs are responsible for managing the appearance and functionality of your desktop, providing the tools and features that you need to interact with your operating system. + +But what's the difference between a window manager and a desktop environment? +And why might someone want to turn a window manager into a desktop environment? + +In this blog post, we'll explore these questions and more. +I'll provide a step-by-step guide on how to turn a window manager into a desktop environment, along with tips and tricks for customizing your window manager and making it more user-friendly. +We'll also discuss the benefits and drawbacks of using a window manager as a desktop environment, and compare popular window managers and desktop environments. + +So let's dive in and learn more about the fascinating world of window managers and desktop environments! + +## Turning a window manager into a desktop environment + +If you want to turn a window manager into a desktop environment, the first thing you need to do is install the necessary components. +This typically includes the window manager itself, a display manager, and additional utilities and applications. + +Let's explore some of these components: + +### Window Manager + +A window manager is a program that manages the placement and appearance of windows on a computer's desktop. +It is responsible for providing the "frame" around each window, allowing the user to move, resize, and minimize windows. +A window manager also provides the ability to switch between windows and to tile them on the desktop in various ways. + +There are many different window managers available for Linux and other operating systems. +Some of the most popular ones include the following: + +1. **i3**: i3 is a tiling window manager that is designed to be fast and efficient. It is easy to configure and customize, and it provides a number of advanced features for power users. + +2. **Openbox**: Openbox is a lightweight and flexible window manager that is easy to customize. It provides a minimalistic interface and a wide range of options for advanced users. + +3. **bspwm**: bspwm is a tiling window manager that is based on binary space partitioning. It is lightweight and efficient, and it provides a number of advanced features, such as automatic tiling and support for multiple monitors. + +3. **dwm**: dwm is a dynamic window manager that is written in the C programming language. It is fast and lightweight, and it provides a number of advanced features, such as support for multiple monitors and a customizable status bar. + +4. **xmonad**: xmonad is a tiling window manager that is written in the Haskell programming language. It is highly configurable and customizable, and it provides a number of advanced features, such as support for multiple workspaces and automatic tiling. + +Each window manager has its own unique features and capabilities, and the right one for you will depend on your personal preferences and needs. +Some of the pros and cons of using different window managers are as follows: + +- **i3**: i3 is a powerful and customizable window manager that is well-suited for users who want a highly efficient and flexible desktop environment. It has a steep learning curve, but once you master it, you can achieve a high level of productivity. Some of the pros of using i3 are its tiling capabilities, its support for multiple workspaces, and its ability to run on low-end hardware. Some of the cons are its lack of a built-in panel or application launcher, and its reliance on keyboard shortcuts. + +- **Openbox**: Openbox is a lightweight and flexible window manager that is easy to customize. It provides a minimalistic interface and a wide range of options for advanced users. Some of the pros of using Openbox are its low resource usage, its support for desktop icons, and its ability to integrate with other desktop environments. Some of the cons are its lack of tiling capabilities, its reliance on external utilities for certain features, and its lack of a built-in system tray. + +- **bspwm**: bspwm is a tiling window manager that is based on binary space partitioning. It is lightweight and efficient, and it provides a number of advanced features, such as automatic tiling and support for multiple monitors. Some of the pros of using bspwm are its low resource usage, its advanced tiling capabilities, and its support for multiple monitors. Some of the cons are its lack of a built-in panel or application launcher, and its reliance on external utilities for certain features. + +- **dwm**: dwm is a dynamic window manager that is written in the C programming language. It is fast and lightweight, and it provides a number of advanced features, such as support for multiple monitors and a customizable status bar. Some of the pros of using dwm are its low resource usage, its fast performance, and its customizable status bar. Some of the cons are its lack of a built-in panel or application launcher, and its reliance on keyboard shortcuts. + +- **xmonad**: xmonad is a tiling window manager that is written in the Haskell programming language. It is highly configurable and customizable, and it provides a number of advanced features, such as support for multiple workspaces and automatic tiling. Some of the pros of using xmonad are its advanced tiling capabilities, its support for multiple workspaces, and its high level of configurability. Some of the cons are its steep learning curve, its lack of a built-in panel or application launcher, and its reliance on external utilities for certain features. + +### Display Manager + +A display manager is a program that manages the login and display of the desktop environment on a computer. +It is responsible for providing the login screen, allowing the user to enter their username and password, and then starting the desktop environment and managing its display. + +Some common examples of display managers include LightDM, GDM, and SDDM. +LightDM is a cross-platform display manager that is lightweight and customizable. +GDM is the default display manager on the GNOME desktop environment, and it provides a user-friendly login screen and a range of options for advanced users. +SDDM is the default display manager on the KDE desktop environment, and it provides a modern and attractive login screen. + +When you install a desktop environment on your computer, it typically includes a default display manager. +However, you can also choose to install and use a different display manager if you prefer. +For example, if you are using the Xfce desktop environment, you could choose to use LightDM instead of the default XDM display manager. + +### Compositor + +A compositor is a program that is responsible for combining the outputs of multiple applications and displaying them on the screen. +It is typically used to provide advanced graphical effects, such as transparency, animations, and 3D transformations. + +On Linux, a compositor is typically used in conjunction with a window manager to provide a desktop environment. +The window manager is responsible for managing the placement and appearance of windows on the desktop, while the compositor provides the graphical effects. +Some window managers, such as xmonad and i3, include a built-in compositor, while others, such as Openbox, dwm and bspwm, require the use of an external compositor. + +There are many compositors available for Linux, and the right one for you will depend on your personal preferences and needs. +Some popular compositors include compton, picom, and xcompmgr. Compton is a feature-rich compositor that provides a wide range of options and effects, picom is a lightweight and efficient compositor, and xcompmgr is a simple compositor that provides basic graphical effects. + +To choose a compositor, you can consider the following factors: + +1. **Features**: Different compositors provide different sets of features and capabilities. Consider which features are most important to you, and choose a compositor that provides them. + +2. **Performance**: Compositors can have a significant impact on the performance of your desktop environment. If you are using a low-end computer or you have a lot of applications running, you may want to choose a lightweight compositor that uses fewer resources. + +3. **Compatibility**: Some compositors are compatible with only certain window managers or desktop environments. If you are using a specific window manager or desktop environment, make sure the compositor you choose is compatible with it. + +### Additional Utilities + +#### Wallpaper Setter + +You can use a program such as nitrogen or feh to set your wallpaper. +Nitrogen and feh are two popular programs that allow you to browse and select an image to use as your wallpaper, and then set it as the background for your desktop. + +To use nitrogen or feh to set your wallpaper, follow these steps: + +1. Install nitrogen or feh on your system. You can use your package manager to install the program, or you can download and compile the source code from the program's website. + +2. Run nitrogen or feh. This will open a window that allows you to browse and select an image to use as your wallpaper. + +3. Browse to the location of the image you want to use as your wallpaper, and select it. + +4. Once you have selected the image, click on the "Set as wallpaper" button (in nitrogen) or the "Apply" button (in feh) to set the image as your wallpaper. + +5. The selected image will now be displayed as your wallpaper. + +6. Add a line to your startup script to set the wallpaper. +If you're using nitrogen add: + ```shell + nitrogen --restore + ``` + If you're using feh add: + ```shell + feh --bg-scale /path/to/image.jpg + ``` + +#### Bar + +A bar on Linux is a program that displays information on the desktop, such as the current time and date, the system load, and the status of various system services. +A bar is typically used in conjunction with a window manager or desktop environment, and it provides a convenient way to access important information without needing to open a separate application. + +There are many bars available for Linux, and the right one for you will depend on your personal preferences and the desktop environment you are using. +Some popular bars include polybar, lemonbar, i3bar, xmobar, and dwmblocks. +These bars are designed to be used with specific desktop environments or window managers, and they provide a range of features and customization options. + +For example, polybar and lemonbar are general-purpose bars that can be used with any desktop environment or window manager. +They provide a wide range of features, such as support for multiple monitors and custom colors and fonts, and they are highly customizable. +i3bar is the default bar for the i3 window manager, and it provides a minimalistic interface and support for displaying information from i3 modules. +xmobar is the default bar for the xmonad window manager, and it provides a customizable interface and support for displaying information from xmonad plugins. +dwmblocks is the default bar for the dwm window manager, and it provides a minimalist interface and support for displaying information from dwm status commands. + +To choose a bar, you can consider the following factors: + +1. **Compatibility**: Some bars are designed to be used with specific desktop environments or window managers. If you are using a specific desktop environment or window manager, make sure the bar you choose is compatible with it. + +2. **Features**: Different bars provide different sets of features and capabilities. Consider which features are most important to you, and choose a bar that provides them. + +3. **Customization**: Some bars are highly customizable, allowing you to change the appearance and behaviour of the bar to suit your preferences. If you want a bar that you can tailor to your specific needs, choose a bar that provides a high degree of customization. Polybar is a popular choice. + +#### Application Launcher + +An application launcher is a program that allows you to quickly search for and launch applications on your Linux system. +An application launcher typically provides a search bar or a menu that allows you to enter the name of the application you want to launch, and it displays a list of matching applications that you can select from. + +Some popular application launchers include rofi, dmenu, and albert. + +1. **Rofi** is a flexible and customizable application launcher that allows you to search for and launch applications, as well as perform a range of other actions, such as opening a terminal or running a command. + +3. **Dmenu** is a simple and lightweight application launcher that provides a minimalistic interface and support for fuzzy search. + +4. **Albert** is a fast and efficient application launcher that uses a customizable keyboard shortcut to launch applications and perform other actions. + +#### System Tray + +A system tray (also known as a notification area) is a part of the graphical user interface (GUI) on Linux (and other operating systems) that displays icons for system and application notifications. +The system tray is typically located at the bottom or side of the screen, and it provides a convenient way for applications to display important information and notifications without disrupting the user's work. + +For example, the system tray might display an icon for a network manager application, which shows the current network status and allows the user to manage their network connections. +It might also display an icon for a battery manager application, which shows the current battery level and allows the user to manage their power settings. + +#### Polkit + +Polkit (formerly known as PolicyKit) is a framework for defining and managing administrative policies on Linux. +It provides a mechanism for controlling access to system-wide privileges, and it allows users to perform privileged tasks without needing to log in as the root user. + +Polkit consists of a number of components, including a daemon (polkitd), a command-line tool (polkit), and a library (libpolkit). +The polkit daemon runs in the background and listens for requests from applications that need to perform privileged tasks. +The polkit command-line tool allows administrators to manage and configure polkit policies. +The libpolkit library provides a convenient interface for developers to integrate polkit into their applications. + +See [the arch wiki](https://wiki.archlinux.org/title/Polkit) for more information. + +## Customizing your window manager + +Most window managers on Linux allow for customization to some extent. +Customizing your window manager can allow you to tailor the appearance and behaviour of your desktop environment to suit your preferences and needs. + +To customize your window manager, you will need to edit the window manager's configuration files. +These files are typically written in a scripting language, such as Lua or Shell, and they control the various aspects of the window manager's behaviour and appearance. + +Here are some examples of the types of things you can customize in your window manager: + +- **Appearance**: You can customize the appearance of your window manager by changing the colors, fonts, and other visual elements. For example, you might change the color of the window borders, the font used for window titles, or the size of gaps between windows. + +- **Keybindings**: You can customize the keybindings of your window manager to define which keys perform which actions. For example, you might bind the "Alt + Tab" keys to switch between windows, or the "Super + L" keys to lock the screen. + +- **Behaviour**: You can customize the behaviour of your window manager to control how windows are managed and displayed. For example, you might change the default layout of windows on the screen, or the way windows are focused when they are opened. + +- **Autostart**: You can customize the autostart of your window manager to define which applications are automatically launched when you log in. For example, you might specify that your web browser, email client, and music player are automatically started when you log in. + +To customize your window manager, you will need to edit the appropriate configuration files and specify the desired changes. +The exact steps for doing this will depend on the specific window manager you are using. +You can find instructions for customizing your window manager in the documentation or online forums for your window manager. + +## Benefits and drawbacks + +There are both benefits and drawbacks to using a window manager instead of a full desktop environment on Linux. + +As said before, a window manager is a program that provides the basic functions for managing windows on the desktop, such as opening, closing, minimizing, and maximizing windows. +A full desktop environment, on the other hand, is a complete graphical user interface (GUI) that includes not only a window manager, but also a range of other applications and utilities, such as a taskbar, a system tray, a file manager, and a desktop background. + +Here are some of the benefits of using a window manager instead of a full desktop environment on Linux: + +- **Lightweight**: Window managers are typically much lighter and more efficient than full desktop environments. This means they use fewer system resources, such as memory and CPU, and they can run on lower-powered hardware. + +- **Customizable**: Window managers are typically highly customizable, allowing users to tailor the appearance and behavior of their desktop environment to their preferences. This is because window managers typically have simple, modular architectures, with configuration files that are easy to edit and modify. + +- **Flexible**: Window managers are typically more flexible than full desktop environments, allowing users to mix and match different applications and utilities from different desktop environments. For example, a user might use the window manager from one desktop environment, but the file manager from another. +Here are some of the drawbacks of using a window manager instead of a full desktop environment on Linux: + +- **Less user-friendly**: Window managers are typically less user-friendly than full desktop environments, because they do not include many of the applications and utilities that users expect in a GUI. This can make it more difficult for new users to get started with a window manager, and it can limit the functionality of the desktop environment. + +- **Less integrated**: Window managers are typically less integrated than full desktop environments, because they do not include many of the applications and utilities that are designed to work together. This can lead to a less cohesive and consistent user experience, and it can make it more difficult to perform certain tasks. + +- **More complex**: Window managers are typically more complex than full desktop environments, because they require the user to manually configure and manage many of the components of the desktop environment. This can be a challenge for inexperienced users, and it can require more time and effort to set up and maintain a window manager. + +Overall, whether to use a window manager or a full desktop environment on Linux depends on your personal preferences and needs. +Some users may prefer the lightweight, customizable, and flexible nature of window managers, while others may prefer the user-friendly, integrated, and simple nature of full desktop environments. diff --git a/src/content/posts/what-linux-packaging-format-to-use.md b/src/content/posts/what-linux-packaging-format-to-use.md @@ -0,0 +1,122 @@ +--- +title: "Which Linux Packaging Format Should You Use?" +date: 2023-01-09T13:36:52+02:00 +tags: [linux] +cat: sw +draft: false +--- + +## Introduction + +Linux, a widely popular open-source operating system, is known for its versatility and adaptability. +The software distribution in Linux is primarily done through packages, which are used to install, update, and remove applications. +There are several package formats available, and choosing the right one can be confusing for newcomers or even experienced users. +In this blog post, we will discuss the key differences between popular package formats such as Flatpak, Snap, Deb, RPM, AppImage, and AUR, and provide insights to help you choose the best format for your needs. + +## Flatpak + +Flatpak is a relatively new package format. +It aims to create a universal packaging system for all Linux distributions, providing a sandboxed environment for applications. +This isolation ensures that apps have limited access to system resources, enhancing security and stability. + +### Pros: + +- Distribution-agnostic packaging system +- Improved security through sandboxing +- Easy to update and manage applications + +### Cons: + +- Increased disk space usage due to bundling of dependencies +- Slower application startup times + +## Snap + +Snap is a package format developed by Canonical, the company behind Ubuntu. +Similar to Flatpak, it focuses on providing a universal packaging system that works across multiple distributions. +Snaps are self-contained and sandboxed, which improves security and stability. + +### Pros: + +- Works across various Linux distributions +- Enhanced security with sandboxing +- Automatic updates + +### Cons: + +- Limited support for themes and desktop integration +- Increased disk space usage due to bundled dependencies + +## Deb + +Deb is the native package format for Debian-based distributions, such as Ubuntu and Linux Mint. +It uses the Advanced Package Tool (APT) for package management, making it easy to install, update, and remove applications. +Deb packages have been the standard for Debian-based systems for many years. + +### Pros: + +- Well-established and widely supported +- Efficient dependency handling +- Large repository of available packages + +### Cons: + +- Limited to Debian-based distributions +- No sandboxing or containerization + +## RPM + +RPM (RPM Package Manager) is a package format used by Red Hat-based distributions, such as Fedora and CentOS. +RPM packages are managed by various package management tools like DNF and YUM. + +### Pros: + +- Extensive support for Red Hat-based distributions +- Good dependency management +- Large repository of available packages + +### Cons: + +- Limited to Red Hat-based distributions +- No sandboxing or containerization + +## AppImage + +AppImage is a portable package format that allows users to run applications without installing them. +It is distribution-agnostic and works across various Linux distributions. +AppImages are self-contained, bundling all required dependencies, which can lead to increased disk space usage. + +### Pros: + +- Distribution-agnostic +- Portable and easy to use +- No installation required + +### Cons: + +- Increased disk space usage +- No centralized update mechanism + +## AUR (Arch User Repository) + +AUR is a community-driven repository for Arch Linux and its derivatives, such as Manjaro. +It contains a vast collection of packages that are not available in the official repositories. +Users can build and install packages directly from the AUR using helper tools like yay or paru. + +### Pros: + +- Large collection of packages +- Community-driven and maintained +- Easy to use with helper tools + +### Cons: + +- Limited to Arch-based distributions +- Packages may have varying quality and stability + +## Conclusion + +Selecting the right package format depends on various factors, such as your distribution, security requirements, and personal preferences. +If you prioritize universal compatibility and sandboxing, Flatpak and Snap are great options. +For Debian or Red Hat-based distributions, Deb and RPM are well-established choices. +If you need portability and ease of use, AppImage is the way to go. Lastly, for Arch Linux users, AUR provides a vast array of community-maintained packages. diff --git a/src/layouts/Base.astro b/src/layouts/Base.astro @@ -0,0 +1,123 @@ +--- +// The Emacs frame every page shares: head, top line, modeline, minibuffer and +// the *Help* window. Pages fill the default slot with <main>, add page scripts +// to the "head" slot and their keys to the "help" slot. +import "../styles/style.css"; + +interface Props { + title?: string; + description?: string; + /** The home page links to its sections with #id; other pages need /#id. */ + home?: boolean; + /** Modeline buffer name and major mode before any section scrolls into view. */ + buf: string; + mode: string; + /** Where "Skip to content" goes. */ + skip: string; +} + +const { + title = "Michael Neuper", + description = "Computer science student at Stellenbosch University. Low-level software, electronics, a little security, and a lot of Emacs.", + home = false, + buf, + mode, + skip, +} = Astro.props; +const at = home ? "" : "/"; +--- +<!doctype html> +<html lang="en" data-theme="doom-gruvbox"> +<head> +<meta charset="utf-8"> +<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover"> +<title>{title}</title> +<meta name="description" content={description}> +<link rel="icon" href="/favicon.svg" type="image/svg+xml"> +<link rel="alternate" type="application/rss+xml" title="Michael Neuper's posts" href="/index.xml"> +<link rel="preload" href="/fonts/jetbrains-mono-regular.woff2" as="font" type="font/woff2" crossorigin> +<link rel="preload" href="/fonts/cm-regular.woff2" as="font" type="font/woff2" crossorigin> +{/* is:inline: Astro ships this script as written instead of bundling it, so it still runs before first paint. */} +<script is:inline> + // Pick the theme before first paint so there is no flash. Keep this list in sync with src/scripts/themes.js. + try { + var t = localStorage.getItem("theme"); + var known = /^(doom-gruvbox|doom-gruvbox-light|doom-one|atom-one-dark|modus-vivendi|modus-operandi|doom-dracula|doom-nord|catppuccin|doom-tokyo-night|doom-solarized-light)$/; + if (!known.test(t || "")) t = matchMedia("(prefers-color-scheme: light)").matches ? "doom-gruvbox-light" : ""; + if (t) document.documentElement.dataset.theme = t; + } catch (e) {} +</script> +<slot name="head" /> +</head> +<!-- Reading the source? Good instinct. flag{view_source_is_recon} --> +<body> +<a class="skip" href={skip}>Skip to content</a> + +<header class="top"> + <a class="brand" href={home ? "#top" : "/"} aria-label={home ? "Michael Neuper, back to top" : "Michael Neuper, home"}> + <svg class="logo" viewBox="0 0 32 16" aria-hidden="true"><path d="M1 14H3V2H9V14H11V2H17V14H20V2H26V14H31"/></svg> + <span>michael neuper</span> + </a> + <nav class="nav" id="nav" aria-label="Sections"> + <a href={`${at}#top`}>pinout</a> + <a href={`${at}#about`}>about</a> + <a href={`${at}#projects`}>projects</a> + <a href={`${at}#posts`}>posts</a> + <a href={`${at}#contact`}>contact</a> + </nav> + <button class="theme-key" type="button" title="SPC h t">load-theme</button> + <button class="menu-key" type="button" aria-expanded="false" aria-controls="nav">menu</button> +</header> + +<slot /> + +<!-- ───────────────────────── modeline and minibuffer ───────────────────────── --> +<div class="which-key" hidden aria-live="polite"></div> +<div class="frame-bottom"> + <div class="modeline"> + <span class="ml-evil" title="evil state"><N></span> + <span class="ml-buf">{buf}</span> + <span class="ml-pos" aria-hidden="true">1:0</span> + <span class="ml-pct" aria-hidden="true">Top</span> + <span class="ml-spacer"></span> + <span class="ml-misc" aria-live="polite"></span> + <span class="ml-flags" aria-hidden="true"></span> + <span class="ml-mode">{mode}</span> + <span class="ml-enc" aria-hidden="true">LF UTF-8</span> + <span class="ml-vc" aria-hidden="true">master</span> + <span class="ml-time" aria-hidden="true"></span> + </div> + <div class="mini" hidden> + <form class="mini-prompt" autocomplete="off"> + <label><span class="mini-count"></span><span class="mini-ps"></span><input type="text" spellcheck="false" autocapitalize="off" role="combobox" aria-expanded="true" aria-controls="mini-list" aria-autocomplete="list"></label> + </form> + <ul class="mini-list" id="mini-list" role="listbox"></ul> + </div> + <div class="echo" role="status"></div> +</div> + +<dialog class="help" aria-labelledby="help-title"> + <p class="winbar"><span class="file" id="help-title">*Help*</span><span class="rule" aria-hidden="true"></span><span class="meta">q or esc to close</span></p> + <div class="help-cols"> + <div> + <h3>Keys</h3> + <dl> + <div><dt><kbd>SPC</kbd></dt><dd>leader key: shows every command</dd></div> + <div><dt><kbd>M-x</kbd></dt><dd>run a command by name (Alt+x or Option+x)</dd></div> + <div><dt><kbd>SPC h t</kbd></dt><dd>load a theme, with live preview</dd></div> + <div><dt><kbd>:</kbd></dt><dd>evil ex commands, like <code>:q</code></dd></div> + <div><dt><kbd>C-g</kbd></dt><dd>cancel (so does <kbd>esc</kbd>)</dd></div> + </dl> + </div> + <div> + <h3>On this page</h3> + <dl> + <slot name="help" /> + <div><dt><code>submit-flag</code></dt><dd>check a CTF flag</dd></div> + </dl> + </div> + </div> + <form method="dialog"><button class="btn" type="submit">close</button></form> +</dialog> +</body> +</html> diff --git a/src/lib/feed.js b/src/lib/feed.js @@ -0,0 +1,20 @@ +// The RSS feed. The Hugo site served it at /index.xml and /posts/index.xml, +// so both of those URLs still return it for anyone subscribed. + +import rss from "@astrojs/rss"; +import { publishedPosts, summary } from "./posts.js"; + +export async function GET(context) { + const posts = await publishedPosts(); + return rss({ + title: "Michael Neuper", + description: "Posts by Michael Neuper on Emacs, Linux, maths and security.", + site: context.site, + items: posts.map((p) => ({ + title: p.data.title, + pubDate: p.data.date, + link: `/posts/${p.id}/`, + description: summary(p.body ?? ""), + })), + }); +} diff --git a/src/lib/posts.js b/src/lib/posts.js @@ -0,0 +1,44 @@ +// Shared by the home page's posts list, the post pages and the feeds. + +import { getCollection } from "astro:content"; + +/** Published posts, newest first. */ +export async function publishedPosts() { + const posts = await getCollection("posts", (p) => !p.data.draft); + return posts.sort((a, b) => b.data.date - a.data.date); +} + +// Dates are in South African time, as the posts were written. +const day = new Intl.DateTimeFormat("en-CA", { timeZone: "Africa/Johannesburg", year: "numeric", month: "2-digit", day: "2-digit" }); +const dow = new Intl.DateTimeFormat("en-GB", { timeZone: "Africa/Johannesburg", weekday: "short" }); +/** 2024-11-22 */ +export const ymd = (date) => day.format(date); +/** Fri */ +export const weekday = (date) => dow.format(date); + +/** Words in the Markdown source, about what wc -w says. */ +export const wordCount = (body) => body.split(/\s+/).filter(Boolean).length; + +/** + * The opening of a post as plain text, for meta descriptions and the feeds: + * everything above Hugo's <!--more--> marker, or else the first paragraph. + */ +export function summary(body, max = 220) { + const blocks = body.trim().split(/\n\s*\n/); + const head = body.includes("<!--more-->") + ? body.split("<!--more-->")[0] + : blocks.find((b) => !/^\s*([#!<>|`-]|\d+\.|\[\^)/.test(b)) ?? ""; + const text = head + .split("\n") + .filter((line) => !/^\[\^[^\]]+\]:/.test(line)) // footnote definitions + .join(" ") + .replace(/!\[[^\]]*\]\([^)]*\)/g, "") // images + .replace(/\[([^\]]+)\]\([^)]*\)/g, "$1") // links + .replace(/\[\^[^\]]+\]/g, "") // footnote references + .replace(/<[^>]+>/g, "") + .replace(/[`*_]/g, "") + .replace(/\s+/g, " ") + .trim(); + if (text.length <= max) return text; + return `${text.slice(0, text.lastIndexOf(" ", max)).replace(/[,.;:]$/, "")}…`; +} diff --git a/src/pages/404.astro b/src/pages/404.astro @@ -0,0 +1,20 @@ +--- +import Base from "../layouts/Base.astro"; +--- +<Base title="Not found - Michael Neuper" buf="*Messages*" mode="Messages" skip="#missing"> + <Fragment slot="head"> + <script src="../scripts/page.js"></script> + </Fragment> + + <main> + <section class="sec" id="missing" data-file="*Messages*" data-mode="Messages" aria-labelledby="missing-title"> + <p class="winbar"><span class="file">*Messages*</span><span class="rule" aria-hidden="true"></span><span class="meta">404</span></p> + <h1 class="h2" id="missing-title">No such file</h1> + <p class="lede">Nothing lives at this address. If it was a page on my old site, it didn't make the move, but every post did, at the same address as before.</p> + <p class="missing-links"><a href="/">Home</a> <a href="/#posts">Posts</a></p> + </section> + <footer class="foot"> + <p class="dim">Press <kbd>SPC</kbd> for commands or <kbd>M-x</kbd> to run one by name.</p> + </footer> + </main> +</Base> diff --git a/src/pages/index.astro b/src/pages/index.astro @@ -1,25 +1,12 @@ --- -import "../styles/style.css"; +import Base from "../layouts/Base.astro"; +import { publishedPosts, ymd } from "../lib/posts.js"; + +const posts = await publishedPosts(); --- -<!doctype html> -<html lang="en" data-theme="doom-gruvbox"> -<head> -<meta charset="utf-8"> -<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover"> -<title>Michael Neuper</title> -<meta name="description" content="Computer science student at Stellenbosch University. Low-level software, electronics, a little security, and a lot of Emacs."> -<link rel="icon" href="/favicon.svg" type="image/svg+xml"> -<link rel="preload" href="/fonts/jetbrains-mono-regular.woff2" as="font" type="font/woff2" crossorigin> -<link rel="preload" href="/fonts/cm-regular.woff2" as="font" type="font/woff2" crossorigin> -{/* is:inline: Astro ships this script as written instead of bundling it, so it still runs before first paint. */} +<Base home buf="sap-1.asm" mode="Asm" skip="#about"> +<Fragment slot="head"> <script is:inline> - // Pick the theme before first paint so there is no flash. Keep this list in sync with src/scripts/themes.js. - try { - var t = localStorage.getItem("theme"); - var known = /^(doom-gruvbox|doom-gruvbox-light|doom-one|atom-one-dark|modus-vivendi|modus-operandi|doom-dracula|doom-nord|catppuccin|doom-tokyo-night|doom-solarized-light)$/; - if (!known.test(t || "")) t = matchMedia("(prefers-color-scheme: light)").matches ? "doom-gruvbox-light" : ""; - if (t) document.documentElement.dataset.theme = t; - } catch (e) {} // Hide the name until src/scripts/decode.js has split it into letters, so it never flashes whole first. // CSS shows it anyway after a moment in case the script never runs. try { @@ -29,26 +16,13 @@ import "../styles/style.css"; } catch (e) {} </script> <script src="../scripts/main.js"></script> -</head> -<!-- Reading the source? Good instinct. flag{view_source_is_recon} --> -<body> -<a class="skip" href="#about">Skip to content</a> - -<header class="top"> - <a class="brand" href="#top" aria-label="Michael Neuper, back to top"> - <svg class="logo" viewBox="0 0 32 16" aria-hidden="true"><path d="M1 14H3V2H9V14H11V2H17V14H20V2H26V14H31"/></svg> - <span>michael neuper</span> - </a> - <nav class="nav" id="nav" aria-label="Sections"> - <a href="#top">pinout</a> - <a href="#about">about</a> - <a href="#projects">projects</a> - <a href="#posts">posts</a> - <a href="#contact">contact</a> - </nav> - <button class="theme-key" type="button" title="SPC h t">load-theme</button> - <button class="menu-key" type="button" aria-expanded="false" aria-controls="nav">menu</button> -</header> +</Fragment> +<Fragment slot="help"> + <div><dt><kbd>c</kbd> <kbd>n</kbd> <kbd>r</kbd></dt><dd>computer: run or pause, step, reset</dd></div> + <div><dt><kbd>TAB</kbd></dt><dd>fold the Org heading under focus</dd></div> + <div><dt><kbd>S-TAB</kbd></dt><dd>cycle the whole Org buffer</dd></div> + <div><dt><code>coffee-refill</code></dt><dd>top up VCC and overclock the computer</dd></div> +</Fragment> <main> @@ -227,17 +201,11 @@ yearly Vac Weeks.</div></div> <p class="winbar"><span class="file">posts.org</span><span class="rule" aria-hidden="true"></span><span class="meta" data-org="meta"></span></p> <p class="org-p">From my blog at michaelneuper.com, shown the way I read them. <kbd>TAB</kbd> on a heading folds it, <kbd>S-TAB</kbd> cycles the whole buffer, and clicking a heading’s stars does the same.</p> - <!-- Edit posts here. data-tags are space separated; each tag gets its own colour (see TAG_COLORS in src/scripts/org.js). --> + <!-- Built from the posts in src/content/posts. data-tags are space separated; each tag gets its own colour (see TAG_COLORS in src/scripts/tags.js). --> <ol class="posts"> - <li data-date="2024-11-22" data-tags="emacs org" data-cat="sw"><a href="https://michaelneuper.com/posts/how-i-use-org-roam-to-takes-notes-for-cs/">How I Use Org-Roam to Take Notes for CS</a></li> - <li data-date="2023-11-15" data-tags="emacs latex" data-cat="math"><a href="https://michaelneuper.com/posts/efficient-latex-editing-with-emacs/">Efficient LaTeX Editing With Emacs</a></li> - <li data-date="2023-11-12" data-tags="python maths" data-cat="math"><a href="https://michaelneuper.com/posts/modelling-population-growth-in-python/">Modelling Population Growth in Python</a></li> - <li data-date="2023-02-27" data-tags="emacs org" data-cat="sw"><a href="https://michaelneuper.com/posts/replace-jupyter-notebook-with-emacs-org-mode/">Replace Jupyter Notebook With Emacs Org Mode</a></li> - <li data-date="2023-01-09" data-tags="linux" data-cat="sw"><a href="https://michaelneuper.com/posts/what-linux-packaging-format-to-use/">Which Linux Packaging Format Should You Use?</a></li> - <li data-date="2022-12-16" data-tags="linux security" data-cat="sec"><a href="https://michaelneuper.com/posts/enable-monitor-mode-on-tp-link/">Enable Monitor Mode on TP-Link TL-WN722N</a></li> - <li data-date="2022-12-14" data-tags="security c" data-cat="sec"><a href="https://michaelneuper.com/posts/buffer-overflow/">Buffer Overflows: Keep Your Code Safe</a></li> - <li data-date="2022-12-11" data-tags="linux" data-cat="sw"><a href="https://michaelneuper.com/posts/turning-a-window-manager-into-a-desktop-environment/">A Guide to Building Your Own Desktop Environment</a></li> - <li data-date="2022-12-03" data-tags="web" data-cat="sw"><a href="https://michaelneuper.com/posts/how-to-create-your-own-website/">How to Create Your Own Static Website</a></li> + {posts.map((p) => ( + <li data-date={ymd(p.data.date)} data-tags={p.data.tags.join(" ")} data-cat={p.data.cat}><a href={`/posts/${p.id}/`}>{p.data.title}</a></li> + ))} </ol> </section> @@ -282,12 +250,12 @@ yearly Vac Weeks.</div></div> </section> <footer class="foot"> - <p>Made with HTML, CSS, JavaScript and Emacs, without a framework, a build step or cookies.</p> + <p>Made with Astro and Emacs, without cookies.</p> <p class="dim">Press <kbd>SPC</kbd> for commands or <kbd>M-x</kbd> to run one by name.</p> </footer> </main> -<!-- ───────────────────────── coffee, modeline and minibuffer ───────────────────────── --> +<!-- ───────────────────────── coffee ───────────────────────── --> <div class="coffee" hidden role="dialog" aria-label="Coffee refill"> <pre class="mug" aria-hidden="true"></pre> <div class="coffee-info"> @@ -298,55 +266,4 @@ yearly Vac Weeks.</div></div> </div> <button type="button" class="coffee-close" aria-label="Close">×</button> </div> -<div class="which-key" hidden aria-live="polite"></div> -<div class="frame-bottom"> - <div class="modeline"> - <span class="ml-evil" title="evil state"><N></span> - <span class="ml-buf">sap-1.asm</span> - <span class="ml-pos" aria-hidden="true">1:0</span> - <span class="ml-pct" aria-hidden="true">Top</span> - <span class="ml-spacer"></span> - <span class="ml-misc" aria-live="polite"></span> - <span class="ml-flags" aria-hidden="true"></span> - <span class="ml-mode">Asm</span> - <span class="ml-enc" aria-hidden="true">LF UTF-8</span> - <span class="ml-vc" aria-hidden="true">master</span> - <span class="ml-time" aria-hidden="true"></span> - </div> - <div class="mini" hidden> - <form class="mini-prompt" autocomplete="off"> - <label><span class="mini-count"></span><span class="mini-ps"></span><input type="text" spellcheck="false" autocapitalize="off" role="combobox" aria-expanded="true" aria-controls="mini-list" aria-autocomplete="list"></label> - </form> - <ul class="mini-list" id="mini-list" role="listbox"></ul> - </div> - <div class="echo" role="status"></div> -</div> - -<dialog class="help" aria-labelledby="help-title"> - <p class="winbar"><span class="file" id="help-title">*Help*</span><span class="rule" aria-hidden="true"></span><span class="meta">q or esc to close</span></p> - <div class="help-cols"> - <div> - <h3>Keys</h3> - <dl> - <div><dt><kbd>SPC</kbd></dt><dd>leader key: shows every command</dd></div> - <div><dt><kbd>M-x</kbd></dt><dd>run a command by name (Alt+x or Option+x)</dd></div> - <div><dt><kbd>SPC h t</kbd></dt><dd>load a theme, with live preview</dd></div> - <div><dt><kbd>:</kbd></dt><dd>evil ex commands, like <code>:q</code></dd></div> - <div><dt><kbd>C-g</kbd></dt><dd>cancel (so does <kbd>esc</kbd>)</dd></div> - </dl> - </div> - <div> - <h3>On this page</h3> - <dl> - <div><dt><kbd>c</kbd> <kbd>n</kbd> <kbd>r</kbd></dt><dd>computer: run or pause, step, reset</dd></div> - <div><dt><kbd>TAB</kbd></dt><dd>fold the Org heading under focus</dd></div> - <div><dt><kbd>S-TAB</kbd></dt><dd>cycle the whole Org buffer</dd></div> - <div><dt><code>coffee-refill</code></dt><dd>top up VCC and overclock the computer</dd></div> - <div><dt><code>submit-flag</code></dt><dd>check a CTF flag</dd></div> - </dl> - </div> - </div> - <form method="dialog"><button class="btn" type="submit">close</button></form> -</dialog> -</body> -</html> +</Base> diff --git a/src/pages/index.xml.js b/src/pages/index.xml.js @@ -0,0 +1 @@ +export { GET } from "../lib/feed.js"; diff --git a/src/pages/posts/[slug].astro b/src/pages/posts/[slug].astro @@ -0,0 +1,87 @@ +--- +// One post, shown as its Markdown buffer: front matter, then the text, with an +// imenu-list window of its headings beside it when the post asks for a table of +// contents, and the giscus comments from the old site below. +import { render } from "astro:content"; +import "katex/dist/katex.min.css"; +import Base from "../../layouts/Base.astro"; +import { publishedPosts, summary, wordCount, weekday, ymd } from "../../lib/posts.js"; +import { TAG_COLORS } from "../../scripts/tags.js"; + +export async function getStaticPaths() { + const posts = await publishedPosts(); + return posts.map((post, i) => ({ + params: { slug: post.id }, + props: { post, newer: posts[i - 1], older: posts[i + 1] }, + })); +} + +const { post, newer, older } = Astro.props; +const { Content, headings } = await render(post); +const { title, date, tags, toc } = post.data; +const file = `${post.id}.md`; +const words = wordCount(post.body ?? ""); +const minutes = Math.max(1, Math.round(words / 230)); +// (the footnotes' heading is only there for screen readers) +const outline = toc ? headings.filter((h) => (h.depth === 2 || h.depth === 3) && h.slug !== "footnote-label") : []; +--- +<Base title={`${title} - Michael Neuper`} description={summary(post.body ?? "")} buf={file} mode="Markdown" skip="#post"> + <Fragment slot="head"> + <script src="../../scripts/page.js"></script> + </Fragment> + <Fragment slot="help"> + <div><dt><code>imenu</code></dt><dd>jump to a heading in this post</dd></div> + <div><dt><kbd>SPC n</kbd></dt><dd>back to the list of posts</dd></div> + </Fragment> + + <main> + <section class="sec post" id="post" data-file={file} data-mode="Markdown" aria-labelledby="post-title"> + <p class="winbar"><span class="file">{file}</span><span class="rule" aria-hidden="true"></span><span class="meta">{words.toLocaleString("en")} words, {minutes} min</span></p> + + <div class="fm"> + <p class="fm-fence" aria-hidden="true">---</p> + <p><span class="fm-k">date:</span> <time class="fm-ts" datetime={ymd(date)}>{ymd(date)} {weekday(date)}</time></p> + {tags.length > 0 && ( + <p><span class="fm-k">tags:</span> [{tags.map((t, i) => ( + <Fragment>{i > 0 && ", "}<span class="t" style={`--tag: ${TAG_COLORS[t] ?? "var(--dim)"}`}>{t}</span></Fragment> + ))}]</p> + )} + <p class="fm-fence" aria-hidden="true">---</p> + </div> + + <h1 class="post-title" id="post-title">{title}</h1> + + <div class:list={["post-cols", { "has-toc": outline.length > 0 }]}> + {outline.length > 0 && ( + <aside class="post-toc" aria-labelledby="toc-title"> + <p class="winbar"><span class="file" id="toc-title">*Ilist*</span><span class="rule" aria-hidden="true"></span><span class="meta">contents</span></p> + <ol> + {outline.map((h) => ( + <li class={`d${h.depth}`}><a href={`#${h.slug}`}><span class="mk" aria-hidden="true">{"#".repeat(h.depth)}</span> {h.text}</a></li> + ))} + </ol> + </aside> + )} + <article class="post-body"> + <Content /> + </article> + </div> + + <nav class="post-nav" aria-label="More posts"> + {older ? <a class="older" href={`/posts/${older.id}/`}><span class="dim">older</span>{older.data.title}</a> : <span></span>} + {newer && <a class="newer" href={`/posts/${newer.id}/`}><span class="dim">newer</span>{newer.data.title}</a>} + </nav> + <p class="post-all"><a href="/#posts">All posts</a></p> + </section> + + <section class="sec post-comments" id="comments" data-file="*comments*" data-mode="giscus" aria-labelledby="comments-title"> + <p class="winbar"><span class="file" id="comments-title">*comments*</span><span class="rule" aria-hidden="true"></span><span class="meta">GitHub Discussions</span></p> + <div class="giscus"></div> + <p class="dim giscus-note">Comments load from GitHub Discussions through giscus.<noscript> They need JavaScript.</noscript></p> + </section> + + <footer class="foot"> + <p class="dim">Press <kbd>SPC</kbd> for commands or <kbd>M-x</kbd> to run one by name.</p> + </footer> + </main> +</Base> diff --git a/src/pages/posts/index.xml.js b/src/pages/posts/index.xml.js @@ -0,0 +1 @@ +export { GET } from "../../lib/feed.js"; diff --git a/src/scripts/org.js b/src/scripts/org.js @@ -4,22 +4,10 @@ import { $, $$ } from "./util.js"; import { message } from "./shell.js"; +import { TAG_COLORS } from "./tags.js"; const DAYS = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]; -// One colour per tag, like org-tag-faces. Add an entry when you add a tag. -const TAG_COLORS = { - emacs: "var(--magenta)", - org: "var(--green)", - latex: "var(--cyan)", - python: "var(--yellow)", - maths: "var(--blue)", - linux: "var(--orange)", - security: "var(--red)", - c: "var(--violet)", - web: "var(--fg)", -}; - export function initOrg() { const sec = $("#posts"); const src = sec && $(".posts", sec); diff --git a/src/scripts/page.js b/src/scripts/page.js @@ -0,0 +1,99 @@ +// Pages other than the home page get the same Emacs (modeline, M-x, themes, +// which-key), plus, on posts: imenu over the headings, the *Ilist* window +// following along, and the giscus comments the old site had. + +import { $, $$, on, themeColors, reducedMotion } from "./util.js"; +import { defcommand, completingRead } from "./shell.js"; +import { initCtf } from "./ctf.js"; + +// SPC a, p, n and m still go to the home page's sections. +for (const id of ["top", "about", "projects", "posts", "contact"]) { + defcommand(`goto-${id}`, () => location.assign(`/#${id}`), { hidden: true }); +} + +try { initCtf(); } catch (err) { console.error(err); } + +// ── imenu ───────────────────────────────────────────────────────────── + +const heads = $$(".post-body :is(h2, h3)[id]:not(.sr-only)"); +if (heads.length) { + defcommand("imenu", async () => { + const text = (h) => h.textContent.trim(); + const pick = await completingRead("imenu: ", heads.map((h) => ({ value: text(h), ann: h.tagName === "H3" ? "subsection" : "" }))); + const h = heads.find((x) => text(x) === pick); + if (!h) return; + h.scrollIntoView({ behavior: reducedMotion.matches ? "auto" : "smooth", block: "start" }); + history.replaceState(null, "", `#${h.id}`); + }, { doc: "Jump to a heading in this post", key: "SPC i" }); +} + +// The *Ilist* window marks the heading you're reading: the last one above a +// line 30% down the screen. A long list scrolls to keep that entry in view. +const toc = $(".post-toc"); +const tocLinks = toc ? $$("a", toc) : []; +if (tocLinks.length) { + let raf = 0; + const mark = () => { + raf = 0; + const line = innerHeight * 0.3; + let cur = null; + for (const h of heads) { + if (h.getBoundingClientRect().top > line) break; + cur = h; + } + for (const a of tocLinks) { + const on = !!cur && a.hash === `#${cur.id}`; + if (on && a.getAttribute("aria-current") !== "true" && toc.scrollHeight > toc.clientHeight) { + const y = a.offsetTop - toc.scrollTop; + if (y < 40 || y > toc.clientHeight - 40) toc.scrollTop = a.offsetTop - toc.clientHeight / 2; + } + a.setAttribute("aria-current", String(on)); + } + }; + addEventListener("scroll", () => { raf ||= requestAnimationFrame(mark); }, { passive: true }); + mark(); +} + +// ── comments ────────────────────────────────────────────────────────── +// Threads are GitHub Discussions matched by path, so the old site's comments +// still show up. The script only loads once you scroll near the comments. + +const GISCUS_THEMES = { + "doom-gruvbox": "gruvbox_dark", + "doom-gruvbox-light": "gruvbox_light", + catppuccin: "catppuccin_mocha", +}; +const giscusTheme = () => GISCUS_THEMES[document.documentElement.dataset.theme] + ?? (themeColors().light ? "noborder_light" : "transparent_dark"); + +const box = $(".giscus"); +if (box) { + const io = new IntersectionObserver(([e]) => { + if (!e.isIntersecting) return; + io.disconnect(); + const s = document.createElement("script"); + s.src = "https://giscus.app/client.js"; + s.async = true; + s.crossOrigin = "anonymous"; + Object.assign(s.dataset, { + repo: "michaelneuper/website", + repoId: "R_kgDOIjPNaQ", + category: "General", + categoryId: "DIC_kwDOIjPNac4CaklA", + mapping: "pathname", + strict: "0", + reactionsEnabled: "1", + emitMetadata: "0", + inputPosition: "top", + theme: giscusTheme(), + lang: "en", + loading: "lazy", + }); + box.append(s); + }, { rootMargin: "600px" }); + io.observe(box); + + on("theme", () => { + $("iframe.giscus-frame", box)?.contentWindow.postMessage({ giscus: { setConfig: { theme: giscusTheme() } } }, "https://giscus.app"); + }); +} diff --git a/src/scripts/shell.js b/src/scripts/shell.js @@ -373,6 +373,7 @@ const KEYMAP = [ { key: "p", desc: "projects", cmd: "goto-projects" }, { key: "n", desc: "posts", cmd: "goto-posts" }, { key: "m", desc: "mail me", cmd: "goto-contact" }, + { key: "i", desc: "imenu", cmd: "imenu" }, { key: "r", desc: "refill coffee", cmd: "coffee-refill" }, { key: "b", desc: "buy me a coffee", cmd: "buy-me-a-coffee" }, { key: "h", desc: "+help", map: [ @@ -392,8 +393,11 @@ const KEYMAP = [ let leader = null; // { map, prefix } let wkTimer = 0; +// Only the bindings whose command this page defines (posts have no computer). +const bound = (map) => map.filter((b) => (b.map ? bound(b.map).length : commands.has(b.cmd))); + function startLeader() { - leader = { map: KEYMAP, prefix: "SPC" }; + leader = { map: bound(KEYMAP), prefix: "SPC" }; message("SPC-", "", 0); clearTimeout(wkTimer); wkTimer = setTimeout(showWhichKey, 300); @@ -431,7 +435,7 @@ function press(key) { const b = leader.map.find((x) => x.key === key); if (!b) return endLeader(`${leader.prefix} ${key} is undefined`); if (b.map) { - leader = { map: b.map, prefix: `${leader.prefix} ${key}` }; + leader = { map: bound(b.map), prefix: `${leader.prefix} ${key}` }; message(`${leader.prefix}-`, "", 0); showWhichKey(); return; diff --git a/src/scripts/tags.js b/src/scripts/tags.js @@ -0,0 +1,13 @@ +// One colour per post tag, like org-tag-faces. The posts list on the home page +// (org.js) and the post pages both use it. Add an entry when you add a tag. +export const TAG_COLORS = { + emacs: "var(--magenta)", + org: "var(--green)", + latex: "var(--cyan)", + python: "var(--yellow)", + maths: "var(--blue)", + linux: "var(--orange)", + security: "var(--red)", + c: "var(--violet)", + web: "var(--fg)", +}; diff --git a/src/styles/style.css b/src/styles/style.css @@ -2247,6 +2247,344 @@ input[type="range"]::-moz-range-thumb { margin-top: 28px; } +/* ───────────────────────────── posts ───────────────────────────── + A post is its Markdown buffer: the front matter in mono, then the text in + the serif, with headings keeping their # markers the way markdown-mode + shows them. Code is coloured from the current theme through the variables + Shiki's css-variables theme reads. */ + +:root { + --astro-code-foreground: var(--fg); + --astro-code-background: var(--hl); + --astro-code-token-comment: var(--dim); + --astro-code-token-keyword: var(--magenta); + --astro-code-token-string: var(--green); + --astro-code-token-string-expression: var(--green); + --astro-code-token-constant: var(--orange); + --astro-code-token-function: var(--blue); + --astro-code-token-parameter: var(--fg); + --astro-code-token-punctuation: var(--dim); + --astro-code-token-link: var(--cyan); +} + +.post { + padding-top: 112px; +} + +/* post file names are long; on a phone they're cut short rather than wrapped */ +.post > .winbar .file { + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.post > .winbar .meta { + white-space: nowrap; +} + +.fm { + margin: 0 0 32px; + color: var(--faint); + font-size: 13px; + line-height: 1.7; +} + +.fm p { + margin: 0; +} + +.fm-k { + color: var(--blue); +} + +.fm-ts { + color: var(--cyan); +} + +.fm .t { + color: var(--tag, var(--dim)); +} + +.post-title { + max-width: 18em; + margin: 0 0 56px; + font: 400 clamp(34px, 4.8vw, 54px)/1.08 var(--serif); + letter-spacing: -0.01em; + text-wrap: balance; +} + +.post-body { + max-width: 680px; + font: 400 19px/1.6 var(--serif); +} + +.post-body > :first-child { + margin-top: 0; +} + +.post-body p, +.post-body ul, +.post-body ol, +.post-body dl { + margin: 0 0 1em; +} + +.post-body ul, +.post-body ol { + padding-left: 1.3em; +} + +.post-body li { + margin: 0.25em 0; +} + +.post-body li > p { + margin: 0; +} + +.post-body h2, +.post-body h3, +.post-body h4 { + margin: 1.7em 0 0.5em; + font-family: var(--serif); + font-weight: 400; + line-height: 1.2; +} + +.post-body h2 { + font-size: 30px; +} + +.post-body h3 { + font-size: 24px; +} + +.post-body h4 { + font-size: 20px; +} + +.post-body :is(h2, h3, h4)::before { + margin-right: 0.6ch; + color: var(--faint); + font: 400 15px/1 var(--mono); + vertical-align: 0.2em; +} + +.post-body h2::before { content: "##"; } +.post-body h3::before { content: "###"; } +.post-body h4::before { content: "####"; } + +.post-body :not(pre) > code { + padding: 0.05em 0.3em; + border-radius: 2px; + background: var(--hl); + font-size: 0.74em; +} + +.post-body kbd { + font: 400 0.72em/1 var(--mono); + vertical-align: 0.1em; +} + +.post-body pre { + margin: 1.4em 0; + padding: 14px 16px; + overflow-x: auto; + background: var(--hl); + font: 400 13px/1.6 var(--mono); + tab-size: 4; +} + +.post-body pre code { + font-size: inherit; +} + +.post-body blockquote { + margin: 1.4em 0; + padding-left: 1em; + border-left: 2px solid var(--rule); + color: var(--dim); +} + +.post-body img, +.post-body video { + display: block; + max-width: 100%; + height: auto; + margin: 1.4em 0; +} + +.post-body hr { + margin: 2.4em 0; + border: 0; + border-top: 1px solid var(--rule); +} + +.post-body table { + display: block; + margin: 1.4em 0; + overflow-x: auto; + border-collapse: collapse; + font: 400 13px/1.5 var(--mono); +} + +.post-body th, +.post-body td { + padding: 6px 16px 6px 0; + border-bottom: 1px solid var(--rule); + text-align: left; + vertical-align: top; +} + +.post-body th { + color: var(--dim); + font-weight: 400; +} + +/* KaTeX's font is Computer Modern too, so it can match the text size. */ +.post-body .katex { + font-size: 1em; +} + +.post-body .katex-display { + margin: 1.2em 0; + padding: 0.2em 0; + overflow-x: auto; + overflow-y: hidden; +} + +.post-body .footnotes { + margin-top: 3em; + padding-top: 1em; + border-top: 1px solid var(--rule); + color: var(--dim); + font-size: 15px; +} + +.post-body .footnotes ol { + margin: 0; +} + +/* imenu-list: the post's headings in a window beside it */ +.post-toc { + margin: 0 0 48px; + font-size: 12px; + line-height: 1.5; +} + +.post-toc .winbar { + margin-bottom: 14px; +} + +.post-toc ol { + margin: 0; + padding: 0; + list-style: none; +} + +.post-toc li.d3 { + padding-left: 2ch; +} + +.post-toc a { + display: block; + padding: 3px 0 3px 1ch; + border-left: 2px solid transparent; + color: var(--dim); + text-decoration: none; +} + +.post-toc a:hover { + color: var(--fg); +} + +.post-toc a[aria-current="true"] { + border-left-color: var(--blue); + color: var(--fg); +} + +.post-toc .mk { + color: var(--faint); +} + +@media (min-width: 1100px) { + .post-cols.has-toc { + display: grid; + grid-template-columns: minmax(0, 680px) minmax(180px, 1fr); + column-gap: 64px; + align-items: start; + } + + .post-cols.has-toc .post-body { + grid-row: 1; + grid-column: 1; + } + + .post-cols.has-toc .post-toc { + position: sticky; + top: calc(var(--top-h) + 32px); + grid-row: 1; + grid-column: 2; + max-height: calc(100vh - var(--top-h) - var(--frame-h) - 64px); + margin: 0; + overflow-y: auto; + } +} + +.post-nav { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 24px; + max-width: 680px; + margin: 96px 0 0; + padding-top: 20px; + border-top: 1px solid var(--rule); + font-size: 13px; + line-height: 1.5; +} + +.post-nav a { + display: flex; + flex-direction: column; + gap: 2px; + text-decoration: none; +} + +.post-nav a:hover { + text-decoration: underline; +} + +.post-nav .dim { + font-size: 12px; +} + +.post-nav .newer { + grid-column: 2; + text-align: right; +} + +.post-all { + margin: 20px 0 0; + font-size: 12px; +} + +.giscus { + max-width: 760px; +} + +.giscus-note { + margin: 16px 0 0; + font-size: 12px; +} + +.missing-links { + display: flex; + gap: 3ch; + margin: 0; + font-size: 13px; +} + /* ───────────────────────────── responsive ───────────────────────────── */ @media (max-width: 1100px) {