From 28c3cfcf6d449ee14a9edf4e7cc6a22c426191ea Mon Sep 17 00:00:00 2001 From: Barrett Ruth Date: Sun, 9 Nov 2025 14:48:07 -0500 Subject: [PATCH 01/16] feat(ci): add ci lol --- .github/workflows/ci.yaml | 38 ++++++++++++++++++++++++++++++++++++++ package.json | 8 ++++++-- 2 files changed, 44 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/ci.yaml diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml new file mode 100644 index 0000000..97252e9 --- /dev/null +++ b/.github/workflows/ci.yaml @@ -0,0 +1,38 @@ +name: CI + +on: + pull_request: + push: + branches: + - main + +jobs: + lint: + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup PNPM + uses: pnpm/action-setup@v4 + with: + version: 10 + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: "pnpm" + + - name: Install dependencies + run: pnpm install + + - name: Prettier check + run: pnpm prettier --check . + + - name: Astro check + run: pnpm astro check + + - name: Build + run: pnpm build diff --git a/package.json b/package.json index 6c74ef1..71f56ba 100644 --- a/package.json +++ b/package.json @@ -7,10 +7,14 @@ "build": "astro build", "preview": "astro preview", "astro": "astro", - "prepare": "husky" + "prepare": "husky", + "check": "astro check" }, "lint-staged": { - "*.{js,ts,jsx,tsx,astro,html,md,json,yml,yaml}": "prettier --write" + "*.{js,ts,jsx,tsx,astro,html,md,json,yml,yaml}": [ + "prettier --write", + "astro check --minimumSeverity warning" + ] }, "dependencies": { "@astrojs/mdx": "^4.3.6", From 16850cf468e2fb85817adf7301c4244589c7e313 Mon Sep 17 00:00:00 2001 From: Barrett Ruth Date: Sun, 9 Nov 2025 14:53:56 -0500 Subject: [PATCH 02/16] fix(ci): proper types --- package.json | 1 + pnpm-lock.yaml | 552 ++++++++++++++++++++++++++++++ src/components/Pseudocode.astro | 2 +- src/layouts/PostLayout.astro | 2 +- src/pages/404.astro | 8 +- src/pages/[category]/[slug].astro | 15 +- src/pages/[category]/index.astro | 3 +- src/pages/index.astro | 21 +- src/types.ts | 18 + 9 files changed, 607 insertions(+), 15 deletions(-) create mode 100644 src/types.ts diff --git a/package.json b/package.json index 71f56ba..37ded1b 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "remark-math": "^6.0.0" }, "devDependencies": { + "@astrojs/check": "^0.9.5", "@typescript-eslint/parser": "^8.46.0", "husky": "^9.1.7", "lint-staged": "^16.2.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 211a904..b5081b7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -21,6 +21,9 @@ importers: specifier: ^6.0.0 version: 6.0.0 devDependencies: + '@astrojs/check': + specifier: ^0.9.5 + version: 0.9.5(prettier-plugin-astro@0.14.1)(prettier@3.6.2)(typescript@5.9.3) '@typescript-eslint/parser': specifier: ^8.46.0 version: 8.46.0(eslint@9.27.0)(typescript@5.9.3) @@ -42,12 +45,30 @@ importers: packages: + '@astrojs/check@0.9.5': + resolution: {integrity: sha512-88vc8n2eJ1Oua74yXSGo/8ABMeypfQPGEzuoAx2awL9Ju8cE6tZ2Rz9jVx5hIExHK5gKVhpxfZj4WXm7e32g1w==} + hasBin: true + peerDependencies: + typescript: ^5.0.0 + '@astrojs/compiler@2.13.0': resolution: {integrity: sha512-mqVORhUJViA28fwHYaWmsXSzLO9osbdZ5ImUfxBarqsYdMlPbqAqGJCxsNzvppp1BEzc1mJNjOVvQqeDN8Vspw==} '@astrojs/internal-helpers@0.7.3': resolution: {integrity: sha512-6Pl0bQEIChuW5wqN7jdKrzWfCscW2rG/Cz+fzt4PhSQX2ivBpnhXgFUCs0M3DCYvjYHnPVG2W36X5rmFjZ62sw==} + '@astrojs/language-server@2.16.0': + resolution: {integrity: sha512-oX2KkuIfEEM5d4/+lfuxy6usRDYko0S02YvtHFTrnqW0h9e4ElAfWZRKyqxWlwpuPdciBPKef5YJ7DFH3PPssw==} + hasBin: true + peerDependencies: + prettier: ^3.0.0 + prettier-plugin-astro: '>=0.11.0' + peerDependenciesMeta: + prettier: + optional: true + prettier-plugin-astro: + optional: true + '@astrojs/markdown-remark@6.3.7': resolution: {integrity: sha512-KXGdq6/BC18doBCYXp08alHlWChH0hdD2B1qv9wIyOHbvwI5K6I7FhSta8dq1hBQNdun8YkKPR013D/Hm8xd0g==} @@ -65,6 +86,9 @@ packages: resolution: {integrity: sha512-UFBgfeldP06qu6khs/yY+q1cDAaArM2/7AEIqQ9Cuvf7B1hNLq0xDrZkct+QoIGyjq56y8IaE2I3CTvG99mlhQ==} engines: {node: 18.20.8 || ^20.3.0 || >=22.0.0} + '@astrojs/yaml2ts@0.2.2': + resolution: {integrity: sha512-GOfvSr5Nqy2z5XiwqTouBBpy5FyI6DEe+/g/Mk5am9SjILN1S5fOEvYK0GuWHg98yS/dobP4m8qyqw/URW35fQ==} + '@babel/helper-string-parser@7.27.1': resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} engines: {node: '>=6.9.0'} @@ -85,6 +109,28 @@ packages: '@capsizecss/unpack@2.4.0': resolution: {integrity: sha512-GrSU71meACqcmIUxPYOJvGKF0yryjN/L1aCuE9DViCTJI7bfkjgYDPD1zbNDcINJwSSP6UaBZY9GAbYDO7re0Q==} + '@emmetio/abbreviation@2.3.3': + resolution: {integrity: sha512-mgv58UrU3rh4YgbE/TzgLQwJ3pFsHHhCLqY20aJq+9comytTXUDNGG/SMtSeMJdkpxgXSXunBGLD8Boka3JyVA==} + + '@emmetio/css-abbreviation@2.1.8': + resolution: {integrity: sha512-s9yjhJ6saOO/uk1V74eifykk2CBYi01STTK3WlXWGOepyKa23ymJ053+DNQjpFcy1ingpaO7AxCcwLvHFY9tuw==} + + '@emmetio/css-parser@https://codeload.github.com/ramya-rao-a/css-parser/tar.gz/370c480ac103bd17c7bcfb34bf5d577dc40d3660': + resolution: {tarball: https://codeload.github.com/ramya-rao-a/css-parser/tar.gz/370c480ac103bd17c7bcfb34bf5d577dc40d3660} + version: 0.4.0 + + '@emmetio/html-matcher@1.3.0': + resolution: {integrity: sha512-NTbsvppE5eVyBMuyGfVu2CRrLvo7J4YHb6t9sBFLyY03WYhXET37qA4zOYUjBWFCRHO7pS1B9khERtY0f5JXPQ==} + + '@emmetio/scanner@1.0.4': + resolution: {integrity: sha512-IqRuJtQff7YHHBk4G8YZ45uB9BaAGcwQeVzgj/zj8/UdOhtQpEIupUhSk8dys6spFIWVZVeK20CzGEnqR5SbqA==} + + '@emmetio/stream-reader-utils@0.1.0': + resolution: {integrity: sha512-ZsZ2I9Vzso3Ho/pjZFsmmZ++FWeEd/txqybHTm4OgaZzdS8V9V/YYWQwg5TC38Z7uLWUV1vavpLLbjJtKubR1A==} + + '@emmetio/stream-reader@2.2.0': + resolution: {integrity: sha512-fXVXEyFA5Yv3M3n8sUGT7+fvecGrZP4k6FnWWMSZVQf69kAq0LLpaBQLGcPR30m3zMmKYhECP4k/ZkzvhEW5kw==} + '@emnapi/runtime@1.5.0': resolution: {integrity: sha512-97/BJ3iXHww3djw6hYIfErCZFee7qCtrneuLa20UXFCOTCfBM2cvQHjWJ2EG0s0MtdNwInarqCTz35i4wWXHsQ==} @@ -674,6 +720,32 @@ packages: '@ungap/structured-clone@1.3.0': resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} + '@volar/kit@2.4.23': + resolution: {integrity: sha512-YuUIzo9zwC2IkN7FStIcVl1YS9w5vkSFEZfPvnu0IbIMaR9WHhc9ZxvlT+91vrcSoRY469H2jwbrGqpG7m1KaQ==} + peerDependencies: + typescript: '*' + + '@volar/language-core@2.4.23': + resolution: {integrity: sha512-hEEd5ET/oSmBC6pi1j6NaNYRWoAiDhINbT8rmwtINugR39loROSlufGdYMF9TaKGfz+ViGs1Idi3mAhnuPcoGQ==} + + '@volar/language-server@2.4.23': + resolution: {integrity: sha512-k0iO+tybMGMMyrNdWOxgFkP0XJTdbH0w+WZlM54RzJU3WZSjHEupwL30klpM7ep4FO6qyQa03h+VcGHD4Q8gEg==} + + '@volar/language-service@2.4.23': + resolution: {integrity: sha512-h5mU9DZ/6u3LCB9xomJtorNG6awBNnk9VuCioGsp6UtFiM8amvS5FcsaC3dabdL9zO0z+Gq9vIEMb/5u9K6jGQ==} + + '@volar/source-map@2.4.23': + resolution: {integrity: sha512-Z1Uc8IB57Lm6k7q6KIDu/p+JWtf3xsXJqAX/5r18hYOTpJyBn0KXUR8oTJ4WFYOcDzWC9n3IflGgHowx6U6z9Q==} + + '@volar/typescript@2.4.23': + resolution: {integrity: sha512-lAB5zJghWxVPqfcStmAP1ZqQacMpe90UrP5RJ3arDyrhy4aCUQqmxPPLB2PWDKugvylmO41ljK7vZ+t6INMTag==} + + '@vscode/emmet-helper@2.11.0': + resolution: {integrity: sha512-QLxjQR3imPZPQltfbWRnHU6JecWTF1QSWhx3GAKQpslx7y3Dp6sIIXhKjiUJ/BR9FX8PVthjr9PD6pNwOJfAzw==} + + '@vscode/l10n@0.0.18': + resolution: {integrity: sha512-KYSIHVmslkaCDyw013pphY+d7x1qV8IZupYfeIfzNA+nsaWHbn5uPuQRvdRFsa9zFzGeudPuoGoZ1Op4jrJXIQ==} + acorn-jsx@5.3.2: resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: @@ -684,9 +756,20 @@ packages: engines: {node: '>=0.4.0'} hasBin: true + ajv-draft-04@1.0.0: + resolution: {integrity: sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==} + peerDependencies: + ajv: ^8.5.0 + peerDependenciesMeta: + ajv: + optional: true + ajv@6.12.6: resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + ajv@8.17.1: + resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==} + ansi-align@3.0.1: resolution: {integrity: sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==} @@ -820,6 +903,10 @@ packages: resolution: {integrity: sha512-7JDGG+4Zp0CsknDCedl0DYdaeOhc46QNpXi3NLQblkZpXXgA6LncLDUUyvrjSvZeF3VRQa+KiMGomazQrC1V8g==} engines: {node: '>=20'} + cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + clone@2.1.2: resolution: {integrity: sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==} engines: {node: '>=0.8'} @@ -937,6 +1024,9 @@ packages: resolution: {integrity: sha512-2QF/g9/zTaPDc3BjNcVTGoBbXBgYfMTTceLaYcFJ/W9kggFUkhxD/hMEeuLKbugyef9SqAx8cpgwlIP/jinUTA==} engines: {node: '>=4'} + emmet@2.4.11: + resolution: {integrity: sha512-23QPJB3moh/U9sT4rQzGgeyyGIrcM+GH5uVYg2C6wZIxAIJq7Ng3QLT79tl8FUwDXhyq9SusfknOrofAKqvgyQ==} + emoji-regex@10.5.0: resolution: {integrity: sha512-lb49vf1Xzfx080OKA0o6l8DQQpV+6Vg95zyCJX9VB/BqKYlhG7N4wgROUUHRA+ZPUefLnteQOad7z1kT2bV7bg==} @@ -965,6 +1055,10 @@ packages: engines: {node: '>=18'} hasBin: true + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + escape-string-regexp@4.0.0: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} @@ -1058,6 +1152,9 @@ packages: fast-levenshtein@2.0.6: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + fast-uri@3.1.0: + resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==} + fastq@1.19.1: resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} @@ -1104,6 +1201,10 @@ packages: engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] + get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + get-east-asian-width@1.4.0: resolution: {integrity: sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q==} engines: {node: '>=18'} @@ -1270,9 +1371,18 @@ packages: json-schema-traverse@0.4.1: resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + json-schema-traverse@1.0.0: + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + json-stable-stringify-without-jsonify@1.0.1: resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + jsonc-parser@2.3.1: + resolution: {integrity: sha512-H8jvkz1O50L3dMZCsLqiuB2tA7muqbSg1AtGEkN0leAqGjsUzDJir3Zwr02BhqdcITPg3ei3mZ+HjMocAknhhg==} + + jsonc-parser@3.3.1: + resolution: {integrity: sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==} + katex@0.16.23: resolution: {integrity: sha512-7VlC1hsEEolL9xNO05v9VjrvWZePkCVBJqj8ruICxYjZfHaHbaU53AlP+PODyFIXEnaEIEWi3wJy7FPZ95JAVg==} hasBin: true @@ -1308,6 +1418,9 @@ packages: lodash.merge@4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + lodash@4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + log-update@6.1.0: resolution: {integrity: sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==} engines: {node: '>=18'} @@ -1522,6 +1635,9 @@ packages: ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + muggle-string@0.4.1: + resolution: {integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==} + nano-spawn@1.0.3: resolution: {integrity: sha512-jtpsQDetTnvS2Ts1fiRdci5rx0VYws5jGyC+4IYOTnIQ/wwdf6JdomlHBwqC3bJYOvaKu0C2GSZ1A60anrYpaA==} engines: {node: '>=20.17'} @@ -1619,6 +1735,9 @@ packages: parse5@7.3.0: resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==} + path-browserify@1.0.1: + resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} + path-exists@4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} @@ -1751,6 +1870,20 @@ packages: remark-stringify@11.0.0: resolution: {integrity: sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==} + request-light@0.5.8: + resolution: {integrity: sha512-3Zjgh+8b5fhRJBQZoy+zbVKpAQGLyka0MPgW3zruTF4dFFJ8Fqcfu9YsAvi/rvdcaTeWG3MkbZv4WKxAn/84Lg==} + + request-light@0.7.0: + resolution: {integrity: sha512-lMbBMrDoxgsyO+yB3sDcrDuX85yYt7sS8BfQd11jtbW/z5ZWgLZRcEGLsLoYw7I0WSUGQBs8CC8ScIxkTX1+6Q==} + + require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + + require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + resolve-from@4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} @@ -1935,6 +2068,12 @@ packages: resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==} engines: {node: '>=16'} + typesafe-path@0.2.2: + resolution: {integrity: sha512-OJabfkAg1WLZSqJAJ0Z6Sdt3utnbzr/jh+NAHoyWHJe8CMSy79Gm085094M9nvTPy22KzTVn5Zq5mbapCI/hPA==} + + typescript-auto-import-cache@0.3.6: + resolution: {integrity: sha512-RpuHXrknHdVdK7wv/8ug3Fr0WNsNi5l5aB8MYYuXhq2UH5lnEB1htJ1smhtD5VeCsGr2p8mUDtd83LCQDFVgjQ==} + typescript@5.9.3: resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} engines: {node: '>=14.17'} @@ -2116,6 +2255,98 @@ packages: vite: optional: true + volar-service-css@0.0.66: + resolution: {integrity: sha512-XrL1V9LEAHnunglYdDf/7shJbQXqKsHB+P69zPmJTqHx6hqvM9GWNbn2h7M0P/oElW8p/MTVHdfjl6C8cxdsBQ==} + peerDependencies: + '@volar/language-service': ~2.4.0 + peerDependenciesMeta: + '@volar/language-service': + optional: true + + volar-service-emmet@0.0.66: + resolution: {integrity: sha512-BMPSpm6mk0DAEVdI2haxYIOt1Z2oaIZvCGtXuRu95x50a5pOSRPjdeHv2uGp1rQsq1Izigx+VR/bZUf2HcSnVQ==} + peerDependencies: + '@volar/language-service': ~2.4.0 + peerDependenciesMeta: + '@volar/language-service': + optional: true + + volar-service-html@0.0.66: + resolution: {integrity: sha512-MKKD2qM8qVZvBKBIugt00+Bm8j1ehgeX7Cm5XwgeEgdW/3PhUEEe/aeTxQGon1WJIGf2MM/cHPjZxPJOQN4WfQ==} + peerDependencies: + '@volar/language-service': ~2.4.0 + peerDependenciesMeta: + '@volar/language-service': + optional: true + + volar-service-prettier@0.0.66: + resolution: {integrity: sha512-CVaQEyfmFWoq3NhNVExoyDKonPqdacmb/07w7OfTZljxLgZpDRygiHAvzBKIcenb7rKtJNHqfQJv99ULOinJBA==} + peerDependencies: + '@volar/language-service': ~2.4.0 + prettier: ^2.2 || ^3.0 + peerDependenciesMeta: + '@volar/language-service': + optional: true + prettier: + optional: true + + volar-service-typescript-twoslash-queries@0.0.66: + resolution: {integrity: sha512-PA3CyvEaBrkxJcBq+HFdks1TF1oJ8H+jTOTQUurLDRkVjmUFg8bfdya6U/dWfTsPaDSRM4m/2chwgew5zoQXfg==} + peerDependencies: + '@volar/language-service': ~2.4.0 + peerDependenciesMeta: + '@volar/language-service': + optional: true + + volar-service-typescript@0.0.66: + resolution: {integrity: sha512-8irsfCEf86R1RqPijrU6p5NCqKDNzyJNWKM6ZXmCcJqhebtl7Hr/a0bnlr59AzqkS3Ym4PbbJZs1K/92CXTDsw==} + peerDependencies: + '@volar/language-service': ~2.4.0 + peerDependenciesMeta: + '@volar/language-service': + optional: true + + volar-service-yaml@0.0.66: + resolution: {integrity: sha512-q6oTKD6EMEu1ws1FDjRw+cfCF69Gu51IEGM9jVbtmSZS1qQHKxMqlt2+wBInKl2D+xILtjzkWbfkjQyBYQMw7g==} + peerDependencies: + '@volar/language-service': ~2.4.0 + peerDependenciesMeta: + '@volar/language-service': + optional: true + + vscode-css-languageservice@6.3.8: + resolution: {integrity: sha512-dBk/9ullEjIMbfSYAohGpDOisOVU1x2MQHOeU12ohGJQI7+r0PCimBwaa/pWpxl/vH4f7ibrBfxIZY3anGmHKQ==} + + vscode-html-languageservice@5.6.0: + resolution: {integrity: sha512-FIVz83oGw2tBkOr8gQPeiREInnineCKGCz3ZD1Pi6opOuX3nSRkc4y4zLLWsuop+6ttYX//XZCI6SLzGhRzLmA==} + + vscode-json-languageservice@4.1.8: + resolution: {integrity: sha512-0vSpg6Xd9hfV+eZAaYN63xVVMOTmJ4GgHxXnkLCh+9RsQBkWKIghzLhW2B9ebfG+LQQg8uLtsQ2aUKjTgE+QOg==} + engines: {npm: '>=7.0.0'} + + vscode-jsonrpc@8.2.0: + resolution: {integrity: sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==} + engines: {node: '>=14.0.0'} + + vscode-languageserver-protocol@3.17.5: + resolution: {integrity: sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==} + + vscode-languageserver-textdocument@1.0.12: + resolution: {integrity: sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA==} + + vscode-languageserver-types@3.17.5: + resolution: {integrity: sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==} + + vscode-languageserver@9.0.1: + resolution: {integrity: sha512-woByF3PDpkHFUreUa7Hos7+pUWdeWMXRd26+ZX2A8cFx6v/JPTtd4/uN0/jB6XQHYaOlHbio03NTHCqrgG5n7g==} + hasBin: true + + vscode-nls@5.2.0: + resolution: {integrity: sha512-RAaHx7B14ZU04EU31pT+rKz2/zSl7xMsfIZuo8pd+KZO6PXtQmpevpq3vxvWNcrGbdmhM/rr5Uw5Mz+NBfhVng==} + + vscode-uri@3.1.0: + resolution: {integrity: sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==} + web-namespaces@2.0.1: resolution: {integrity: sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==} @@ -2142,6 +2373,10 @@ packages: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + wrap-ansi@9.0.2: resolution: {integrity: sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==} engines: {node: '>=18'} @@ -2149,6 +2384,19 @@ packages: xxhash-wasm@1.1.0: resolution: {integrity: sha512-147y/6YNh+tlp6nd/2pWq38i9h6mz/EuQ6njIrmW8D1BS5nCqs0P6DG+m6zTGnNz5I+uhZ0SHxBs9BsPrwcKDA==} + y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + + yaml-language-server@1.19.2: + resolution: {integrity: sha512-9F3myNmJzUN/679jycdMxqtydPSDRAarSj3wPiF7pchEPnO9Dg07Oc+gIYLqXR4L+g+FSEVXXv2+mr54StLFOg==} + hasBin: true + + yaml@2.7.1: + resolution: {integrity: sha512-10ULxpnOCQXxJvBgxsn9ptjq6uviG/htZKk9veJGhlqn3w/DxQ631zFF+nlQXLwmImeS5amR2dl2U8sg6U9jsQ==} + engines: {node: '>= 14'} + hasBin: true + yaml@2.8.1: resolution: {integrity: sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==} engines: {node: '>= 14.6'} @@ -2158,6 +2406,10 @@ packages: resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} engines: {node: '>=12'} + yargs@17.7.2: + resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} + engines: {node: '>=12'} + yocto-queue@0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} @@ -2193,10 +2445,47 @@ packages: snapshots: + '@astrojs/check@0.9.5(prettier-plugin-astro@0.14.1)(prettier@3.6.2)(typescript@5.9.3)': + dependencies: + '@astrojs/language-server': 2.16.0(prettier-plugin-astro@0.14.1)(prettier@3.6.2)(typescript@5.9.3) + chokidar: 4.0.3 + kleur: 4.1.5 + typescript: 5.9.3 + yargs: 17.7.2 + transitivePeerDependencies: + - prettier + - prettier-plugin-astro + '@astrojs/compiler@2.13.0': {} '@astrojs/internal-helpers@0.7.3': {} + '@astrojs/language-server@2.16.0(prettier-plugin-astro@0.14.1)(prettier@3.6.2)(typescript@5.9.3)': + dependencies: + '@astrojs/compiler': 2.13.0 + '@astrojs/yaml2ts': 0.2.2 + '@jridgewell/sourcemap-codec': 1.5.5 + '@volar/kit': 2.4.23(typescript@5.9.3) + '@volar/language-core': 2.4.23 + '@volar/language-server': 2.4.23 + '@volar/language-service': 2.4.23 + fast-glob: 3.3.3 + muggle-string: 0.4.1 + volar-service-css: 0.0.66(@volar/language-service@2.4.23) + volar-service-emmet: 0.0.66(@volar/language-service@2.4.23) + volar-service-html: 0.0.66(@volar/language-service@2.4.23) + volar-service-prettier: 0.0.66(@volar/language-service@2.4.23)(prettier@3.6.2) + volar-service-typescript: 0.0.66(@volar/language-service@2.4.23) + volar-service-typescript-twoslash-queries: 0.0.66(@volar/language-service@2.4.23) + volar-service-yaml: 0.0.66(@volar/language-service@2.4.23) + vscode-html-languageservice: 5.6.0 + vscode-uri: 3.1.0 + optionalDependencies: + prettier: 3.6.2 + prettier-plugin-astro: 0.14.1 + transitivePeerDependencies: + - typescript + '@astrojs/markdown-remark@6.3.7': dependencies: '@astrojs/internal-helpers': 0.7.3 @@ -2258,6 +2547,10 @@ snapshots: transitivePeerDependencies: - supports-color + '@astrojs/yaml2ts@0.2.2': + dependencies: + yaml: 2.8.1 + '@babel/helper-string-parser@7.27.1': {} '@babel/helper-validator-identifier@7.27.1': {} @@ -2279,6 +2572,29 @@ snapshots: transitivePeerDependencies: - encoding + '@emmetio/abbreviation@2.3.3': + dependencies: + '@emmetio/scanner': 1.0.4 + + '@emmetio/css-abbreviation@2.1.8': + dependencies: + '@emmetio/scanner': 1.0.4 + + '@emmetio/css-parser@https://codeload.github.com/ramya-rao-a/css-parser/tar.gz/370c480ac103bd17c7bcfb34bf5d577dc40d3660': + dependencies: + '@emmetio/stream-reader': 2.2.0 + '@emmetio/stream-reader-utils': 0.1.0 + + '@emmetio/html-matcher@1.3.0': + dependencies: + '@emmetio/scanner': 1.0.4 + + '@emmetio/scanner@1.0.4': {} + + '@emmetio/stream-reader-utils@0.1.0': {} + + '@emmetio/stream-reader@2.2.0': {} + '@emnapi/runtime@1.5.0': dependencies: tslib: 2.8.1 @@ -2764,12 +3080,66 @@ snapshots: '@ungap/structured-clone@1.3.0': {} + '@volar/kit@2.4.23(typescript@5.9.3)': + dependencies: + '@volar/language-service': 2.4.23 + '@volar/typescript': 2.4.23 + typesafe-path: 0.2.2 + typescript: 5.9.3 + vscode-languageserver-textdocument: 1.0.12 + vscode-uri: 3.1.0 + + '@volar/language-core@2.4.23': + dependencies: + '@volar/source-map': 2.4.23 + + '@volar/language-server@2.4.23': + dependencies: + '@volar/language-core': 2.4.23 + '@volar/language-service': 2.4.23 + '@volar/typescript': 2.4.23 + path-browserify: 1.0.1 + request-light: 0.7.0 + vscode-languageserver: 9.0.1 + vscode-languageserver-protocol: 3.17.5 + vscode-languageserver-textdocument: 1.0.12 + vscode-uri: 3.1.0 + + '@volar/language-service@2.4.23': + dependencies: + '@volar/language-core': 2.4.23 + vscode-languageserver-protocol: 3.17.5 + vscode-languageserver-textdocument: 1.0.12 + vscode-uri: 3.1.0 + + '@volar/source-map@2.4.23': {} + + '@volar/typescript@2.4.23': + dependencies: + '@volar/language-core': 2.4.23 + path-browserify: 1.0.1 + vscode-uri: 3.1.0 + + '@vscode/emmet-helper@2.11.0': + dependencies: + emmet: 2.4.11 + jsonc-parser: 2.3.1 + vscode-languageserver-textdocument: 1.0.12 + vscode-languageserver-types: 3.17.5 + vscode-uri: 3.1.0 + + '@vscode/l10n@0.0.18': {} + acorn-jsx@5.3.2(acorn@8.15.0): dependencies: acorn: 8.15.0 acorn@8.15.0: {} + ajv-draft-04@1.0.0(ajv@8.17.1): + optionalDependencies: + ajv: 8.17.1 + ajv@6.12.6: dependencies: fast-deep-equal: 3.1.3 @@ -2777,6 +3147,13 @@ snapshots: json-schema-traverse: 0.4.1 uri-js: 4.4.1 + ajv@8.17.1: + dependencies: + fast-deep-equal: 3.1.3 + fast-uri: 3.1.0 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + ansi-align@3.0.1: dependencies: string-width: 4.2.3 @@ -2988,6 +3365,12 @@ snapshots: slice-ansi: 7.1.2 string-width: 8.1.0 + cliui@8.0.1: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + clone@2.1.2: {} clsx@2.1.1: {} @@ -3076,6 +3459,11 @@ snapshots: dset@3.1.4: {} + emmet@2.4.11: + dependencies: + '@emmetio/abbreviation': 2.3.3 + '@emmetio/css-abbreviation': 2.1.8 + emoji-regex@10.5.0: {} emoji-regex@8.0.0: {} @@ -3129,6 +3517,8 @@ snapshots: '@esbuild/win32-ia32': 0.25.10 '@esbuild/win32-x64': 0.25.10 + escalade@3.2.0: {} + escape-string-regexp@4.0.0: {} escape-string-regexp@5.0.0: {} @@ -3253,6 +3643,8 @@ snapshots: fast-levenshtein@2.0.6: {} + fast-uri@3.1.0: {} + fastq@1.19.1: dependencies: reusify: 1.1.0 @@ -3303,6 +3695,8 @@ snapshots: fsevents@2.3.3: optional: true + get-caller-file@2.0.5: {} + get-east-asian-width@1.4.0: {} github-slugger@2.0.0: {} @@ -3542,8 +3936,14 @@ snapshots: json-schema-traverse@0.4.1: {} + json-schema-traverse@1.0.0: {} + json-stable-stringify-without-jsonify@1.0.1: {} + jsonc-parser@2.3.1: {} + + jsonc-parser@3.3.1: {} + katex@0.16.23: dependencies: commander: 8.3.0 @@ -3586,6 +3986,8 @@ snapshots: lodash.merge@4.6.2: {} + lodash@4.17.21: {} + log-update@6.1.0: dependencies: ansi-escapes: 7.1.1 @@ -4090,6 +4492,8 @@ snapshots: ms@2.1.3: {} + muggle-string@0.4.1: {} + nano-spawn@1.0.3: {} nanoid@3.3.11: {} @@ -4191,6 +4595,8 @@ snapshots: dependencies: entities: 6.0.1 + path-browserify@1.0.1: {} + path-exists@4.0.0: {} path-key@3.1.1: {} @@ -4377,6 +4783,14 @@ snapshots: mdast-util-to-markdown: 2.1.2 unified: 11.0.5 + request-light@0.5.8: {} + + request-light@0.7.0: {} + + require-directory@2.1.1: {} + + require-from-string@2.0.2: {} + resolve-from@4.0.0: {} restore-cursor@5.1.0: @@ -4604,6 +5018,12 @@ snapshots: type-fest@4.41.0: {} + typesafe-path@0.2.2: {} + + typescript-auto-import-cache@0.3.6: + dependencies: + semver: 7.7.3 + typescript@5.9.3: {} ufo@1.6.1: {} @@ -4733,6 +5153,103 @@ snapshots: optionalDependencies: vite: 6.3.6(@types/node@24.7.0)(yaml@2.8.1) + volar-service-css@0.0.66(@volar/language-service@2.4.23): + dependencies: + vscode-css-languageservice: 6.3.8 + vscode-languageserver-textdocument: 1.0.12 + vscode-uri: 3.1.0 + optionalDependencies: + '@volar/language-service': 2.4.23 + + volar-service-emmet@0.0.66(@volar/language-service@2.4.23): + dependencies: + '@emmetio/css-parser': https://codeload.github.com/ramya-rao-a/css-parser/tar.gz/370c480ac103bd17c7bcfb34bf5d577dc40d3660 + '@emmetio/html-matcher': 1.3.0 + '@vscode/emmet-helper': 2.11.0 + vscode-uri: 3.1.0 + optionalDependencies: + '@volar/language-service': 2.4.23 + + volar-service-html@0.0.66(@volar/language-service@2.4.23): + dependencies: + vscode-html-languageservice: 5.6.0 + vscode-languageserver-textdocument: 1.0.12 + vscode-uri: 3.1.0 + optionalDependencies: + '@volar/language-service': 2.4.23 + + volar-service-prettier@0.0.66(@volar/language-service@2.4.23)(prettier@3.6.2): + dependencies: + vscode-uri: 3.1.0 + optionalDependencies: + '@volar/language-service': 2.4.23 + prettier: 3.6.2 + + volar-service-typescript-twoslash-queries@0.0.66(@volar/language-service@2.4.23): + dependencies: + vscode-uri: 3.1.0 + optionalDependencies: + '@volar/language-service': 2.4.23 + + volar-service-typescript@0.0.66(@volar/language-service@2.4.23): + dependencies: + path-browserify: 1.0.1 + semver: 7.7.3 + typescript-auto-import-cache: 0.3.6 + vscode-languageserver-textdocument: 1.0.12 + vscode-nls: 5.2.0 + vscode-uri: 3.1.0 + optionalDependencies: + '@volar/language-service': 2.4.23 + + volar-service-yaml@0.0.66(@volar/language-service@2.4.23): + dependencies: + vscode-uri: 3.1.0 + yaml-language-server: 1.19.2 + optionalDependencies: + '@volar/language-service': 2.4.23 + + vscode-css-languageservice@6.3.8: + dependencies: + '@vscode/l10n': 0.0.18 + vscode-languageserver-textdocument: 1.0.12 + vscode-languageserver-types: 3.17.5 + vscode-uri: 3.1.0 + + vscode-html-languageservice@5.6.0: + dependencies: + '@vscode/l10n': 0.0.18 + vscode-languageserver-textdocument: 1.0.12 + vscode-languageserver-types: 3.17.5 + vscode-uri: 3.1.0 + + vscode-json-languageservice@4.1.8: + dependencies: + jsonc-parser: 3.3.1 + vscode-languageserver-textdocument: 1.0.12 + vscode-languageserver-types: 3.17.5 + vscode-nls: 5.2.0 + vscode-uri: 3.1.0 + + vscode-jsonrpc@8.2.0: {} + + vscode-languageserver-protocol@3.17.5: + dependencies: + vscode-jsonrpc: 8.2.0 + vscode-languageserver-types: 3.17.5 + + vscode-languageserver-textdocument@1.0.12: {} + + vscode-languageserver-types@3.17.5: {} + + vscode-languageserver@9.0.1: + dependencies: + vscode-languageserver-protocol: 3.17.5 + + vscode-nls@5.2.0: {} + + vscode-uri@3.1.0: {} + web-namespaces@2.0.1: {} webidl-conversions@3.0.1: {} @@ -4754,6 +5271,12 @@ snapshots: word-wrap@1.2.5: {} + wrap-ansi@7.0.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi@9.0.2: dependencies: ansi-styles: 6.2.3 @@ -4762,10 +5285,39 @@ snapshots: xxhash-wasm@1.1.0: {} + y18n@5.0.8: {} + + yaml-language-server@1.19.2: + dependencies: + '@vscode/l10n': 0.0.18 + ajv: 8.17.1 + ajv-draft-04: 1.0.0(ajv@8.17.1) + lodash: 4.17.21 + prettier: 3.6.2 + request-light: 0.5.8 + vscode-json-languageservice: 4.1.8 + vscode-languageserver: 9.0.1 + vscode-languageserver-textdocument: 1.0.12 + vscode-languageserver-types: 3.17.5 + vscode-uri: 3.1.0 + yaml: 2.7.1 + + yaml@2.7.1: {} + yaml@2.8.1: {} yargs-parser@21.1.1: {} + yargs@17.7.2: + dependencies: + cliui: 8.0.1 + escalade: 3.2.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 21.1.1 + yocto-queue@0.1.0: {} yocto-queue@1.2.1: {} diff --git a/src/components/Pseudocode.astro b/src/components/Pseudocode.astro index 9853e01..53b1551 100644 --- a/src/components/Pseudocode.astro +++ b/src/components/Pseudocode.astro @@ -30,7 +30,7 @@ const lines = Astro.props.code.trim().split(/\r?\n/);
   {lines.map((line, i) => (
-    
+
{i + 1}. {line}
diff --git a/src/layouts/PostLayout.astro b/src/layouts/PostLayout.astro index 9fbddd1..734fa0d 100644 --- a/src/layouts/PostLayout.astro +++ b/src/layouts/PostLayout.astro @@ -40,7 +40,7 @@ if (post?.collection === "git" && post?.slug) { documentTitle = `${post.slug}`; } -const topicColor = getTopicColor(post.collection); +const topicColor = getTopicColor(post?.collection); --- diff --git a/src/pages/404.astro b/src/pages/404.astro index ce776eb..3eec252 100644 --- a/src/pages/404.astro +++ b/src/pages/404.astro @@ -10,7 +10,7 @@ import BaseLayout from "../layouts/BaseLayout.astro"; diff --git a/src/pages/[category]/[slug].astro b/src/pages/[category]/[slug].astro index 16c555b..ae4a1b0 100644 --- a/src/pages/[category]/[slug].astro +++ b/src/pages/[category]/[slug].astro @@ -1,14 +1,19 @@ --- -import { getCollection } from "astro:content"; +import { getCollection, type CollectionEntry } from "astro:content"; import PostLayout from "../../layouts/PostLayout.astro"; import * as collections from "../../content/config"; +import type { PostCollection, AnyCollectionEntry } from "../../types"; export async function getStaticPaths() { const categories = Object.keys(collections.collections).filter( (c) => c !== "git" && c !== "gists", - ); + ) as PostCollection[]; + + const entries: Array<{ + params: { category: string; slug: string }; + props: { post: AnyCollectionEntry }; + }> = []; - const entries = []; for (const category of categories) { const docs = await getCollection(category); for (const doc of docs) { @@ -21,6 +26,10 @@ export async function getStaticPaths() { return entries; } +interface Props { + post: AnyCollectionEntry; +} + const { post } = Astro.props; const category = Astro.params.category; const { Content } = await post.render(); diff --git a/src/pages/[category]/index.astro b/src/pages/[category]/index.astro index c105524..06c6819 100644 --- a/src/pages/[category]/index.astro +++ b/src/pages/[category]/index.astro @@ -3,6 +3,7 @@ import BaseLayout from "../../layouts/BaseLayout.astro"; import { getCollection } from "astro:content"; import { sortItem } from "../../utils/sort.js"; import * as collections from "../../content/config"; +import type { PostCollection } from "../../types"; export async function getStaticPaths() { return Object.keys(collections.collections) @@ -12,7 +13,7 @@ export async function getStaticPaths() { })); } -const category = Astro.params.category; +const category = Astro.params.category as PostCollection; const title = "Barrett Ruth"; const posts = await getCollection(category); diff --git a/src/pages/index.astro b/src/pages/index.astro index 7cc24ea..08bedd2 100644 --- a/src/pages/index.astro +++ b/src/pages/index.astro @@ -1,12 +1,27 @@ --- import BaseLayout from "../layouts/BaseLayout.astro"; import { sortItem } from "../utils/sort.js"; -import { getCollection } from "astro:content"; +import { getCollection, type CollectionEntry } from "astro:content"; +import type { PostCollection } from "../types"; const title = "Barrett Ruth"; -const CATS = ["algorithms", "software", "meditations", "autonomous-racing"]; +const CATS: PostCollection[] = [ + "algorithms", + "software", + "meditations", + "autonomous-racing", +]; -const postsByCategory = {}; +type PostData = { + id: string; + slug: string; + data: { + title: string; + date: string | null; + }; +}; + +const postsByCategory: Record = {}; for (const c of CATS) { const entries = await getCollection(c); entries.sort(sortItem); diff --git a/src/types.ts b/src/types.ts new file mode 100644 index 0000000..4d3d7eb --- /dev/null +++ b/src/types.ts @@ -0,0 +1,18 @@ +import type { CollectionEntry } from "astro:content"; + +export type CollectionKey = + | "algorithms" + | "software" + | "meditations" + | "autonomous-racing" + | "git" + | "gists"; +export type PostCollection = Exclude; + +export type AnyCollectionEntry = + | CollectionEntry<"algorithms"> + | CollectionEntry<"software"> + | CollectionEntry<"meditations"> + | CollectionEntry<"autonomous-racing"> + | CollectionEntry<"git"> + | CollectionEntry<"gists">; From 04308592ff0c1d6cc0d7c268a495040517c34c0b Mon Sep 17 00:00:00 2001 From: Barrett Ruth Date: Sun, 9 Nov 2025 14:55:50 -0500 Subject: [PATCH 03/16] fix: cleanup --- src/components/Footer.astro | 2 +- src/components/Header.astro | 2 +- src/layouts/GitLayout.astro | 2 +- src/layouts/PostLayout.astro | 4 ++-- src/pages/gist.astro | 2 +- src/pages/git.astro | 2 +- src/pages/index.astro | 2 +- 7 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/components/Footer.astro b/src/components/Footer.astro index ac382ea..9f5012c 100644 --- a/src/components/Footer.astro +++ b/src/components/Footer.astro @@ -38,4 +38,4 @@ } - + diff --git a/src/components/Header.astro b/src/components/Header.astro index 0afc660..e039e7e 100644 --- a/src/components/Header.astro +++ b/src/components/Header.astro @@ -40,4 +40,4 @@ const promptText = topic ? `barrett@ruth:~$ ${topic}` : "barrett@ruth:~$"; } - + diff --git a/src/layouts/GitLayout.astro b/src/layouts/GitLayout.astro index 46c0ba1..d7ca0b4 100644 --- a/src/layouts/GitLayout.astro +++ b/src/layouts/GitLayout.astro @@ -10,7 +10,7 @@ const { frontmatter, post } = Astro.props; - +
diff --git a/src/layouts/PostLayout.astro b/src/layouts/PostLayout.astro index 734fa0d..e0b2cbe 100644 --- a/src/layouts/PostLayout.astro +++ b/src/layouts/PostLayout.astro @@ -72,9 +72,9 @@ const topicColor = getTopicColor(post?.collection);
- + - {frontmatter.scripts?.map((src) => +
diff --git a/src/pages/git.astro b/src/pages/git.astro index 857afcf..e1672be 100644 --- a/src/pages/git.astro +++ b/src/pages/git.astro @@ -11,7 +11,7 @@ repos.sort(sortItem); - +
diff --git a/src/pages/index.astro b/src/pages/index.astro index 08bedd2..60a885a 100644 --- a/src/pages/index.astro +++ b/src/pages/index.astro @@ -64,5 +64,5 @@ for (const c of CATS) { - + From 77acd7c517723585ebe730331c3d49077f81785b Mon Sep 17 00:00:00 2001 From: Barrett Ruth Date: Sun, 9 Nov 2025 14:56:25 -0500 Subject: [PATCH 04/16] fix(ci): more cleanup --- src/pages/[category]/[slug].astro | 4 ++-- src/pages/index.astro | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/pages/[category]/[slug].astro b/src/pages/[category]/[slug].astro index ae4a1b0..aa1c7ce 100644 --- a/src/pages/[category]/[slug].astro +++ b/src/pages/[category]/[slug].astro @@ -1,5 +1,5 @@ --- -import { getCollection, type CollectionEntry } from "astro:content"; +import { getCollection } from "astro:content"; import PostLayout from "../../layouts/PostLayout.astro"; import * as collections from "../../content/config"; import type { PostCollection, AnyCollectionEntry } from "../../types"; @@ -43,7 +43,7 @@ if (post.data?.redirect) { {pageTitle} - + diff --git a/src/pages/index.astro b/src/pages/index.astro index 60a885a..e0d0818 100644 --- a/src/pages/index.astro +++ b/src/pages/index.astro @@ -1,7 +1,7 @@ --- import BaseLayout from "../layouts/BaseLayout.astro"; import { sortItem } from "../utils/sort.js"; -import { getCollection, type CollectionEntry } from "astro:content"; +import { getCollection } from "astro:content"; import type { PostCollection } from "../types"; const title = "Barrett Ruth"; From be188323e08d9e3695b9cfbf2ba6db273cffa01c Mon Sep 17 00:00:00 2001 From: Barrett Ruth Date: Sun, 9 Nov 2025 14:57:22 -0500 Subject: [PATCH 05/16] fix(ci): remove unused vars --- src/layouts/GitLayout.astro | 2 +- src/layouts/PostLayout.astro | 10 +--------- src/pages/index.astro | 4 ++-- 3 files changed, 4 insertions(+), 12 deletions(-) diff --git a/src/layouts/GitLayout.astro b/src/layouts/GitLayout.astro index d7ca0b4..df2baa2 100644 --- a/src/layouts/GitLayout.astro +++ b/src/layouts/GitLayout.astro @@ -1,5 +1,5 @@ --- -const { frontmatter, post } = Astro.props; +const { frontmatter } = Astro.props; --- diff --git a/src/layouts/PostLayout.astro b/src/layouts/PostLayout.astro index e0b2cbe..10d983b 100644 --- a/src/layouts/PostLayout.astro +++ b/src/layouts/PostLayout.astro @@ -20,15 +20,7 @@ interface Props { } const { frontmatter, post } = Astro.props as Props; -const { title, description, useKatex = false, useD3 = false } = frontmatter; - -const filePath = post?.id ?? ""; -const categoryFromId = (filePath.split("/")[0] ?? "").replace( - /\.(mdx?|MDX?)$/, - "", -); -const category = - (frontmatter.category ?? post?.collection ?? categoryFromId) || ""; +const { title, description, useKatex = false } = frontmatter; let documentTitle = title; if (post?.collection === "git" && post?.slug) { diff --git a/src/pages/index.astro b/src/pages/index.astro index e0d0818..6fbad4f 100644 --- a/src/pages/index.astro +++ b/src/pages/index.astro @@ -61,8 +61,8 @@ for (const c of CATS) {
- - +
From 9312bb5fb8a6b02cf016f939b0d492a26481d5e7 Mon Sep 17 00:00:00 2001 From: Barrett Ruth Date: Sun, 9 Nov 2025 14:59:59 -0500 Subject: [PATCH 06/16] fix(ci): make astro check more strict --- src/components/Footer.astro | 2 +- src/components/Header.astro | 2 +- src/content/algorithms/leetcode-daily.mdx | 2 +- src/layouts/GitLayout.astro | 2 +- src/layouts/PostLayout.astro | 4 ++-- src/pages/[category]/[slug].astro | 2 +- src/pages/gist.astro | 2 +- src/pages/git.astro | 2 +- 8 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/components/Footer.astro b/src/components/Footer.astro index 9f5012c..246edb2 100644 --- a/src/components/Footer.astro +++ b/src/components/Footer.astro @@ -38,4 +38,4 @@ } - + diff --git a/src/components/Header.astro b/src/components/Header.astro index e039e7e..8d9b177 100644 --- a/src/components/Header.astro +++ b/src/components/Header.astro @@ -40,4 +40,4 @@ const promptText = topic ? `barrett@ruth:~$ ${topic}` : "barrett@ruth:~$"; } - + diff --git a/src/content/algorithms/leetcode-daily.mdx b/src/content/algorithms/leetcode-daily.mdx index 7a53620..e22afc9 100644 --- a/src/content/algorithms/leetcode-daily.mdx +++ b/src/content/algorithms/leetcode-daily.mdx @@ -103,7 +103,7 @@ def countFairPairs(self, nums, lower, upper): ## optimizing the approach -If we interpret the criteria this way, the above approach is relatively efficient. To improve this approach, we'll need to reinterpret the constraints. Forget about the indexing and consider the constraint in aggregate. We want to find all $i,j$ with $x=nums$i$+nums$j$$ such that $i\neq j,lower\leq x\leq upper$. +If we interpret the criteria this way, the above approach is relatively efficient. To improve this approach, we'll need to reinterpret the constraints. Forget about the indexing and consider the constraint in aggregate. We want to find all $i,j$ with $x=nums[i]+nums[j]$ such that $i\neq j,lower\leq x\leq upper$. We _still_ need to reduce the “dimensionality” of the problem—there are just too many moving parts to consider at once. This seems challening. Let's simplify the problem to identify helpful ideas: pretend `lower` does not exist (and, of course, that `nums` is sorted). diff --git a/src/layouts/GitLayout.astro b/src/layouts/GitLayout.astro index df2baa2..411560f 100644 --- a/src/layouts/GitLayout.astro +++ b/src/layouts/GitLayout.astro @@ -10,7 +10,7 @@ const { frontmatter } = Astro.props; - +
diff --git a/src/layouts/PostLayout.astro b/src/layouts/PostLayout.astro index 10d983b..958b0f8 100644 --- a/src/layouts/PostLayout.astro +++ b/src/layouts/PostLayout.astro @@ -64,9 +64,9 @@ const topicColor = getTopicColor(post?.collection);
- + - {frontmatter.scripts?.map((src) => + diff --git a/src/pages/gist.astro b/src/pages/gist.astro index fedf29a..0321f82 100644 --- a/src/pages/gist.astro +++ b/src/pages/gist.astro @@ -11,7 +11,7 @@ gists.sort(sortItem); - +
diff --git a/src/pages/git.astro b/src/pages/git.astro index e1672be..5e303bf 100644 --- a/src/pages/git.astro +++ b/src/pages/git.astro @@ -11,7 +11,7 @@ repos.sort(sortItem); - +
From eac0aa54b1f4338dd020f268be3f0dc2c86ee1d4 Mon Sep 17 00:00:00 2001 From: Barrett Ruth Date: Sun, 9 Nov 2025 15:00:48 -0500 Subject: [PATCH 07/16] fix(ci): dont run astro check on staged files --- package.json | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/package.json b/package.json index 37ded1b..b01654c 100644 --- a/package.json +++ b/package.json @@ -11,10 +11,7 @@ "check": "astro check" }, "lint-staged": { - "*.{js,ts,jsx,tsx,astro,html,md,json,yml,yaml}": [ - "prettier --write", - "astro check --minimumSeverity warning" - ] + "*.{js,ts,jsx,tsx,astro,html,md,json,yml,yaml}": "prettier --write" }, "dependencies": { "@astrojs/mdx": "^4.3.6", From c8bcc4663282f95b603f14abb8b254289024e813 Mon Sep 17 00:00:00 2001 From: Barrett Ruth Date: Sun, 9 Nov 2025 15:03:48 -0500 Subject: [PATCH 08/16] cleanup --- .github/workflows/ci.yaml | 2 -- package.json | 5 ++++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 97252e9..0e46c99 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -16,8 +16,6 @@ jobs: - name: Setup PNPM uses: pnpm/action-setup@v4 - with: - version: 10 - name: Setup Node uses: actions/setup-node@v4 diff --git a/package.json b/package.json index b01654c..b361e7b 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,10 @@ "check": "astro check" }, "lint-staged": { - "*.{js,ts,jsx,tsx,astro,html,md,json,yml,yaml}": "prettier --write" + "*.{js,ts,jsx,tsx,astro,html,md,json,yml,yaml}": [ + "prettier --write", + "astro check" + ] }, "dependencies": { "@astrojs/mdx": "^4.3.6", From 0aaf0f13c8cf2c6ef1b46e45ca896635c89f17b0 Mon Sep 17 00:00:00 2001 From: Barrett Ruth Date: Sun, 9 Nov 2025 15:04:41 -0500 Subject: [PATCH 09/16] fix(ci): cleanup --- .github/workflows/aws.yaml | 2 +- .../algorithms/extrema-circular-buffer.mdx | 4 +- .../algorithms/models-of-production.mdx | 1 - src/content/algorithms/proofs.mdx | 91 ++++++++++++------- .../multithreading-a-gui.mdx | 9 +- .../refactoring-a-state-machine.mdx | 11 +-- src/content/git/cp.nvim.mdx | 55 ++++++++--- src/content/software/hosting-a-git-server.mdx | 2 +- 8 files changed, 112 insertions(+), 63 deletions(-) diff --git a/.github/workflows/aws.yaml b/.github/workflows/aws.yaml index 860f401..67f8aa0 100644 --- a/.github/workflows/aws.yaml +++ b/.github/workflows/aws.yaml @@ -21,7 +21,7 @@ jobs: uses: actions/setup-node@v3 with: node-version: 18 - cache: 'pnpm' + cache: "pnpm" - name: Install dependencies run: pnpm install diff --git a/src/content/algorithms/extrema-circular-buffer.mdx b/src/content/algorithms/extrema-circular-buffer.mdx index 8250f0e..b2f628d 100644 --- a/src/content/algorithms/extrema-circular-buffer.mdx +++ b/src/content/algorithms/extrema-circular-buffer.mdx @@ -4,8 +4,7 @@ date: "30/07/2024" useKatex: true --- -import { Code } from 'astro:components'; - +import { Code } from "astro:components"; # context @@ -15,7 +14,6 @@ While working for [TRB Capital Management](https://trbcap.com/), certain strateg Design a data structure supporting the following operations: - - `build(size_t capacity)` : initialize the data structure with capacity/window size `capacity` The data structure must always hold $\leq$ `capacity` prices. - `void push_back(double value)` diff --git a/src/content/algorithms/models-of-production.mdx b/src/content/algorithms/models-of-production.mdx index 5552192..c7cfe62 100644 --- a/src/content/algorithms/models-of-production.mdx +++ b/src/content/algorithms/models-of-production.mdx @@ -138,7 +138,6 @@ Using both mathematical intuition and manipulating the visualization above, we f - $\bar{A}$ has a positive relationship with steady-state output - Capital is influenced by workforce size, TFP, and savings rate - Capital output share's $\alpha$ impact on output is twofold: - 1. Directly through capital quantity 2. Indirectly through TFP diff --git a/src/content/algorithms/proofs.mdx b/src/content/algorithms/proofs.mdx index f867cea..ad7c189 100644 --- a/src/content/algorithms/proofs.mdx +++ b/src/content/algorithms/proofs.mdx @@ -5,7 +5,7 @@ useKatex: true useD3: true --- -import Pseudocode from '@components/Pseudocode.astro'; +import Pseudocode from "@components/Pseudocode.astro"; A computer science student attempting to learn proofs. @@ -14,16 +14,19 @@ A computer science student attempting to learn proofs. ## E Minimize-Digit-Diff($l, r$): + 1. Initialize $ans=18$, the largest possible value of $f(l, x) + f(x, r)$ with $l \leq r\leq 10^9$ 2. For $i:=1$ to $200$: - - Let $x$ be a random sample from the open interval $[l, r]$ - - Let $cost:=f(l, x) + f(x, r)$, computed in $O(1)$ time - - $ans:=min(ans,cost)$ + +- Let $x$ be a random sample from the open interval $[l, r]$ +- Let $cost:=f(l, x) + f(x, r)$, computed in $O(1)$ time +- $ans:=min(ans,cost)$ + 3. Return $ans$ --- -*Proof.* +_Proof._ We are interested in minimizing the expected probability of failure over $t\leq10^4$ tests. Firstly, consider a single test (i.e. a single $(l,r)$ input): @@ -32,7 +35,6 @@ Thus, a digit has approximately a $1-\frac{1}{5}=\frac{4}{5}$ probability of min It follows that the candidate then has roughly a $(\frac{4}{5})^9\approx13\%$ chance of minimizing the entire score $f(l,x)+f(x,r)$. So, one random sample $x$ has a $100-13\approx87\%$ chance of being a suboptimal candidate. - Consider sampling $n$ times--the probability of all random samples being suboptimal is $0.87^n$. Let's only settle for a probability of failure near $1/10^{10}$ and solve for our number of trials: $$ @@ -57,7 +59,7 @@ Maximize-F(s): --- -*Proof.* +_Proof._ Let $X$ and $Y$ be the number of occurrences of zeroes and ones in the substring $p$ of $s$. Then, $f(p)=max(X, Y)=\frac{X+Y+|X-Y|}{2}$. The goal is to compute: @@ -96,11 +98,12 @@ $\blacksquare$ ## A Count-Pairs($n$): + 1. Return $n-1$ --- -*Proof.* +_Proof._ Suppose $(a,b)\in\mathbb{N}^2$. Because $a=n-b$ and $a\geq1$, it follows that $1\leq b\leq n-1$. Each choice of $b$ yields a unique $a=n-b$, so there are $n-1$ unique solutions. @@ -109,15 +112,17 @@ $\blacksquare$ ## B Mirror-String($s$): + 1. Reverse $s$ 2. For each character $c$ in $s$: - - If $c$ is "w": Print($c$) - - If $c$ is "p": Print($q$) - - If $c$ is "q": Print($p$) + +- If $c$ is "w": Print($c$) +- If $c$ is "p": Print($q$) +- If $c$ is "q": Print($p$) --- -*Proof.* +_Proof._ The string appears fipped on the y-axis from within the score due to the perspective shifting. Structurally, it is read right-to-left. "p"/"q"/"w" appear as "q"/"p"/"w" when flipped on its y-axis. @@ -127,11 +132,12 @@ $\blacksquare$ ## C Seat-Monkeys($a$, $b$, $c$, $m$): + 1. Return $min(m, a)+min(m, b)+min(c, 2\cdot m-(min(m, a) + min(m, b)))$ --- -*Proof.* +_Proof._ Consider an assignment of monkeys $S$ that sits the $a$ and $b$ monkeys in the first and second row and then fills the remaining seats with the $c$ monkeys. @@ -140,11 +146,16 @@ Assume there exists a more optimal assignment of monkeys $S^{'}$. WLOG, assume $ $S^{'}$ can only differ from $S$ as follows: 1. Seats a $c$ monkey in row 1 instead of an $a$ monkey - - $S^{'}$ leaves an $a$ monkey unseated. $S$ seats this monkey instead--the same number of monkeys are seated in $S$. + +- $S^{'}$ leaves an $a$ monkey unseated. $S$ seats this monkey instead--the same number of monkeys are seated in $S$. + 2. Seats a $c$ monkey in row 2 instead of a $b$ monkey - - $S^{'}$ leaves a $b$ monkey unseated. $S$ seats this monkey instead--the same number of monkeys are seated in $S$. + +- $S^{'}$ leaves a $b$ monkey unseated. $S$ seats this monkey instead--the same number of monkeys are seated in $S$. + 3. Does not seat a monkey where $S$ has - - $S$ seats more than $S^{'}$. + +- $S$ seats more than $S^{'}$. In all cases, $S^{'}$ is no better than S, therefore $S$ is optimal. @@ -153,21 +164,26 @@ $\blacksquare$ ## D Construct-B($a$): + 1. Let $b$ be an array of size $n=\#a$ and $X$ be the set of numbers in $a$. 2. For each element $x$ of $a$ at index $i$: + - If $x\in X$: - $b[i]:=x$ - $X:=X \backslash \{x\}$ + 3. Let $Y=\{1,2,\cdots,n\}\backslash X$ 4. For each element $x$ of $b$ at index $i$: + - If $b[i]$ is NIL: - $b[i]:=\text{first-element}(Y)$ - $Y:=Y\backslash\{\text{first-element}{(Y)}\}$ + 5. Return $b$ --- -*Proof.* +_Proof._ Consider the array $b$ from Construct-B. @@ -182,17 +198,21 @@ $\blacksquare$ ## E Count-Pairs($l_1$, $l_2$, $r_1$, $r_2$, $k$): + 1. Let $A:=\lfloor log_k(r_2/l_1)\rfloor$ 2. Let $B:=\lfloor max(0, log_k(l_2/r_1))\rfloor$ 3. Let $\text{total}:=0$ 4. For each $A\leq i\leq B$: + - Let $r=\lfloor r_2/ k^n\rfloor$ - Let $l=\lfloor l_2/k^n\rfloor$ - $\text{total} := \text{total} + max(0, r - l + 1)$ + 5. Return $\text{total}$ + --- -*Proof.* +_Proof._ Each value of $n$ corresponds to a line with slope $k^n$ because $y/x=k^n\leftrightarrow y=x\cdot k^n$. The problem can be visualized as follows: @@ -206,12 +226,13 @@ For each $n_0$ in this range, the smallest $x$ satisfying $y=x\cdot k^n_0$ is $\ 1. Let $A=\sum a$ and $B=\sum b$. 2. For each query with requested beauty $q$: + - If $\exists (i,j)\in(\{1,2,\cdots,n\},\{1,2,\cdots,m\}):(A-a[i])\cdot(B-b[j])=x$, print "YES" - Otherwise, print "NO" --- -*Proof.* +_Proof._ The beauty of the grid equals $B=\sum_i \sum_j M_{i,j}=\sum_i\sum_j a_i\cdot b_j=\sum_i(a_i\cdot \sum_j b_j)=(\sum_i a_i)\cdot (\sum_j b_j)$. @@ -231,14 +252,16 @@ If such $a_i$ and $b_j$ exist, the operation can be performed. 1. Let $G$ be the input graph 2. Let $\text{ans}=0$ 3. For each component $C$ in $G$: - - Let $\text{cycle}$ be the set of nodes in the cycle of $C$ - - Let $d=max_{u\in \text{cycle}}\text{Distance-To-Cycle(C, u)}$ - - $\text{ans}:=max(\text{ans}, d)$ + +- Let $\text{cycle}$ be the set of nodes in the cycle of $C$ +- Let $d=max_{u\in \text{cycle}}\text{Distance-To-Cycle(C, u)}$ +- $\text{ans}:=max(\text{ans}, d)$ + 4. Return $\text{ans}$ --- -*Proof.* +_Proof._ Consider some $v_i\in V$. There must exist an edge $e=(v_i,v_j),i\neq j$. Following this path from $v_i$, the each edge must map to a previously seen node, forming a cycle, or a new node. Because the graph is finite, the path starting any $v_i$ must contain a cycle. Thus, G is a graph of components with one cycle each. @@ -260,18 +283,20 @@ $\blacksquare$ 1. Let $G$ be the input graph 2. Let $\text{ans}=0$ 3. For each component $C$ in $G$: - - Let $U$ be the set of all nodes not in the cycle of $C$ - - Let $\text{count}$ be the number of plushies each spider has - - Let $\text{par}$ be the set of parents for each spider - - Run a multi-source BFS simulating the state transitions on $U$. For each iteration at year $y$: - - $\text{count[u]}:=\text{count[u]}+\sum_{p\in \text{par[u]}}\text{count[p]}$ - - $\text{ans}:=max(\text{ans}, \text{count[u]})$ - - $y:=y+1$ + +- Let $U$ be the set of all nodes not in the cycle of $C$ +- Let $\text{count}$ be the number of plushies each spider has +- Let $\text{par}$ be the set of parents for each spider +- Run a multi-source BFS simulating the state transitions on $U$. For each iteration at year $y$: + - $\text{count[u]}:=\text{count[u]}+\sum_{p\in \text{par[u]}}\text{count[p]}$ + - $\text{ans}:=max(\text{ans}, \text{count[u]})$ + - $y:=y+1$ + 4. Return $\text{ans}$ --- -*Proof.* +_Proof._ Consider some $v_i\in V$. There must exist an edge $e=(v_i,v_j),i\neq j$. Following this path from $v_i$, the each edge must map to a previously seen node, forming a cycle, or a new node. Because the graph is finite, the path starting any $v_i$ must contain a cycle. Thus, G is a graph of components with one cycle each. @@ -295,7 +320,9 @@ $\blacksquare$ - Let $\text{Colwise}$ be the column-wise 2D-prefix coefficient sum matrix of $A$ - Let $\text{Rowwise}$ be the row-wise 2D-prefix coefficient sum matrix of $A$ - Let $\text{Submatrix-Sum}(x_1,y_1,x_2,y_2)$ compute the sum of the submatrix of $M$ bounded by $(x_1,y_1)$ and $(x_2,y_2)$ + 1. For each query $x_1,x_2,y_1,y_2$: + - Let $w=y_2-y_1+1$ - Let $prefix=\text{Submatrix-Sum}(\text{Prefix},x_1,y_1,x_2,y_2)$ - Let $rowsum=\text{Submatrix-Sum}(\text{Rowwise},x_1,y_1,x_2,y_2)$ @@ -304,7 +331,7 @@ $\blacksquare$ --- -*Proof.* +_Proof._ Mathematically formulated: diff --git a/src/content/autonomous-racing/multithreading-a-gui.mdx b/src/content/autonomous-racing/multithreading-a-gui.mdx index 0e5958b..f5b90bd 100644 --- a/src/content/autonomous-racing/multithreading-a-gui.mdx +++ b/src/content/autonomous-racing/multithreading-a-gui.mdx @@ -71,19 +71,19 @@ This lead me to the follow structure: ![multi-threaded-design](/multithreading-a-gui/multi-threaded-implementation.webp) - Three callback groups are triggered at differing intervals according to their urgency on the GUI node - - A thread-safe queue[^2] processes all ingested data for each callback group + - A thread-safe queue[^2] processes all ingested data for each callback group - Every 10ms, the GUI is updated, highest to lowest urgency messages first - The `MainWindow` houses the visualization widgets as before—however, the GUI thread actually performs the update logic - GUI Widgets were re-implemented to be thread-safe with basic locking, a small amount of overhead for safe memory access ## \*actual\* multi-threaded implementation -Sounds good, right? Well, I should've done my research first. The Qt framework has *already internalized* the logic for this entire paradigm of multithreaded code. Turns out all I need are: +Sounds good, right? Well, I should've done my research first. The Qt framework has _already internalized_ the logic for this entire paradigm of multithreaded code. Turns out all I need are: - [Signals/slots](https://doc.qt.io/qt-6/signalsandslots.html) and a `Qt::QueuedConnection` - Running the GUI with `MultithreadedExecutor` -As it turns out, signals and slots *automatically* leverage ROS's internal thread-safe message queue, ensuring deserialization one at a time. +As it turns out, signals and slots _automatically_ leverage ROS's internal thread-safe message queue, ensuring deserialization one at a time. The following (final) design employs two threads: @@ -132,8 +132,8 @@ Elegantly, registering a signal/slot with `Qt::QueuedConnection` does all of the Looking back, this GUI should've been implemented with a modern web framework such as [React](https://react.dev/) with [react-ros](https://github.com/flynneva/react-ros?tab=readme-ov-file). CAR needs high-speed, reactive data, and a QtC++ front-end is simply not meant for this level of complexity. I made it a lot harder than it needed to be with my lack of due diligence, but the single-threaded GUI event loop in ROS is more harm than help. - [^1]: See [the ROS documentation](https://docs.ros.org/en/foxy/How-To-Guides/Using-callback-groups.onhtml) to learn more. The CAR publishes various topic-related data at set rates, so I'm looking to run various groups of mutually exclusive callbacks at a set interval (i.e. `MutuallyExclusive`) + [^2]: The simplest implementation did the job: ```cpp @@ -179,3 +179,4 @@ Looking back, this GUI should've been implemented with a modern web framework su } ... }; + ``` diff --git a/src/content/autonomous-racing/refactoring-a-state-machine.mdx b/src/content/autonomous-racing/refactoring-a-state-machine.mdx index cfccf35..09e778d 100644 --- a/src/content/autonomous-racing/refactoring-a-state-machine.mdx +++ b/src/content/autonomous-racing/refactoring-a-state-machine.mdx @@ -9,7 +9,7 @@ Given the recent switch to the [Marelli flag system](https://www.racecar-enginee # the problem -Our original state machine implementation had grown organically over several racing seasons. What started as a simple finite state machine under a more trivial flag system grew vastly more complex as it maladapted to the new flag system. Specifically, the original flag system only had one flag system for the entire track and a few car-specific ones (i.e. request to retire the car after a rules violation). The Marelli flag system has both flag *and* vehicle flags, each of which enforces different requirements. +Our original state machine implementation had grown organically over several racing seasons. What started as a simple finite state machine under a more trivial flag system grew vastly more complex as it maladapted to the new flag system. Specifically, the original flag system only had one flag system for the entire track and a few car-specific ones (i.e. request to retire the car after a rules violation). The Marelli flag system has both flag _and_ vehicle flags, each of which enforces different requirements. The state machine worked "fine" for its initial use case: @@ -29,7 +29,7 @@ State_Machine: 2. Subsequently, the code was read in and parsed with a [cpp yaml library](https://github.com/jbeder/yaml-cpp). -3. Since all of this was done at runtime, `state_actions`, `entry_condition`, and `next_states` were *manually parsed*, hard-coded in a sort of custom language encoded in a string. Opening up our codebase, I found this set of foundational structs that the data was converted into: +3. Since all of this was done at runtime, `state_actions`, `entry_condition`, and `next_states` were _manually parsed_, hard-coded in a sort of custom language encoded in a string. Opening up our codebase, I found this set of foundational structs that the data was converted into: ```cpp struct Action { @@ -83,9 +83,9 @@ I knew state machines were ubiquitous in software so I explored a few previous a 1. One big switch: nested conditions (i.e. all combinations of vehicle + track flags) seemed a bit nightmarish to look at as we continue to build out the state machine 2. [Boost SMS](https://www.boost.org/library/latest/msm/): this and other existing libraries looked enticing but three things stopped me: - 1. Integrating dependencies into our production code is simple but takes time for other members of the team to vet it - 2. It also bloats our deployment latency and final size because: - 3. It is overkill and writing things from scratch is more fun + 1. Integrating dependencies into our production code is simple but takes time for other members of the team to vet it + 2. It also bloats our deployment latency and final size because: + 3. It is overkill and writing things from scratch is more fun 3. A minimalist and expressive approach: @@ -121,7 +121,6 @@ enum class StateID { State1, State2, ..., Last }; A transition has entry and exit conditions which are compositions of state (i.e. just our bitmask) and an arbitrary action (i.e. a lambda). The entry/exit bits must be set/unset in order to enter/exit the state. - ```cpp struct Transition { VehicleTrackFlags entry; diff --git a/src/content/git/cp.nvim.mdx b/src/content/git/cp.nvim.mdx index b151651..6f3156f 100644 --- a/src/content/git/cp.nvim.mdx +++ b/src/content/git/cp.nvim.mdx @@ -12,12 +12,17 @@ Things have changed since I last documented my competitive programming setup [he After many months of using the aforementioned `make` based setup, I had a few qualms: -- I'm lazy: I grew tired of copying (and mis-copying) inputs, outputs, etc from online judges. -- I'm lazy: I frequently submitted incorrect solutions after erroneously asserting that my outputs matched those of the sample test cases -- External dependencies: it unsettles me that my bare-bones setup required copy-pasting an entire suite of scripts -- Non-native NeoVim experience: while composition and the UNIX philosophy are great, there's only so much you can do with pipes and files. - - Raw I/O files meant I couldn't see colored stdin/stdout - - Fine-grained per-testcase I/O was suspect--isolating and running a subset of test cases required manual intervention +- I'm lazy: I grew tired of copying (and mis-copying) inputs, outputs, + etc from online judges. +- I'm lazy: I frequently submitted incorrect solutions after erroneously + asserting that my outputs matched those of the sample test cases +- External dependencies: it unsettles me that my bare-bones setup + required copy-pasting an entire suite of scripts +- Non-native NeoVim experience: while composition and the UNIX philosophy + are great, there's only so much you can do with pipes and files. - Raw I/O + files meant I couldn't see colored stdin/stdout - Fine-grained per-testcase + I/O was suspect--isolating and running a subset of test cases required manual + intervention The solution was to leverage Neovim's great APIs to give me granular control over every aspect of my problem-solving experience. @@ -25,22 +30,42 @@ The solution was to leverage Neovim's great APIs to give me granular control ove The GitHub page documents the plugin well enough so I'll avoid re-hashing it here. Instead, what's more interesting to document is why I thought this was a worthwhile experience. -1. Making Something Useful for Others: cp.nvim is an opportunity for me to make my first open-source project "right"--not some side project or demo, but a *real*, usable tool that I'll be rolling out to the public soon. I consider the following in my active development of the plugin: +1. Making Something Useful for Others: cp.nvim is an opportunity for me + to make my first open-source project "right"--not some side project or demo, + but a *real*, usable tool that I'll be rolling out to the public soon. I + consider the following in my active development of the plugin: -- Comprehensive continuous integration (*real* testing, linting, and more) +- Comprehensive continuous integration (_real_ testing, linting, and more) - [LuaRocks](https://luarocks.org/) integration (the future of NeoVim package management) - Concise and thorough Vimdoc documentation that communicates effectively - Modern lua tooling: use of [busted](https://lunarmodules.github.io/busted/), [selene](https://kampfkarren.github.io/selene/) and more integrated with the NeoVim lua interpreter - Sensible user defaults & extreme customization - Proper versioning, tagging, and releases -2. The Neovim Community: I'm elated to finally give back to the community (even if no one uses this plugin). [folke](https://github.com/folke), [bfredl](https://github.com/bfredl), and [echasnovski](https://github.com/echasnovski) are my greatest inspirations as an open-source developer and I've had enough of taking without giving back. +2. The Neovim Community: I'm elated to finally give back to the community + (even if no one uses this plugin). [folke](https://github.com/folke), + [bfredl](https://github.com/bfredl), and + [echasnovski](https://github.com/echasnovski) are my greatest inspirations as + an open-source developer and I've had enough of taking without giving back. + - In the coming months I plan to contribute to [NeoVim core](https://github.com/neovim/neovim), including making `:checkhealth` asynchronous and integrating an [mdx](https://mdxjs.com/) parser. -3. Learning Random things: I think this plugin is *really* cool by virtue of its efficacy and the miscellany of knowledge I accrued in the 15k+ LOC as of version v0.3.0. Some things I learned include: +3. Learning Random things: I think this plugin is *really* cool by virtue + of its efficacy and the miscellany of knowledge I accrued in the 15k+ LOC as + of version v0.3.0. Some things I learned include: -- ANSI terminal colors and escape codes: I wrote my own stateful ANSI escape sequence parser to map raw bytes to native NeoVim highlighted text -- Extmarks: NeoVim extmarks (`:h extmarks`) are extremely powerful. Here, I used them to apply dynamic highlighting across various components of the plugin but I also plan to leverage virtual text to catch compile errors in real-time -- VIM filetypes and diffing: Vim is strange and the event-based system is fragile. I faced filetype detection race conditions and odd side effects of functions (such as `:diffthis` resetting `foldcolumn`). -- [LuaCATS](https://github.com/LuaCATS): apparently writing comments is the best way to typecheck in lua... -- The (Neo)Vim event loop: Scraper subprocesses spawned with `vim.system`. Though a powerful API, I often had to obey the event loop and wrap side effects with `vim.schedule` to ensure they ran after jobs finished. This was useful to defer UI updates. +- ANSI terminal colors and escape codes: I wrote my own stateful ANSI + escape sequence parser to map raw bytes to native NeoVim highlighted text +- Extmarks: NeoVim extmarks (`:h extmarks`) are extremely powerful. Here, + I used them to apply dynamic highlighting across various components of the + plugin but I also plan to leverage virtual text to catch compile errors in + real-time +- VIM filetypes and diffing: Vim is strange and the event-based system is + fragile. I faced filetype detection race conditions and odd side effects of + functions (such as `:diffthis` resetting `foldcolumn`). +- [LuaCATS](https://github.com/LuaCATS): apparently writing comments is + the best way to typecheck in lua... +- The (Neo)Vim event loop: Scraper subprocesses spawned with + `vim.system`. Though a powerful API, I often had to obey the event loop and + wrap side effects with `vim.schedule` to ensure they ran after jobs finished. + This was useful to defer UI updates. diff --git a/src/content/software/hosting-a-git-server.mdx b/src/content/software/hosting-a-git-server.mdx index 4383769..c68bf85 100644 --- a/src/content/software/hosting-a-git-server.mdx +++ b/src/content/software/hosting-a-git-server.mdx @@ -83,7 +83,7 @@ Restart=on-failure [Install] WantedBy=multi-user.target -```` +``` 12. I use the name `main` instead of `master` as my default branch (it's less letters, ok?)--set this up on the server too: From 1de9c3decb33c85b06625e9fb9abb60d2261fd28 Mon Sep 17 00:00:00 2001 From: Barrett Ruth Date: Sun, 9 Nov 2025 15:07:46 -0500 Subject: [PATCH 10/16] fix(proofs): reformat equation to be multiline --- src/content/algorithms/proofs.mdx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/content/algorithms/proofs.mdx b/src/content/algorithms/proofs.mdx index ad7c189..c770058 100644 --- a/src/content/algorithms/proofs.mdx +++ b/src/content/algorithms/proofs.mdx @@ -83,9 +83,9 @@ As $S_ii}$, $|S_{j>i}-S_i|=S_{j>i}-S_i$--the sum is unchanged. Rewriting t $$ \begin{align*} -\sum_{1\leq r\leq n}\sum_{1\leq l Date: Sun, 9 Nov 2025 15:26:34 -0500 Subject: [PATCH 11/16] feat: use two styles --- astro.config.mjs | 69 ++++++++++++++++++++++++++++++++++++++++- public/styles/posts.css | 9 +++--- 2 files changed, 73 insertions(+), 5 deletions(-) diff --git a/astro.config.mjs b/astro.config.mjs index 3ac90be..86b2383 100644 --- a/astro.config.mjs +++ b/astro.config.mjs @@ -4,6 +4,70 @@ import remarkMath from "remark-math"; import rehypeKatex from "rehype-katex"; import path from "path"; +const midnight = { + name: "midnight", + type: "dark", + colors: { + "editor.background": "#121212", + "editor.foreground": "#e0e0e0", + }, + tokenColors: [ + { + scope: [ + "keyword", + "storage.type", + "storage.modifier", + "keyword.control", + "keyword.operator.new", + ], + settings: { foreground: "#7aa2f7" }, + }, + { + scope: [ + "string", + "constant", + "constant.numeric", + "constant.language", + "constant.character", + "number", + ], + settings: { foreground: "#e5c07b" }, + }, + ], +}; + +const daylight = { + name: "daylight", + type: "light", + colors: { + "editor.background": "#f5f5f5", + "editor.foreground": "#1a1a1a", + }, + tokenColors: [ + { + scope: [ + "keyword", + "storage.type", + "storage.modifier", + "keyword.control", + "keyword.operator.new", + ], + settings: { foreground: "#3b5bdb" }, + }, + { + scope: [ + "string", + "constant", + "constant.numeric", + "constant.language", + "constant.character", + "number", + ], + settings: { foreground: "#996800" }, + }, + ], +}; + export default defineConfig({ build: { format: "file", @@ -23,7 +87,10 @@ export default defineConfig({ }, markdown: { shikiConfig: { - theme: "github-light", + themes: { + light: daylight, + dark: midnight, + }, langs: [], wrap: true, }, diff --git a/public/styles/posts.css b/public/styles/posts.css index dc49a8b..46399bd 100644 --- a/public/styles/posts.css +++ b/public/styles/posts.css @@ -118,12 +118,13 @@ article pre { padding: 1rem; overflow-x: auto; border-radius: 4px; - background: var(--code-bg) !important; border: 1px solid var(--border); } -pre * { - background: var(--code-bg) !important; +[data-theme="dark"] .astro-code, +[data-theme="dark"] .astro-code span { + color: var(--shiki-dark) !important; + background-color: var(--shiki-dark-bg) !important; } :not(pre) > code { @@ -133,7 +134,7 @@ pre * { white-space: nowrap; border: 1px solid var(--border); border-radius: 4px; - background: var(--code-bg) !important; + background: var(--code-bg); } .astro-code { From 7989ba0033549be0c13f3bf1fa2f9e7ae35bc528 Mon Sep 17 00:00:00 2001 From: Barrett Ruth Date: Sun, 9 Nov 2025 15:27:07 -0500 Subject: [PATCH 12/16] feat: run prettier on css --- package.json | 2 +- src/content/algorithms/proofs.mdx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index b361e7b..f934cdf 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "check": "astro check" }, "lint-staged": { - "*.{js,ts,jsx,tsx,astro,html,md,json,yml,yaml}": [ + "*.{js,ts,jsx,tsx,astro,html,md,json,yml,yaml,css}": [ "prettier --write", "astro check" ] diff --git a/src/content/algorithms/proofs.mdx b/src/content/algorithms/proofs.mdx index c770058..bdbc839 100644 --- a/src/content/algorithms/proofs.mdx +++ b/src/content/algorithms/proofs.mdx @@ -65,8 +65,8 @@ Let $X$ and $Y$ be the number of occurrences of zeroes and ones in the substring $$ \begin{align*} -\sum_{1\leq r\leq n}\sum_{1\leq l Date: Sun, 9 Nov 2025 15:58:25 -0500 Subject: [PATCH 13/16] fix(modelsofproduction): render graphs --- public/scripts/models-of-production.js | 18 ++-- public/styles/graph.css | 29 ++++--- .../algorithms/models-of-production.mdx | 84 +++++++++++++------ src/content/config.ts | 1 + src/layouts/PostLayout.astro | 3 +- 5 files changed, 88 insertions(+), 47 deletions(-) diff --git a/public/scripts/models-of-production.js b/public/scripts/models-of-production.js index 17a99fc..9448dec 100644 --- a/public/scripts/models-of-production.js +++ b/public/scripts/models-of-production.js @@ -629,18 +629,16 @@ function drawRomerSolowChangeGraph() { .text("log10(Y)"); } -document.addEventListener("DOMContentLoaded", function () { +drawSolowGraph(); +drawRomerGraph(); +drawRomerlGraph(); +drawRomerSolowGraph(); +drawRomerSolowChangeGraph(); + +window.onresize = () => { drawSolowGraph(); drawRomerGraph(); drawRomerlGraph(); drawRomerSolowGraph(); drawRomerSolowChangeGraph(); - - window.onresize = () => { - drawSolowGraph(); - drawRomerGraph(); - drawRomerlGraph(); - drawRomerSolowGraph(); - drawRomerSolowChangeGraph(); - }; -}); +}; diff --git a/public/styles/graph.css b/public/styles/graph.css index a6f8af3..114fb1c 100644 --- a/public/styles/graph.css +++ b/public/styles/graph.css @@ -27,7 +27,7 @@ appearance: none; width: 100%; height: 2px; - background: black; + background: var(--text); cursor: pointer; outline: none; transform: translateY(-50%); @@ -36,26 +36,29 @@ .slider input::-webkit-slider-thumb { -webkit-appearance: none; - width: 2px; - height: 15px; - background: black; - cursor: col-resize; + width: 12px; + height: 12px; + border-radius: 50%; + background: var(--text); + cursor: pointer; position: relative; } .slider input::-moz-range-thumb { - width: 2px; - height: 15px; - background: black; - cursor: col-resize; + width: 12px; + height: 12px; + border-radius: 50%; + background: var(--text); + cursor: pointer; position: relative; + border: none; } .slider input::-webkit-slider-runnable-track, .slider input::-moz-range-track { width: 100%; height: 2px; - background: black; + background: var(--text); border: none; } @@ -64,6 +67,10 @@ justify-content: center; } +.sliders li { + list-style: none; +} + .romer-table-container { display: flex; justify-content: center; @@ -79,7 +86,7 @@ #romer-table th, #romer-table td { - border: 1px solid black; + border: 1px solid var(--border); text-align: center; padding: 5px; } diff --git a/src/content/algorithms/models-of-production.mdx b/src/content/algorithms/models-of-production.mdx index c7cfe62..e9c7b31 100644 --- a/src/content/algorithms/models-of-production.mdx +++ b/src/content/algorithms/models-of-production.mdx @@ -14,7 +14,9 @@ This post offers a basic introduction to the Solow, Romer, and Romer-Solow econo The Solow Model is an economic model of production that incorporates the idea of capital accumulation. Based on the [Cobb-Douglas production function](https://en.wikipedia.org/wiki/Cobb%E2%80%93Douglas_production_function), the Solow Model describes production as follows: -$$Y_t=F(K_t,L_t)=\bar{A}K_t^\alpha L_t^{1-\alpha}$$ +$$ +Y_t=F(K_t,L_t)=\bar{A}K_t^\alpha L_t^{1-\alpha} +$$ With: @@ -41,7 +43,9 @@ Visualizing the model, namely output as a function of capital, provides helpful Letting $(L_t,\alpha)=(\bar{L}, \frac{1}{3})$, it follows that: -$$Y_t=F(K_t,L_t)=\bar{A}K_t^{\frac{1}{3}} \bar{L}^{\frac{2}{3}}$$ +$$ +Y_t=F(K_t,L_t)=\bar{A}K_t^{\frac{1}{3}} \bar{L}^{\frac{2}{3}} +$$ Utilizing this simplification and its graphical representation below, output is clearly characterized by the cube root of capital: @@ -119,17 +123,20 @@ When investment is completely disincentivized by depreciation (in other words, $ Using this equilibrium condition, it follows that: -$$Y_t^*=\bar{A}{K_t^*}^\alpha\bar{L}^{1-\alpha}$$ - -$$\rightarrow \bar{d}K_t^*=\bar{s}\bar{A}{K_t^*}^\alpha\bar{L}^{1-\alpha}$$ - -$$\rightarrow K^*=\bar{L}(\frac{\bar{s}\bar{A}}{\bar{d}})^\frac{1}{1-\alpha}$$ - -$$\rightarrow Y^*=\bar{A}^\frac{1}{1-\alpha}(\frac{\bar{s}}{\bar{d}})^\frac{\alpha}{1-\alpha}\bar{L}$$ +$$ +\begin{align*} +Y_t^* &= \bar{A}{K_t^*}^\alpha\bar{L}^{1-\alpha} \\ +\bar{d}K_t^* &= \bar{s}\bar{A}{K_t^*}^\alpha\bar{L}^{1-\alpha} \\ +K^* &= \bar{L}(\frac{\bar{s}\bar{A}}{\bar{d}})^\frac{1}{1-\alpha} \\ +Y^* &= \bar{A}^\frac{1}{1-\alpha}(\frac{\bar{s}}{\bar{d}})^\frac{\alpha}{1-\alpha}\bar{L} +\end{align*} +$$ Thus, the equilibrium intensive form (output per worker) of both capital and output are summarized as follows: -$$(k^*,y^*)=(\frac{K^*}{\bar{L}},\frac{Y^*}{\bar{L}}) =((\frac{\bar{s}\bar{A}}{\bar{d}})^\frac{1}{1-\alpha}, \bar{A}^\frac{1}{1-\alpha}(\frac{\bar{s}}{\bar{d}})^\frac{\alpha}{1-\alpha})$$ +$$ +(k^*,y^*)=(\frac{K^*}{\bar{L}},\frac{Y^*}{\bar{L}}) =((\frac{\bar{s}\bar{A}}{\bar{d}})^\frac{1}{1-\alpha}, \bar{A}^\frac{1}{1-\alpha}(\frac{\bar{s}}{\bar{d}})^\frac{\alpha}{1-\alpha}) +$$ ### analysis @@ -146,7 +153,9 @@ Using both mathematical intuition and manipulating the visualization above, we f Lastly (and perhaps most importantly), exogenous parameters $\bar{s}, \bar{d}$, and $\bar{A}$ all have immense ramifications on economic status. For example, comparing the difference in country $C_1$'s output versus $C_2$'s using the Solow Model, we find that a difference in economic performance can only be explained by these factors: -$$\frac{Y_1}{Y_2}=\frac{\bar{A_1}}{\bar{A_2}}(\frac{\bar{s_1}}{\bar{s_2}})^\frac{\alpha}{1-\alpha}$$ +$$ +\frac{Y_1}{Y_2}=\frac{\bar{A_1}}{\bar{A_2}}(\frac{\bar{s_1}}{\bar{s_2}})^\frac{\alpha}{1-\alpha} +$$ We see that TFP is more important in explaining the differences in per-capital output ($\frac{1}{1-\alpha}>\frac{\alpha}{1-\alpha},\alpha\in[0,1)$). Notably, the Solow Model does not give any insights into how to alter the most important predictor of output, TFP. @@ -167,7 +176,9 @@ The Model divides the world into two parts: The Romer Models' production function can be modelled as: -$$Y_t=F(A_t,L_{yt})=A_tL_{yt}$$ +$$ +Y_t=F(A_t,L_{yt})=A_tL_{yt} +$$ With: @@ -178,7 +189,9 @@ Assuming $L_t=\bar{L}$ people work in the economy, a proportion $\bar{l}$ of the Further, this economy garners ideas with time at rate $\bar{z}$: the "speed of ideas". Now, we can describe the quantity of ideas tomorrow as function of those of today: the Law of Ideal Motion (I made that up). -$$A_{t+1}=A_t+\bar{z}A_tL_{at}\leftrightarrow\Delta A_{t+1}=\bar{z}A_tL_{at}$$ +$$ +A_{t+1}=A_t+\bar{z}A_tL_{at}\leftrightarrow\Delta A_{t+1}=\bar{z}A_tL_{at} +$$ Analagously to capital in the solow model, ideas begin in the economy with some $\bar{A}_0\gt0$ and grow at an _exponential_ rate. At its core, this is because ideas are non-rivalrous; more ideas bring about more ideas. @@ -279,19 +292,27 @@ Playing with the sliders, this graph may seem underwhelming in comparison to the To find the output in terms of exogenous parameters, first note that -$$L_t=\bar{L}\rightarrow L_{yt}=(1-\bar{l})\bar{L}$$ +$$ +L_t=\bar{L}\rightarrow L_{yt}=(1-\bar{l})\bar{L} +$$ Now, all that remains is to find ideas $A_t$. It is assumed that ideas grow at some rate $g_A$: -$$A_t=A_0(1+g_A)^t$$ +$$ +A_t=A_0(1+g_A)^t +$$ Using the growth rate formula, we find: -$$g_A=\frac{\Delta A_{t+1}-A_t}{A_t}=\frac{A_t+\bar{z}A_tL_{at}-A_t}{A_t}=\bar{z}L_{at}=\bar{z}\bar{l}\bar{L}$$ +$$ +g_A=\frac{\Delta A_{t+1}-A_t}{A_t}=\frac{A_t+\bar{z}A_tL_{at}-A_t}{A_t}=\bar{z}L_{at}=\bar{z}\bar{l}\bar{L} +$$ Thus, ideas $A_t=A_0(1+\bar{z}\bar{l}\bar{L})^t$. Finally, output can be solved the production function: -$$Y_t=A_t L_{yt}=A_0(1+\bar{z}\bar{l}\bar{L})^t(1-\bar{l})\bar{L}$$ +$$ +Y_t=A_t L_{yt}=A_0(1+\bar{z}\bar{l}\bar{L})^t(1-\bar{l})\bar{L} +$$ ### analysis @@ -384,7 +405,9 @@ From previous analysis it was found that $g_A=\bar{z}\bar{l}\bar{L}$. Based on the Law of Capital Motion, -$$g_K=\frac{\Delta K_{t+1}}{K_t}=\bar{s}\frac{Y_t}{K_t}-\bar{d}$$ +$$ +g_K=\frac{\Delta K_{t+1}}{K_t}=\bar{s}\frac{Y_t}{K_t}-\bar{d} +$$ Because growth rates are constant on the Balanced Growth Path, $g_K$ must be constant as well. Thus, so is $\bar{s}\frac{Y_t}{K_t}-\bar{d}$; it must be that $g_K^*=g_Y^*$. @@ -392,24 +415,35 @@ The model assumes population is constant, so $g_{\bar{L}}=0\rightarrow g_{\bar{L Combining these terms, we find: -$$g_Y^*=\bar{z}\bar{l}\bar{L}+\alpha g_Y^*+(1-\alpha)\cdot 0\rightarrow$$ -$$g_Y^*=\frac{\bar{z}\bar{l}\bar{L}}{1-\alpha}$$ +$$ +\begin{align*} +g_Y^* &= \bar{z}\bar{l}\bar{L}+\alpha g_Y^*+(1-\alpha)\cdot 0 \\ +g_Y^* &= \frac{\bar{z}\bar{l}\bar{L}}{1-\alpha} +\end{align*} +$$ Solving for $Y_t^*$ is trivial after discovering $g_K=g_Y$ must hold on a balanced growth path. Invoking the Law of Capital Motion with magic chants, -$$g_K^*=\bar{s}\frac{Y_t^*}{K_t^*}-\bar{d}=g_Y^*\rightarrow K_t^*=\frac{\bar{s}Y_t^*}{g_Y^*+\bar{d}}$$ +$$ +g_K^*=\bar{s}\frac{Y_t^*}{K_t^*}-\bar{d}=g_Y^*\rightarrow K_t^*=\frac{\bar{s}Y_t^*}{g_Y^*+\bar{d}} +$$ Isolating $Y_t^*$, -$$Y_t^*=A_t^* (\frac{\bar{s}Y_t^*}{g_Y^*+\bar{d}})^\alpha ({(1-\bar{l})\bar{L}})^{1-\alpha}$$ - -$$\rightarrow {Y_t^*}^{1-\alpha}=A_t^*(\frac{\bar{s}}{g_Y^*+\bar{d}})^\alpha({(1-\bar{l})\bar{L}})^{1-\alpha}$$ +$$ +\begin{align*} +Y_t^* &= A_t^* (\frac{\bar{s}Y_t^*}{g_Y^*+\bar{d}})^\alpha ({(1-\bar{l})\bar{L}})^{1-\alpha} \\ +{Y_t^*}^{1-\alpha} &= A_t^*(\frac{\bar{s}}{g_Y^*+\bar{d}})^\alpha({(1-\bar{l})\bar{L}})^{1-\alpha} +\end{align*} +$$ Plugging in the known expressions for $A_t^*$ and $g_Y^*$, a final expression for the Balanced Growth Path output as a function of the endogenous parameters and time is obtained: -$$Y_t^*={(A_0(1+\bar{z}\bar{l}\bar{L})^t})^\frac{1}{1-\alpha}(\frac{\bar{s}}{\frac{\bar{z}\bar{l}\bar{L}}{1-\alpha}+\bar{d}})^\frac{\alpha}{1-\alpha}(1-\bar{l})\bar{L}$$ +$$ +Y_t^*={(A_0(1+\bar{z}\bar{l}\bar{L})^t})^\frac{1}{1-\alpha}(\frac{\bar{s}}{\frac{\bar{z}\bar{l}\bar{L}}{1-\alpha}+\bar{d}})^\frac{\alpha}{1-\alpha}(1-\bar{l})\bar{L} +$$ ### analysis diff --git a/src/content/config.ts b/src/content/config.ts index 3c6cdd1..9d4d776 100644 --- a/src/content/config.ts +++ b/src/content/config.ts @@ -6,6 +6,7 @@ const base = z.object({ date: z.string().optional(), useKatex: z.boolean().optional(), useD3: z.boolean().optional(), + scripts: z.array(z.string()).optional(), redirect: z.string().optional(), }); diff --git a/src/layouts/PostLayout.astro b/src/layouts/PostLayout.astro index 958b0f8..62b58cd 100644 --- a/src/layouts/PostLayout.astro +++ b/src/layouts/PostLayout.astro @@ -20,7 +20,7 @@ interface Props { } const { frontmatter, post } = Astro.props as Props; -const { title, description, useKatex = false } = frontmatter; +const { title, description, useKatex = false, useD3 = false } = frontmatter; let documentTitle = title; if (post?.collection === "git" && post?.slug) { @@ -49,6 +49,7 @@ const topicColor = getTopicColor(post?.collection); /> ) } + {useD3 && diff --git a/src/layouts/PostLayout.astro b/src/layouts/PostLayout.astro index 62b58cd..37af82e 100644 --- a/src/layouts/PostLayout.astro +++ b/src/layouts/PostLayout.astro @@ -53,7 +53,10 @@ const topicColor = getTopicColor(post?.collection); -
+

{title}

{frontmatter.date && } diff --git a/src/utils/colors.js b/src/utils/colors.js index 1d84326..587eb2c 100644 --- a/src/utils/colors.js +++ b/src/utils/colors.js @@ -11,6 +11,6 @@ export function getTopicColor(topicName) { case "git": return "#cc5500"; default: - return "#000000"; + return null; } } From a1d8f48a085f3af8f0ba03d543d3c2c82a64ab8c Mon Sep 17 00:00:00 2001 From: Barrett Ruth Date: Sun, 9 Nov 2025 16:40:02 -0500 Subject: [PATCH 15/16] fix(modelsofproduction): clean up graphs --- public/scripts/models-of-production.js | 99 +++++++++++++++++--------- 1 file changed, 64 insertions(+), 35 deletions(-) diff --git a/public/scripts/models-of-production.js b/public/scripts/models-of-production.js index 86be618..2e927ee 100644 --- a/public/scripts/models-of-production.js +++ b/public/scripts/models-of-production.js @@ -28,7 +28,7 @@ function setUpParameters(render, parameters, modelPrefix) { function drawSolowGraph() { const L = 150, K_MAX = 500, - margin = { top: 20, right: 30, bottom: 20, left: 50 }; + margin = { top: 80, right: 80, bottom: 60, left: 100 }; const [A, d, s, alpha] = setUpParameters( drawSolowGraph, @@ -55,27 +55,29 @@ function drawSolowGraph() { .attr("transform", `translate(${margin.left}, ${margin.top})`); const x = d3.scaleLinear().domain([0, K_MAX]).range([0, width]); - svg + const xAxis = svg .append("g") .attr("transform", `translate(0, ${height})`) - .call(d3.axisBottom(x)) + .call(d3.axisBottom(x)); + xAxis.selectAll("text").style("font-size", "1.5em"); + xAxis .append("text") .attr("x", width + 10) .attr("y", -10) .style("text-anchor", "end") - .style("font-size", "1.5em") + .style("font-size", "2em") .text("K"); const Y_MAX = solowOutput(K_MAX) + K_MAX / 10; const y = d3.scaleLinear().domain([0, Y_MAX]).range([height, 0]); - svg - .append("g") - .call(d3.axisLeft(y)) + const yAxis = svg.append("g").call(d3.axisLeft(y)); + yAxis.selectAll("text").style("font-size", "1.5em"); + yAxis .append("text") .attr("x", 0) .attr("y", -10) .style("text-anchor", "start") - .style("font-size", "1.5em") + .style("font-size", "2em") .text("Y"); const outputData = Array.from({ length: K_MAX }, (_, k) => ({ @@ -204,7 +206,7 @@ const updateRomerTable = (romerData) => { function drawRomerGraph() { const T_MAX = 100, - margin = { top: 20, right: 100, bottom: 20, left: 50 }; + margin = { top: 80, right: 120, bottom: 60, left: 100 }; const [z, L, l, A0] = setUpParameters( drawRomerGraph, @@ -237,29 +239,33 @@ function drawRomerGraph() { } const x = d3.scaleLinear().domain([1, T_MAX]).range([0, width]); - svg + const xAxis = svg .append("g") .attr("transform", `translate(0, ${height})`) - .call(d3.axisBottom(x)) + .call(d3.axisBottom(x)); + xAxis.selectAll("text").style("font-size", "1.5em"); + xAxis .append("text") .attr("x", width + 10) .attr("y", -10) .style("text-anchor", "end") - .style("font-size", "1.5em") + .style("font-size", "2em") .text("t"); const y = d3 .scaleLinear() .domain([0, romerData[romerData.length - 1].Y]) .range([height, 0]); - svg + const yAxis = svg .append("g") - .call(d3.axisLeft(y).ticks(10, d3.format(".1s"))) + .call(d3.axisLeft(y).ticks(10, d3.format(".1s"))); + yAxis.selectAll("text").style("font-size", "1.5em"); + yAxis .append("text") .attr("x", 0) .attr("y", -10) .style("text-anchor", "start") - .style("font-size", "1.5em") + .style("font-size", "2em") .text("log(Y)"); svg @@ -291,7 +297,7 @@ function drawRomerlGraph() { z = 0.01, L = 50, A0 = 50, - margin = { top: 20, right: 100, bottom: 20, left: 50 }; + margin = { top: 80, right: 120, bottom: 60, left: 100 }; const [l, t0] = setUpParameters(drawRomerlGraph, ["lChange", "t0"], ""); @@ -328,29 +334,33 @@ function drawRomerlGraph() { } const x = d3.scaleLinear().domain([1, T_MAX]).range([0, width]); - svg + const xAxis = svg .append("g") .attr("transform", `translate(0, ${height})`) - .call(d3.axisBottom(x)) + .call(d3.axisBottom(x)); + xAxis.selectAll("text").style("font-size", "1.5em"); + xAxis .append("text") .attr("x", width + 10) .attr("y", -10) .style("text-anchor", "end") - .style("font-size", "1.5em") + .style("font-size", "2em") .text("t"); const y = d3 .scaleLinear() .domain([0, romerData[romerData.length - 1].Y]) .range([height, 0]); - svg + const yAxis = svg .append("g") - .call(d3.axisLeft(y).ticks(10, d3.format(".1s"))) + .call(d3.axisLeft(y).ticks(10, d3.format(".1s"))); + yAxis.selectAll("text").style("font-size", "1.5em"); + yAxis .append("text") .attr("x", 0) .attr("y", -10) .style("text-anchor", "start") - .style("font-size", "1.5em") + .style("font-size", "2em") .text("log(Y)"); svg @@ -437,7 +447,7 @@ function calculateRomerSolowData( function drawRomerSolowGraph() { const T_MAX = 100, - margin = { top: 20, right: 100, bottom: 20, left: 50 }; + margin = { top: 80, right: 120, bottom: 60, left: 100 }; const [z, l, L, A0, s, d, alpha] = setUpParameters( drawRomerSolowGraph, @@ -471,29 +481,33 @@ function drawRomerSolowGraph() { ); const x = d3.scaleLinear().domain([1, T_MAX]).range([0, width]); - svg + const xAxis = svg .append("g") .attr("transform", `translate(0, ${height})`) - .call(d3.axisBottom(x)) + .call(d3.axisBottom(x)); + xAxis.selectAll("text").style("font-size", "1.5em"); + xAxis .append("text") .attr("x", width + 10) .attr("y", -10) .style("text-anchor", "end") - .style("font-size", "1.5em") + .style("font-size", "2em") .text("t"); const y = d3 .scaleLinear() .domain([0, romerSolowData[romerSolowData.length - 1].Y]) .range([height, 0]); - svg + const yAxis = svg .append("g") - .call(d3.axisLeft(y).ticks(10, d3.format(".1s"))) + .call(d3.axisLeft(y).ticks(10, d3.format(".1s"))); + yAxis.selectAll("text").style("font-size", "1.5em"); + yAxis .append("text") .attr("x", 0) .attr("y", -10) .style("text-anchor", "start") - .style("font-size", "1.5em") + .style("font-size", "2em") .text("log(Y)"); svg @@ -566,29 +580,33 @@ function drawRomerSolowChangeGraph() { ); const x = d3.scaleLinear().domain([1, T_MAX]).range([0, width]); - svg + const xAxis = svg .append("g") .attr("transform", `translate(0, ${height})`) - .call(d3.axisBottom(x)) + .call(d3.axisBottom(x)); + xAxis.selectAll("text").style("font-size", "1.5em"); + xAxis .append("text") .attr("x", width + 10) .attr("y", -10) .style("text-anchor", "end") - .style("font-size", "1.5em") + .style("font-size", "2em") .text("t"); const y = d3 .scaleLinear() .domain([0, romerSolowData[romerSolowData.length - 1].Y]) .range([height, 0]); - svg + const yAxis = svg .append("g") - .call(d3.axisLeft(y).ticks(10, d3.format(".1s"))) + .call(d3.axisLeft(y).ticks(10, d3.format(".1s"))); + yAxis.selectAll("text").style("font-size", "1.5em"); + yAxis .append("text") .attr("x", 0) .attr("y", -10) .style("text-anchor", "start") - .style("font-size", "1.5em") + .style("font-size", "2em") .text("log(Y)"); svg @@ -635,3 +653,14 @@ window.onresize = () => { drawRomerSolowGraph(); drawRomerSolowChangeGraph(); }; + +new MutationObserver(() => { + drawSolowGraph(); + drawRomerGraph(); + drawRomerlGraph(); + drawRomerSolowGraph(); + drawRomerSolowChangeGraph(); +}).observe(document.documentElement, { + attributes: true, + attributeFilter: ["data-theme"], +}); From bd746099033e48421ce0878aa1fdace518c04dc5 Mon Sep 17 00:00:00 2001 From: Barrett Ruth Date: Sun, 9 Nov 2025 16:40:34 -0500 Subject: [PATCH 16/16] cleanup --- public/scripts/models-of-production.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/public/scripts/models-of-production.js b/public/scripts/models-of-production.js index 2e927ee..1f0f768 100644 --- a/public/scripts/models-of-production.js +++ b/public/scripts/models-of-production.js @@ -28,7 +28,7 @@ function setUpParameters(render, parameters, modelPrefix) { function drawSolowGraph() { const L = 150, K_MAX = 500, - margin = { top: 80, right: 80, bottom: 60, left: 100 }; + margin = { top: 50, right: 55, bottom: 40, left: 75 }; const [A, d, s, alpha] = setUpParameters( drawSolowGraph, @@ -206,7 +206,7 @@ const updateRomerTable = (romerData) => { function drawRomerGraph() { const T_MAX = 100, - margin = { top: 80, right: 120, bottom: 60, left: 100 }; + margin = { top: 50, right: 110, bottom: 40, left: 75 }; const [z, L, l, A0] = setUpParameters( drawRomerGraph, @@ -297,7 +297,7 @@ function drawRomerlGraph() { z = 0.01, L = 50, A0 = 50, - margin = { top: 80, right: 120, bottom: 60, left: 100 }; + margin = { top: 50, right: 110, bottom: 40, left: 75 }; const [l, t0] = setUpParameters(drawRomerlGraph, ["lChange", "t0"], ""); @@ -447,7 +447,7 @@ function calculateRomerSolowData( function drawRomerSolowGraph() { const T_MAX = 100, - margin = { top: 80, right: 120, bottom: 60, left: 100 }; + margin = { top: 50, right: 110, bottom: 40, left: 75 }; const [z, l, L, A0, s, d, alpha] = setUpParameters( drawRomerSolowGraph,