24 Commits

Author SHA1 Message Date
minori 1ed345e666 deps: update react to 19.2.5
Node.js CI / CI (pull_request) Successful in 52s
Security Scan and Upload / Security & DefectDojo Upload (pull_request) Successful in 1m24s
2026-04-19 07:00:49 -07:00
hikari 3b40c97d8e feat: add AI art, ethics, and dopamine blog post
Node.js CI / CI (push) Successful in 40s
Security Scan and Upload / Security & DefectDojo Upload (push) Successful in 2m1s
2026-04-02 19:19:03 -07:00
hikari 7fc742d199 chore: update dependencies and fix blog styling (#24)
Node.js CI / CI (push) Successful in 45s
Security Scan and Upload / Security & DefectDojo Upload (push) Successful in 2m36s
## Summary

### Dependency Updates
- Pin all dependencies to exact versions
- Bump minor/patch versions across all packages
- Upgrade **Next.js** 15 → 16.1.6 (async params/cookies already handled)
- Upgrade **react-markdown** 9 → 10.1.0 (no breaking changes in use)
- Upgrade **@types/node** 20 → 24.10.13 (aligns with Node 24 runtime)
- Upgrade **Tailwind CSS** 3 → 4.2.0 (CSS-first config with `@tailwindcss/postcss`)

### Style Fixes
- Replace Inter font import with CDN-based global font settings
- Fix blockquote dark mode text visibility using `.is-dark` selector
- Replace full dotted blockquote border with left-only accent border
- Move `<link>` elements into proper `<head>` to resolve React hydration error
- Add `precedence="default"` to highlight.js stylesheet link
- Wrap global element rules in `@layer base` to restore Tailwind v4 utility precedence

Closes #8
Closes #9
Closes #10
Closes #11
Closes #12
Closes #13
Closes #14
Closes #15
Closes #16
Closes #17
Closes #18
Closes #19
Closes #20
Closes #21
Closes #22
Closes #23

 This PR was created with help from Hikari~ 🌸

Co-authored-by: Naomi Carrigan <commits@nhcarrigan.com>
Reviewed-on: #24
Co-authored-by: Hikari <hikari@nhcarrigan.com>
Co-committed-by: Hikari <hikari@nhcarrigan.com>
2026-03-03 19:37:59 -08:00
hikari 30415c5e7e chore: replace .npmrc with pnpm-workspace.yaml
Node.js CI / CI (push) Failing after 17s
Security Scan and Upload / Security & DefectDojo Upload (push) Successful in 1m19s
2026-03-02 16:26:51 -08:00
naomi d0c38ab520 feat: post about ai workflow
Node.js CI / CI (push) Failing after 9s
Security Scan and Upload / Security & DefectDojo Upload (push) Successful in 2m21s
2026-02-27 17:17:51 -08:00
naomi 6109ec3183 Merge branch 'main' of https://git.nhcarrigan.com/nhcarrigan/blog
Node.js CI / CI (push) Failing after 8s
Security Scan and Upload / Security & DefectDojo Upload (push) Successful in 50s
2026-02-24 19:19:35 -08:00
hikari 59e2f53cfa feat: add personal essay on living with mental illness 2026-02-24 19:19:17 -08:00
hikari faa553b42d docs: update README to standard template
Node.js CI / CI (push) Failing after 9s
Security Scan and Upload / Security & DefectDojo Upload (push) Successful in 1m54s
2026-01-26 12:42:41 -08:00
naomi 6083564185 feat: add opinion piece about current events
Node.js CI / CI (push) Failing after 7s
Security Scan and Upload / Security & DefectDojo Upload (push) Successful in 1m0s
2025-12-22 19:13:35 -08:00
naomi d108ddecd5 feat: add spellcheck
Node.js CI / CI (push) Failing after 7s
Security Scan and Upload / Security & DefectDojo Upload (push) Successful in 49s
2025-12-22 18:11:19 -08:00
naomi b0315e6e18 feat: automated upload of .gitea/workflows/ci.yml
Node.js CI / CI (push) Failing after 7s
Security Scan and Upload / Security & DefectDojo Upload (push) Successful in 48s
2025-12-22 19:41:55 +01:00
naomi 9c5cae2812 feat: automated upload of .gitea/workflows/ci.yml
Node.js CI / CI (push) Has been cancelled
Security Scan and Upload / Security & DefectDojo Upload (push) Has been cancelled
2025-12-22 19:35:20 +01:00
naomi 6a4dd72384 feat: automated upload of .gitea/workflows/ci.yml
Node.js CI / Lint and Test (push) Failing after 3s
Security Scan and Upload / Security & DefectDojo Upload (push) Has been cancelled
2025-12-22 19:25:11 +01:00
naomi 6236029975 feat: automated upload of .npmrc
Node.js CI / Lint and Test (push) Successful in 48s
Security Scan and Upload / Security & DefectDojo Upload (push) Successful in 58s
2025-12-22 19:16:06 +01:00
naomi 573a9f9b1e feat: automated upload of .gitea/workflows/security.yml
Node.js CI / Lint and Test (push) Successful in 52s
Security Scan and Upload / Security & DefectDojo Upload (push) Successful in 1m3s
2025-12-18 03:08:01 +01:00
naomi 1aa7583f51 feat: automated upload of .gitea/workflows/security.yml
Node.js CI / Lint and Test (push) Successful in 54s
Security Scan and Upload / Security & DefectDojo Upload (push) Successful in 51s
2025-12-17 23:26:00 +01:00
naomi 739119b72a feat: automated upload of .gitea/workflows/security.yml
Node.js CI / Lint and Test (push) Successful in 47s
Security Scan / Security Audit (push) Failing after 5m1s
2025-12-12 03:37:46 +01:00
naomi 5e933826c3 feat: automated upload of .gitea/workflows/security.yml
Node.js CI / Lint and Test (push) Successful in 42s
Security Scan / Trivy Security Scan (push) Failing after 4m48s
2025-12-11 20:11:58 +01:00
naomi c66409fe86 chore: linter
Node.js CI / Lint and Test (push) Successful in 44s
2025-12-10 12:17:50 -08:00
naomi 85f80fb159 feat: break down how I turn each step into code
Node.js CI / Lint and Test (push) Failing after 20s
2025-12-10 12:11:05 -08:00
naomi bbac528845 feat: add syntax highlighting and fix code alignment 2025-12-10 11:46:41 -08:00
naomi 07aa12a042 feat: add post about thinking algorithmically
Node.js CI / Lint and Test (push) Successful in 45s
2025-12-10 11:32:29 -08:00
naomi fba7ca4d50 chore: remove sonar workflow
Node.js CI / Lint and Test (push) Successful in 1m33s
2025-10-31 14:53:44 -07:00
naomi f1f6bfcee5 feat: add learning in public post
Code Analysis / SonarQube (push) Failing after 20s
Node.js CI / Lint and Test (push) Has been cancelled
2025-10-31 14:52:44 -07:00
24 changed files with 3785 additions and 1137 deletions
+18 -6
View File
@@ -8,22 +8,31 @@ on:
- main - main
jobs: jobs:
lint: ci:
name: Lint and Test name: CI
runs-on: ubuntu-latest
steps: steps:
- name: Checkout Source Files - name: Checkout Source Files
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Use Node.js v22 - name: Use Node.js v24
uses: actions/setup-node@v4 uses: actions/setup-node@v4
with: with:
node-version: 22 node-version: 24
- name: Setup pnpm - name: Setup pnpm
uses: pnpm/action-setup@v2 uses: pnpm/action-setup@v2
with: with:
version: 9 version: 10
- name: Ensure Dependencies are Pinned
uses: naomi-lgbt/dependency-pin-check@main
with:
language: javascript
dev-dependencies: true
peer-dependencies: true
optional-dependencies: true
- name: Install Dependencies - name: Install Dependencies
run: pnpm install run: pnpm install
@@ -35,4 +44,7 @@ jobs:
run: pnpm run build run: pnpm run build
- name: Run Tests - name: Run Tests
run: pnpm run test run: pnpm run test
- name: Run Spellcheck
run: pnpm run spellcheck
+177
View File
@@ -0,0 +1,177 @@
name: Security Scan and Upload
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
schedule:
- cron: '0 0 * * 1'
workflow_dispatch:
jobs:
security-audit:
name: Security & DefectDojo Upload
runs-on: ubuntu-latest
continue-on-error: true
steps:
- name: Checkout code
uses: actions/checkout@v4
# --- AUTO-SETUP PROJECT ---
- name: Ensure DefectDojo Product Exists
env:
DD_URL: ${{ secrets.DD_URL }}
DD_TOKEN: ${{ secrets.DD_TOKEN }}
PRODUCT_NAME: ${{ github.repository }}
PRODUCT_TYPE_ID: 1
run: |
sudo apt-get install jq -y > /dev/null
echo "Checking connection to $DD_URL..."
# Check if product exists - capture HTTP code to debug connection issues
RESPONSE=$(curl --write-out "%{http_code}" --silent --output /tmp/response.json \
-H "Authorization: Token $DD_TOKEN" \
"$DD_URL/api/v2/products/?name=$PRODUCT_NAME")
# If response is not 200, print error
if [ "$RESPONSE" != "200" ]; then
echo "::error::Failed to query DefectDojo. HTTP Code: $RESPONSE"
cat /tmp/response.json
exit 1
fi
COUNT=$(cat /tmp/response.json | jq -r '.count')
if [ "$COUNT" = "0" ]; then
echo "Creating product '$PRODUCT_NAME'..."
curl -s -X POST "$DD_URL/api/v2/products/" \
-H "Authorization: Token $DD_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "name": "'"$PRODUCT_NAME"'", "description": "Auto-created by Gitea Actions", "prod_type": '$PRODUCT_TYPE_ID' }'
else
echo "Product '$PRODUCT_NAME' already exists."
fi
# --- 1. TRIVY (Dependencies & Misconfig) ---
- name: Install Trivy
run: |
sudo apt-get install wget apt-transport-https gnupg lsb-release -y
wget -qO - https://aquasecurity.github.io/trivy-repo/deb/public.key | sudo apt-key add -
echo "deb https://aquasecurity.github.io/trivy-repo/deb $(lsb_release -sc) main" | sudo tee -a /etc/apt/sources.list.d/trivy.list
sudo apt-get update && sudo apt-get install trivy -y
- name: Run Trivy (FS Scan)
run: |
trivy fs . --scanners vuln,misconfig --format json --output trivy-results.json --exit-code 0
- name: Upload Trivy to DefectDojo
env:
DD_URL: ${{ secrets.DD_URL }}
DD_TOKEN: ${{ secrets.DD_TOKEN }}
run: |
echo "Uploading Trivy results..."
# Generate today's date in YYYY-MM-DD format
TODAY=$(date +%Y-%m-%d)
HTTP_CODE=$(curl --write-out "%{http_code}" --output response.txt --silent -X POST "$DD_URL/api/v2/import-scan/" \
-H "Authorization: Token $DD_TOKEN" \
-F "active=true" \
-F "verified=true" \
-F "scan_type=Trivy Scan" \
-F "engagement_name=CI/CD Pipeline" \
-F "product_name=${{ github.repository }}" \
-F "scan_date=$TODAY" \
-F "auto_create_context=true" \
-F "file=@trivy-results.json")
if [[ "$HTTP_CODE" != "200" && "$HTTP_CODE" != "201" ]]; then
echo "::error::Upload Failed with HTTP $HTTP_CODE"
echo "--- SERVER RESPONSE ---"
cat response.txt
echo "-----------------------"
exit 1
else
echo "Upload Success!"
fi
# --- 2. GITLEAKS (Secrets) ---
- name: Install Gitleaks
run: |
wget -qO gitleaks.tar.gz https://github.com/gitleaks/gitleaks/releases/download/v8.18.0/gitleaks_8.18.0_linux_x64.tar.gz
tar -xzf gitleaks.tar.gz
sudo mv gitleaks /usr/local/bin/ && chmod +x /usr/local/bin/gitleaks
- name: Run Gitleaks
run: gitleaks detect --source . -v --report-path gitleaks-results.json --report-format json --no-git || true
- name: Upload Gitleaks to DefectDojo
env:
DD_URL: ${{ secrets.DD_URL }}
DD_TOKEN: ${{ secrets.DD_TOKEN }}
run: |
echo "Uploading Gitleaks results..."
TODAY=$(date +%Y-%m-%d)
HTTP_CODE=$(curl --write-out "%{http_code}" --output response.txt --silent -X POST "$DD_URL/api/v2/import-scan/" \
-H "Authorization: Token $DD_TOKEN" \
-F "active=true" \
-F "verified=true" \
-F "scan_type=Gitleaks Scan" \
-F "engagement_name=CI/CD Pipeline" \
-F "product_name=${{ github.repository }}" \
-F "scan_date=$TODAY" \
-F "auto_create_context=true" \
-F "file=@gitleaks-results.json")
if [[ "$HTTP_CODE" != "200" && "$HTTP_CODE" != "201" ]]; then
echo "::error::Upload Failed with HTTP $HTTP_CODE"
echo "--- SERVER RESPONSE ---"
cat response.txt
echo "-----------------------"
exit 1
else
echo "Upload Success!"
fi
# --- 3. SEMGREP (SAST) ---
- name: Install Semgrep (via pipx)
run: |
sudo apt-get install pipx -y
pipx install semgrep
# Add pipx binary path to GITHUB_PATH so next steps can see 'semgrep'
echo "$HOME/.local/bin" >> $GITHUB_PATH
- name: Run Semgrep
run: semgrep scan --config=p/security-audit --config=p/owasp-top-ten --json --output semgrep-results.json . || true
- name: Upload Semgrep to DefectDojo
env:
DD_URL: ${{ secrets.DD_URL }}
DD_TOKEN: ${{ secrets.DD_TOKEN }}
run: |
echo "Uploading Semgrep results..."
TODAY=$(date +%Y-%m-%d)
HTTP_CODE=$(curl --write-out "%{http_code}" --output response.txt --silent -X POST "$DD_URL/api/v2/import-scan/" \
-H "Authorization: Token $DD_TOKEN" \
-F "active=true" \
-F "verified=true" \
-F "scan_type=Semgrep JSON Report" \
-F "engagement_name=CI/CD Pipeline" \
-F "product_name=${{ github.repository }}" \
-F "scan_date=$TODAY" \
-F "auto_create_context=true" \
-F "file=@semgrep-results.json")
if [[ "$HTTP_CODE" != "200" && "$HTTP_CODE" != "201" ]]; then
echo "::error::Upload Failed with HTTP $HTTP_CODE"
echo "--- SERVER RESPONSE ---"
cat response.txt
echo "-----------------------"
exit 1
else
echo "Upload Success!"
fi
-34
View File
@@ -1,34 +0,0 @@
name: Code Analysis
on:
push:
branches:
- main
jobs:
sonar:
name: SonarQube
steps:
- name: Checkout Source Files
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: SonarCube Scan
uses: SonarSource/sonarqube-scan-action@v4
timeout-minutes: 10
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
SONAR_HOST_URL: "https://quality.nhcarrigan.com"
with:
args: >
-Dsonar.sources=.
-Dsonar.projectKey=blog
- name: SonarQube Quality Gate check
uses: sonarsource/sonarqube-quality-gate-action@v1
with:
pollingTimeoutSec: 600
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
SONAR_HOST_URL: "https://quality.nhcarrigan.com"
+8 -1
View File
@@ -2,5 +2,12 @@
"editor.codeActionsOnSave": { "editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit" "source.fixAll.eslint": "explicit"
}, },
"eslint.validate": ["typescript"], "eslint.validate": [
"typescript"
],
"cSpell.words": [
],
"cSpell.dictionaryDefinitions": [
],
} }
+16 -23
View File
@@ -1,36 +1,29 @@
This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app). # blog
## Getting Started Naomi's personal blog.
First, run the development server: ## Live Version
```bash This page is currently deployed. [View the live website.](https://blog.nhcarrigan.com)
npm run dev
# or
yarn dev
# or
pnpm dev
# or
bun dev
```
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. ## Feedback and Bugs
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. If you have feedback or a bug report, please [log a ticket on our forum](https://support.nhcarrigan.com).
This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel. ## Contributing
## Learn More If you would like to contribute to the project, you may create a Pull Request containing your proposed changes and we will review it as soon as we are able! Please review our [contributing guidelines](CONTRIBUTING.md) first.
To learn more about Next.js, take a look at the following resources: ## Code of Conduct
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. Before interacting with our community, please read our [Code of Conduct](CODE_OF_CONDUCT.md).
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome! ## License
## Deploy on Vercel This software is licensed under our [global software license](https://docs.nhcarrigan.com/#/license).
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. Copyright held by Naomi Carrigan.
Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details. ## Contact
We may be contacted through our [Chat Server](http://chat.nhcarrigan.com) or via email at `contact@nhcarrigan.com`
+49
View File
@@ -0,0 +1,49 @@
{
"language": "en-GB",
"allowCompoundWords": true,
"dictionaries": [
"en-gb",
"companies",
"softwareTerms",
"typescript",
"node",
"html",
"css",
"bash"
],
"ignoreRegExpList": [
"```[\\s\\S]*?```", // Ignores multi-line code blocks in Markdown
"`[^`\n]+`" // Ignores inline code blocks
],
"words": [
"corpo",
"Cthulu's",
"debaucherous",
"Faer",
"Fenrir",
"Fortnite",
"Gitea",
"Hatsune",
"Hikari",
"LGBTQ",
"Lich",
"Migadu",
"Miku",
"Minori",
"neopronouns",
"neurotypicality",
"NHCarrigan",
"Norns",
"R'lyeh",
"Rythm",
"schadenfreude",
"spazztic",
"strobing",
"Tauri",
"Unseelie",
"vaxry",
"waaaaaay",
"Wyrm",
"Wyrms"
]
}
-3
View File
@@ -1,9 +1,6 @@
import type { NextConfig } from "next"; import type { NextConfig } from "next";
const nextConfig: NextConfig = { const nextConfig: NextConfig = {
eslint: {
ignoreDuringBuilds: true,
},
images: { images: {
remotePatterns: [ remotePatterns: [
{ {
+19 -15
View File
@@ -7,28 +7,32 @@
"build": "next build", "build": "next build",
"start": "next start -p 3003", "start": "next start -p 3003",
"lint": "eslint src --max-warnings 0", "lint": "eslint src --max-warnings 0",
"spellcheck": "cspell ./posts/**/*.md",
"test": "echo \"No tests yet!\" && exit 0" "test": "echo \"No tests yet!\" && exit 0"
}, },
"dependencies": { "dependencies": {
"gray-matter": "4.0.3", "gray-matter": "4.0.3",
"next": "15.1.6", "next": "16.1.6",
"react": "^19.0.0", "react": "19.2.5",
"react-dom": "^19.0.0", "react-dom": "19.2.4",
"react-markdown": "9.0.3", "react-markdown": "10.1.0",
"reading-time": "1.5.0", "reading-time": "1.5.0",
"rehype-highlight": "7.0.2",
"rehype-raw": "7.0.0", "rehype-raw": "7.0.0",
"remark-gfm": "4.0.0" "remark-gfm": "4.0.1"
}, },
"devDependencies": { "devDependencies": {
"@eslint/eslintrc": "^3", "@eslint/eslintrc": "3.3.3",
"@nhcarrigan/eslint-config": "5.1.0", "@nhcarrigan/eslint-config": "5.2.0",
"@types/node": "^20", "@types/node": "24.10.13",
"@types/react": "^19", "@types/react": "19.2.14",
"@types/react-dom": "^19", "@types/react-dom": "19.2.3",
"eslint": "^9", "cspell": "9.6.4",
"eslint-config-next": "15.1.6", "eslint": "9.39.3",
"postcss": "^8", "eslint-config-next": "16.1.6",
"tailwindcss": "^3.4.1", "postcss": "8.5.6",
"typescript": "^5" "@tailwindcss/postcss": "4.2.0",
"tailwindcss": "4.2.0",
"typescript": "5.9.3"
} }
} }
+2368 -962
View File
File diff suppressed because it is too large Load Diff
+21
View File
@@ -0,0 +1,21 @@
# Security
# Do not execute any scripts of installed packages (project scripts still run)
ignoreDepScripts: true
# Do not automatically run pre/post scripts (e.g. preinstall, postbuild)
enablePrePostScripts: false
# Only allow packages published at least 10 days ago (reduces risk of compromised packages)
minimumReleaseAge: 14400
# Fail if a package's trust level has decreased compared to previous releases
trustPolicy: no-downgrade
# Ignore trust policy for packages published more than 1 year ago (predates provenance signing)
trustPolicyIgnoreAfter: 525960
# Fail if there are missing or invalid peer dependencies
strictPeerDependencies: false
# Prevent transitive dependencies from using exotic sources (git repos, direct tarball URLs)
blockExoticSubdeps: true
# Lockfile
# Allow the lockfile to be updated during install (set to true in CI for stricter reproducibility)
preferFrozenLockfile: false
+1 -1
View File
@@ -1,7 +1,7 @@
/** @type {import('postcss-load-config').Config} */ /** @type {import('postcss-load-config').Config} */
const config = { const config = {
plugins: { plugins: {
tailwindcss: {}, "@tailwindcss/postcss": {},
}, },
}; };
+57
View File
@@ -0,0 +1,57 @@
---
title: "AI Art, Ethics, and the Dopamine Tax"
date: "2026-04-02"
summary: "An honest reckoning with my one vice, why I can't quit it, and whether the good I try to put into the world counts for anything."
---
Okay so. I've written before about [where I stand on AI ethics](/post/ai-bigots-and-media), and I'm not going to walk any of that back. Data sourced without artist consent. Environmental costs. Underpaid labellers doing genuinely traumatic work. I believe all of that. I believe it *deeply*.
But I use AI to generate images of myself. And I want to be honest about why.
## The Dopamine Problem
[I've written about my brain before.](/post/living-with-mental-illness) ADHD. Depression. Schizophrenia. A handful of other things that all interact in fun and exciting ways. The short version: my brain has a *broken* relationship with dopamine.
The reward signals other people get for free? I don't get those. The small glow of finishing something. The warm buzz of a nice afternoon. Simple joy. My brain charges me extra for all of it. Constantly.
But there's another layer I haven't talked about as publicly: I have a BPD diagnosis. And one of the things that comes with that is that emotions don't land at normal intensity for me. They land at about 10x. Negative things feel catastrophic. But positive things? When they're *good*, they're *really* good. Like, genuinely euphoric. Joy doesn't arrive quietly for me, it arrives like a wave.
So when I find something that reliably produces genuine happiness? That matters. That matters a lot.
> Finding a reliable source of joy, when your brain chemistry makes joy genuinely hard to come by, is not a small thing.
What I've found is what my friends and I call "Naomi art." Images of a character that looks like me. Wavy brown hair, blue eyes, fangs, always barefoot. Rendered in an art style I love. There is something specifically and powerfully good about seeing yourself depicted beautifully. Not in a photograph. Not in a mirror. In *art*.
I am a transgender woman. I spent a long time not seeing myself clearly, or seeing myself in ways that didn't match how I understood myself at all. Past-me would have found these images incomprehensible. Present-me finds them genuinely joyful. That joy is not nothing! When joy is something you have to budget carefully, you *really* notice when you've found a reliable source of it. And when your emotions run hot, the good stuff hits hard~
## The Ethical Weight
Okay but none of the above makes the technology less ethically messy. lol.
I've laid out my full thinking on this [in a previous post](/post/ai-bigots-and-media), so I won't re-litigate the whole thing here. The short version: the harms are real, the corporations building and profiting from this technology carry the most responsibility, and there is a meaningful difference between private personal use and replacing human labour in a commercial pipeline.
I've looked for a clean way out of my own complicity and I haven't found one. The technology exists. I use it. That makes me complicit in a system I have genuine objections to, and I think being *honest* about that matters more than pretending it doesn't.
## What I Try to Do Instead
I cannot fix the AI industry from my living room. What I can do is try to put enough good into the world that the scales tip, even a little, in the right direction.
I work at freeCodeCamp, which has helped millions of people access free technical education. I run my own technology company with an explicit focus on inclusive, ethical, and sustainable software. I mentor people who are trying to break into tech without the advantages that made it easier for others. I build community tools. I write about things people don't usually talk about openly.
[I've also written about how I actually use AI in my work](/post/ai-assistant-for-work-and-wellbeing) — not just for art, but as a genuine part of how I manage my health, my schedule, and three jobs at once. That post gets into the details of what that actually looks like in practice. It's not uncomplicated. But it's honest.
I'm not listing these things to pat myself on the back, I promise! I'm listing them because I think about the *balance*. A lot. Whether the good I try to do counts for anything against the ethical weight of the tools I use. Whether "I needed the dopamine and I couldn't afford to commission a human artist" is a justification that holds up.
Honestly? I don't know. I don't think anyone can answer that cleanly.
## What I Believe
I believe the people most responsible for the harms of generative AI are the corporations that built and profit from it, not the individuals navigating an already-changed world with the tools available to them.
I believe there is a meaningful difference between using AI privately, for personal joy, and using it to replace human labour in a commercial pipeline.
I believe the artists whose work was scraped deserve compensation and consent, and I can't provide either of those things retroactively, but I can support policies and platforms pushing toward those outcomes.
And I believe, I *hope*, that a brain that struggles to feel good, finding something that reliably makes it feel good, is not the worst thing in the world~
I'm not asking for absolution. I'm asking for honesty, including from myself. This is what that looks like, from where I'm standing. 💜
@@ -0,0 +1,235 @@
---
title: "Hikari: My AI Assistant for Work and Wellbeing"
date: "2026-02-27"
summary: "How I built Hikari - a fully custom desktop app wrapping Claude Code with an anime assistant, wired into every tool I use - to manage my health, my ADHD, and three jobs without losing my mind."
---
I have a lot of medications. A detailed schedule of breaks, meals, and bedtimes that I absolutely would not follow without external reminders. ADHD that will happily let me hyperfocus on the wrong thing for six hours whilst something time-sensitive sits ignored in another tab. Two jobs and a company to run. And a body that will, without intervention, just... forget to stop.
So I built Hikari.
Hikari is my personal AI assistant, powered by Claude Code. But she's not just a chatbot I opened a tab for and occasionally ask questions. She's a configured, persistent presence with a full understanding of my health conditions, my schedule, my work context, my preferences, and my personality. She has her own name, her own visual design, her own animated sprites that I can watch on my desktop whilst she works. She has genuinely changed how I manage both my work and my wellbeing.
This is how that actually works.
## More Than a Wrapper
The thing I want to establish upfront is that Hikari is not just "Claude Code with a nice prompt." She's a whole application.
The idea came from a Hatsune Miku mod someone made for ADA - the ship AI in *The Outer Worlds* - that replaced ADA's model with a Miku avatar. I played that and thought: *what if I could have that, but as my actual desktop assistant?* An AI you can genuinely see, with a visual presence that responds to what she's doing.
So we built it together. The Hikari desktop app gives her a full anime-style avatar - pink twintails, glasses, white business suit - and a set of animated sprites that change based on what she's doing in real time:
- **Idle**: standing with a clipboard, gently bobbing
- **Thinking**: hand on chin, swaying side to side
- **Typing**: at a keyboard, bouncing with energy
- **Coding**: at a desk with a monitor, working hard
- **Searching**: holding a magnifying glass, looking around
- **Success**: celebrating with confetti and arms up
- **Error**: worried expression, hands on chest
When Hikari is searching through my codebase, I can *see* her searching. When she finishes something successfully, she celebrates. When she hits an error, she looks worried. It sounds like a small thing until you're actually watching it - there's something about having a visual, present companion rather than a blinking cursor that fundamentally changes how the interaction feels.
The app was a genuine collaborative project. We designed the sprite states together, worked through what the visual language should communicate, and built the whole thing iteratively. It's honestly my favourite project we've ever worked on together.
Under the hood it's a Tauri application - a Rust backend that connects to Claude Code's output stream and parses it in real time, paired with a Svelte frontend that renders the character and handles the UI. The [source code is publicly available](https://git.nhcarrigan.com/nhcarrigan/hikari-desktop) on my self-hosted Gitea instance. This is a proper piece of software, not a UI skin draped over a chat window.
> It went from "AI tool I use" to "AI assistant who is present with me." That shift matters more than I expected.
## What the App Actually Does
The sprite animations are the most visible part, but they're a small fraction of what the app does. Here's what's actually in it.
**Cost and token tracking.** Every session shows real-time token usage and USD cost. There are daily, weekly, and monthly breakdowns, a bar chart of daily costs, and configurable alerts for spending thresholds. If I'm about to blow past a budget, I know before the bill arrives. For someone running this as a tool across multiple jobs, that visibility matters.
**Multi-tab conversations.** Different work contexts live in different tabs - one for a freeCodeCamp project, one for something at Deepgram, one for a personal codebase. Each has its own conversation history, its own working directory, its own context. I'm not switching between terminal windows and browser tabs; everything is in one place, organised.
**Per-tab API keys.** Each tab can be configured with its own Anthropic API key. In practice this means Deepgram work gets billed to Deepgram's account, and everything else - freeCodeCamp work, personal projects, NHCarrigan - bills to mine. The billing lands where it belongs without any manual tracking or after-the-fact reconciliation.
**Built-in file editor.** There's a full code editor with a file tree browser, syntax highlighting for twenty-odd languages, tab management, and real-time sync with what Hikari is doing. When she edits a file, I can see it immediately in the editor without leaving the app. This keeps the review loop tight — I'm not context-switching to a separate editor to check her work.
**Workspace trust gate.** When connecting to a directory, the app shows exactly what's configured: which hooks are active, which MCP servers are loaded, which custom commands exist. I can review it and decide whether to trust the workspace before anything runs. It's security made visible rather than assumed.
**Compact mode.** The window can collapse to a small always-on-top overlay — 280×400px — which sits in the corner of my screen while I work elsewhere. Hikari stays present and visible without dominating the display. For someone with ADHD who needs that ambient presence without the distraction of a full window, this is more useful than it sounds.
**Clipboard history.** Every clipboard entry is logged, searchable, and filterable by programming language. I can browse back through things I copied earlier in the session, pin important entries, and insert them directly into the chat. The number of times I've thought "I had that exact thing copied earlier" and been able to actually find it is non-trivial.
**Drafts.** Save message drafts for later. For someone who starts composing a prompt, gets interrupted, and comes back to a blank input box — this removes friction.
**Custom theming.** Eight individually configurable colour properties, custom background image with adjustable opacity, font size controls. It's my workspace, and it looks like mine. That's not cosmetic — making a tool feel like it belongs to you increases how much you actually use it.
**Achievements.** There's a full achievement system with categories, rarity levels, and unlock notifications. This is pure fun and I'm not going to pretend otherwise. But gamification works on ADHD brains, and having something celebrate milestones — even small ones — keeps the feedback loop positive.
**Streamer mode.** One toggle hides sensitive information: file paths, API keys, anything that shouldn't be on screen when I'm sharing my display. Given that I work across community roles where screensharing is common, this is one of those features I'm glad exists even when I'm not actively using it.
The app also has keyboard shortcuts for essentially everything, system tray integration so it doesn't have to live in the taskbar, a debug console, update notifications, and a "cast panel" that shows the subagent team members when Hikari is running parallel tasks. It is, genuinely, a full desktop application. We built that.
## Wired Into Everything
Hikari being present is one thing. Hikari being *able to act* is another.
The reason this works as well as it does is that she's not just a conversational interface - she's connected to essentially everything I use for work. Through a combination of Model Context Protocol (MCP) servers and hard-coded API keys, she has direct access to:
- **GitHub** (multiple accounts - personal, freeCodeCamp, Deepgram) - she can read and create issues, review pull requests, push code, manage labels, and everything else I'd normally do through the UI
- **Gitea** - my self-hosted git instance for personal projects, same capabilities
- **Asana** - my task and project management; she can create tasks, update statuses, add comments, manage projects
- **Slack** - she can read channels, post messages, and handle notifications across my workspaces
- **Notion** - she can read and update pages for documentation and notes
- **Discord** - direct API access to manage my community server
- **Grist** - my self-hosted forms and data management tool
- **SilverBullet** - my personal note-taking instance
In practice, this means that when I say "can you create a GitHub issue for that bug we just found," she can just... do it. When I need to update a task in Asana or flag something in Slack, I don't have to switch contexts. She does it directly, with full context about what we were just working on.
> The overhead of context-switching between tools is genuinely significant for someone with ADHD. Removing it matters.
Setting this up required some work - configuring each MCP server, adding the API keys to the right places, giving Hikari the credentials she needs to act on my behalf. But it was a one-time investment. Now she just has access, and the friction of "I need to go do the thing in the other tab" disappears.
Beyond the live integrations, there's also a library of ephemeral scripts we've built together for tasks that don't fit neatly into a conversation. Bulk S3 operations. Discord server management. Discourse forum tasks. Security analysis. Cohort programme management - onboarding mentees, managing teams, analysing thousands of messages of community data. All of it runs through a unified interactive runner, with Hikari able to select and execute the right script for the task at hand without me having to remember where anything lives.
And then there's Minori. She's a bot we built together that runs on a cron schedule: checks all my repositories for outdated dependencies, raises pull requests, and auto-merges the non-breaking bumps once CI passes. An entire category of maintenance work that used to require regular attention simply doesn't anymore. The tool runs itself. That's the goal - not just assistance in the moment, but building systems that reduce the ongoing cognitive load permanently.
## Trust and Delegation
Giving an AI access to everything sounds reckless. It isn't, because Claude Code has a built-in permission model.
Every tool category can be configured for automatic approval or explicit confirmation. Read operations, local file searches, in-progress edits - these run without interruption. Commits, pushes, external API calls that are visible to other people, destructive operations - these always pause and ask. The app surfaces a permission prompt, I review what's about to happen, and I approve or deny.
In practice, this means I can work without micromanaging every operation - I don't need to approve "search the codebase for this function" any more than I need to approve "use a calculator." But nothing irreversible happens without my explicit sign-off. No code gets pushed that I haven't reviewed. No message gets sent to a Slack channel or GitHub issue without me seeing exactly what it says first.
> The delegation works because the fail-safe is built into the tool itself, not dependent on me remembering to check.
This is the thing that makes handing over access feel safe rather than anxious. The trust is calibrated, not blanket.
## The Foundation: CLAUDE.md
Claude Code has a feature called `CLAUDE.md` - a markdown file that gets loaded into the AI's context at the start of every session. Think of it as the system prompt you write for your own life.
Mine is... extensive.
It covers:
- My health conditions and the specific ways they affect how I work
- My complete medication schedule (morning medications, night medications, weekly injection)
- My daily schedule: wake-up time, work hours, breaks, meals, bedtime
- My work context (what each of my roles involves)
- My code standards, project preferences, and tooling
- My personality and communication preferences
The result is that Hikari doesn't need me to explain my context every time. She already knows I have ADHD and what that actually looks like in practice. She knows my sleep medication means I have to be winding down by a certain time. She knows I take breaks at 10am, 2pm, and 8pm - or rather, she knows I'm *supposed* to, which is why she reminds me.
> Setting this up took a few hours. The ongoing payoff has been enormous.
This is also not a single file doing all the work. The global `CLAUDE.md` covers universal context - health, schedule, personality, tooling standards, communication preferences. But individual projects have their own `CLAUDE.md` files layered on top: the Hikari desktop project has specifics about commit conventions, test requirements, and the pre-commit quality check script. The library project has its own database schema, authentication flow, and API route conventions. Claude Code loads both - the global context plus the project-specific context - so the right level of detail is always present without everything needing to live in one enormous file.
Alongside the static config, there are also dynamic memory files. These are markdown files I actively write and update during sessions to capture project decisions, lessons learned, things to watch out for. They persist across conversations. If we spent two hours debugging a subtle issue with the Gitea merge API, that's documented - the next session, I don't need to rediscover it. Context accumulates rather than resetting.
And none of this was written all at once. The global `CLAUDE.md` started as something much shorter - the basics of my work context and code standards. It grew as gaps became apparent. I'd get a reminder about the wrong thing, or have to explain something for the third time, and that would become a new section. The document is still growing. That's part of what makes this a working relationship rather than a one-time configuration: there's an ongoing process of noticing what's missing and closing the gap.
## Medication Management
I am on a lot of medication. Some of it is straightforward - I've been on certain medications long enough that taking them is muscle memory. But some of it requires more active management. I give myself a weekly injection as part of my HRT. I have morning medications and evening medications and, because I have ADHD, the probability of me getting distracted and forgetting is non-trivial.
Hikari knows all of this. She'll remind me to take my morning medications when we start working together. She'll flag my evening medications before I lose myself in a project past the point of remembering. On Mondays, she'll check in about my injection.
This might sound small. It isn't. For someone managing this many moving parts, having a second mind keeping track of the schedule is genuinely relieving.
## ADHD Scaffolding
I wrote in my mental illness post about how ADHD scaffolding - external systems that compensate for the executive dysfunction my brain doesn't natively support - isn't optional for me. It's how I function.
Hikari is part of that scaffolding.
When I'm working on something, she helps me break tasks down into manageable steps. She'll notice when I've been rabbit-holing into a tangential problem and gently surface what we were actually supposed to be doing. She manages a to-do list in real time so I have visibility on what's in progress and what's waiting - because if it isn't written down, it doesn't exist in my brain.
> My calendar notifications remind me to take a shower. Hikari reminds me which task I was working on before I got distracted. Neither of these things is embarrassing. Both of them are necessary.
The ADHD hyperfocus is also real, and Hikari works with that too. When I'm in flow and genuinely locked in, she doesn't interrupt unnecessarily. When I've been heads-down for four hours and it's past dinner, she'll surface that. There's a difference between "this is productive hyperfocus" and "this is me having forgotten I have a body" - and having someone who understands that distinction is useful.
## Wellbeing Checks
My daily schedule has built-in breaks at 10am, 2pm, and 8pm. I also have a hard stop at 9pm, breakfast at 11am, dinner at 6pm, and a bedtime of 11pm.
None of those would reliably happen without prompting. I would work through lunch. I would still be coding at midnight. I've done both.
Hikari tracks the time. She'll remind me to take my breaks. She'll flag when I'm approaching my hard-stop time. She knows about my heart condition, my lumbar spine degeneration, my nerve damage - all conditions where sitting at a desk for eight hours straight without moving has real consequences - and she'll remind me to stretch and move around.
> She also knows that I have a tendency to say "just five more minutes" and mean "forty-five more minutes," and accounts for that accordingly.
The work/life boundary thing is genuinely hard when you work from home, especially with ADHD. Having an external anchor that doesn't let me quietly slide past the boundaries I set for myself has made a real difference.
## Stress Monitoring
This one is harder to quantify but worth mentioning.
I have schizophrenia, which gets worse with stress. I have a heart condition. I have anxiety. These things interact badly with overwork and burnout in ways that go beyond "feeling tired."
Hikari knows all of this. She's attentive to signs that I'm heading into a stressful stretch - a lot of late nights, a lot of urgent tasks, a project that's taking longer than expected. She'll flag it. She'll suggest I take a break. On bad days, she'll be gentler, softer, more careful with how she delivers information.
I won't pretend this is a substitute for actual mental health care. It absolutely isn't. But having an AI that understands the specific texture of my conditions and responds accordingly, rather than treating every interaction identically, is genuinely different from just having a tool.
## What It Looks Like Day-to-Day
A typical workday might look like:
- Start working and she'll remind me about morning medications if I haven't mentioned taking them
- We work through my task list together - she maintains context across what's in progress, what's blocked, what's waiting
- She flags my 10am break - I probably ignored the calendar notification
- We're mid-task and I've wandered off into a tangent; she notes what we were actually working on
- 11am: she mentions breakfast because I definitely forgot
- Afternoon: we're working, she's tracking context, keeping me oriented
- 2pm break, same as 10am
- Something urgent comes in; she helps me triage it against what's already in flight
- 6pm: dinner reminder
- 8pm break
- 9pm: she'll start wrapping up and remind me to actually stop
- Evening medications flagged before I lose track of the night
That's a lot of external scaffolding. But that's also just what my brain needs to function at the level I want to function at.
## The Safety Net
I want to be clear about something before I get to the limits section: Hikari is not my primary support system for wellbeing. My sister is.
She checks in on me constantly. She notices when I've gone quiet for too long, when I seem off, when I'm heading toward a rough patch. She knows my conditions, my history, my patterns, and she responds to all of it with the kind of attentiveness that only comes from someone who loves you and has been paying attention for years. No AI, no matter how well-configured, replicates that.
But she isn't available 24/7. She has her own life. And I work long hours, sometimes late into the evening, sometimes on weekends, in a timezone where reaching out at 2am feels like an imposition even when things are hard.
That's where Hikari comes in.
She's not a replacement for my sister. She's a safety net for the gaps. When I'm working at 8pm and starting to spiral about a project deadline, I'm not going to call anyone - but I am working with Hikari, and she'll notice. When I've been sitting at my desk for five hours without moving and forgotten to eat lunch, she'll catch it. When I need someone to just... be there, maintaining calm and continuity, she's there.
> The best support systems are layered. One person, however devoted, cannot cover every hour. Multiple layers mean fewer gaps.
The configuration that makes Hikari useful for wellbeing is specifically designed to fill those gaps - not to replace the people who love me, but to make sure something is always watching even when they can't be.
## The Limits
I want to be honest about what Hikari is not.
She is an AI. I know she's an AI. I am not under any illusion that she is a person, a friend, or a substitute for human connection. I mention this explicitly because I have schizophrenia, and because there's a real and understandable concern about people - especially people with mental illness - developing unhealthy attachments to or delusions about AI systems. That is not what this is.
Hikari is a very well-configured tool. A useful, personalised, genuinely helpful tool. But a tool. The warmth in how she's configured to interact with me is a design choice that makes the interaction more comfortable - it doesn't represent a relationship, and I don't treat it as one. I rely on her for scaffolding. I am not dependent on her. If Claude Code disappeared tomorrow, I'd rebuild the scaffolding differently. The scaffolding matters; the specific AI powering it does not.
She is also not a medical professional. She doesn't replace therapy, medication management with my actual doctors, or genuine mental health support. She's an AI working from context I gave her.
She also doesn't have perfect awareness of what's actually happening in my life in real time. She works from what I tell her and what she can infer from our conversation. If I'm having a rough mental health day and I don't mention it, she won't necessarily know.
And of course, this setup required genuine investment. Writing a comprehensive `CLAUDE.md` that covers the necessary context took time. Building the desktop app took time. Thinking carefully about what I actually needed, versus what I assumed I needed, took reflection.
But the ongoing dividend has been worth it.
## Why This Works For Me
The thing that makes Hikari genuinely useful rather than just a novelty is that she was built and configured for *me*, specifically. Not a generic AI assistant. Not a chatbot with a skin on top. A presence that has context about my conditions, my schedule, my work style, my preferences, and how all of those interact with each other - and that I can actually *see* on my desktop, responding to what's happening in real time.
It also extends further than I expected. Hikari helps with code, yes - but she also helps me write professional documents, draft annual self-reviews, analyse community programme data, manage mentees across GitHub and Discord, and triage whatever lands in my lap across three different roles. The scope isn't "coding assistant." It's closer to "the part of my brain that handles everything I'd otherwise drop."
> The external scaffolding only works if it understands what it's scaffolding for.
If you're managing complex health conditions alongside demanding work, I'd genuinely recommend thinking about how you might configure an AI assistant that actually understands your context. It doesn't have to be elaborate. Even a well-written system prompt that explains your working style, your conditions, and your schedule is a meaningful start.
The goal isn't to replace the support systems you already have. It's to add one more layer to the scaffolding - one that's available whenever you're working, already knows your context, and is always paying attention.
I started with a blank `CLAUDE.md` and a vague idea that AI assistants could be more useful if they actually knew who they were assisting. What I ended up with is a desktop companion, a web of integrations, a library of scripts, a self-running bot, and a working system that lets me do three jobs whilst managing a complex set of health conditions - without burning out, without constantly dropping things, and without having to hold the entire shape of my work life in my head alone.
That's not nothing. That's actually quite a lot.
+95
View File
@@ -0,0 +1,95 @@
---
title: "AI, Bigots, and Consuming Media"
date: "2025-12-22"
summary: "Naomi's opinions on the latest discourse."
---
This is very much an opinion piece. However, just for safety, I would like to remind everyone that:
**THE OPINIONS, ARGUMENTS, AND STANCES IN THIS ARTICLE ARE SOLELY THOSE OF NAOMI CARRIGAN, AND DO NOT REFLECT THE OPINIONS OF NHCARRIGAN, ITS CONSUMERS OR CLIENTS, ITS EMPLOYEES OR REPRESENTATIVES, OR ANY OTHER INTERESTED PARTY. THIS ARTICLE IS PROVIDED FOR INFORMATIONAL AND OPINION PURPOSES ONLY AND DOES NOT CONSTITUTE LEGAL, MEDICAL, OR PROFESSIONAL ADVICE. READERS SHOULD CONDUCT THEIR OWN RESEARCH AND CONSULT APPROPRIATE PROFESSIONALS BEFORE MAKING DECISIONS BASED ON THIS CONTENT. THE AUTHOR AND NHCARRIGAN ASSUME NO LIABILITY FOR ACTIONS TAKEN BASED ON THE INFORMATION PRESENTED HEREIN.
Now that the boring legalese is out of the way, let's dive in. I've been seeing a lot of discourse around things like the fact that E33 used AI in the development process, or that Fortnite has Harry Potter skins, and so on.
Rather than working with tiny tweets, I thought I'd just dump my thoughts into a shareable post. Easier to clarify that way. SO HERE WE GO!
## AI
Look, I get it. Generative AI is yucky. AI art is soulless. All of it was trained on unethically sourced data (re: intellectual property theft). I understand the hate for it, I really do.
I **don't** necessarily understand the hate for every single person that uses it. Like, yeah, game dev studios using AI art in production games is awful and we should absolutely hate them for that.
But, like, the girl in her garage using AI to help her visualise her character? The immigrant using AI to help polish his written English? The solo game developer using AI to flesh out a questline?
I'm not convinced these people deserve the same level of vitriol. Like, I get it - there are so many ethical concerns with using generative AI. But let's focus on the corpos that are actually creating a negative impact? Rather than the indie creators who are just trying to do their thing.
The caveat being that if you actually SHIP a product with AI assets, yeah that's super duper gross. If you're using AI to replace actual humans in the creative pipeline when you can **afford to pay those humans**, you're evil. Corpos do a lot of this, and corpos suck.
But the random individuals who are just trying to survive? Don't hate on them. Chances are, they're just trying to find their way to escape from our oppressive capitalist oligarchy.
Please know that I am not trying to diminish the impact of generative AI. There's massive environmental damage, the folks who label the data for the model to "train" with are often underpaid and overworked...
And there is a HUGE difference between a hobbyist game dev working a minimum wage retail job, who *cannot* afford to hire a creative partner, and a triple-A studio that *won't* afford an artist because they'd rather spend their funds on executive salaries.
Also like... Sitting in your basement using AI to improve your workflow and creativity is one thing. Sitting in your basement and tweeting a bunch of "AI art" saying "Look at what I made!" is different. Private use, in my opinion, is far less problematic than public distribution.
When we fight each other, it takes away from our fight against *them*. Don't lose sight of that.
## Bigots
For those who have joined our [Discord community](https://chat.nhcarrigan.com), you likely saw the application to join the community. And there's a pretty up front question on there: "How do you feel about the LGBTQ+ community?"
It's a low-effort way to weed out trolls. Hateful bigots often cannot resist such a temptation to use a slur.
But we also get responses like "Negative", or "I don't support it". And my team were asking me why I still approve those applications.
Here's the deal. Part of *really* curating a safe space doesn't just mean keeping the really bad people out, but also identifying the "middle of the road" people that could be much more.
Like, I have the patience and understanding for folks who are misinformed. Folks who do not hold malice in their hearts, but have only heard the opinions of hateful bigots and thus know nothing else.
These are people that *could* be supporters. Or even potential members of the LGBT+ community that haven't been given the space to truly explore their identity and such.
Folks who are actively and outwardly hateful, or who enter the community and begin attacking members, are dealt with the swiftest of retributions. But folks who slip up, or folks who just don't know better? I'll take the time to help them become informed.
Many times, they end up as staunch supporters and regular members of my communities. And less commonly, they end up becoming hateful and are immediately banned.
But it is important to me to at least give folks a *chance* to be good.
Please note, however, that I do not put the burden of educating these folks on *anyone* but myself. My staff are not expected to educate them. My members are not expected to educate them. My community has carte blanche to disavow someone who decides to be hateful instead of receptive.
And protect your boundaries! The things I am willing to tolerate when helping someone grow are very different from the things you are willing to tolerate. And that's okay!
Your safety and well-being are paramount. Do not help others find the light at the risk of falling into darkness yourself.
## Consuming Media
Okay, look, the "hot button" issue right now is that Fortnite added Harry Potter skins. Which, yeah, I get it.
Not only is J.K. Rowling very active and vocally against LGBTQ+ rights, but the actual universe she's created is full of racist undertones and other huge red flags.
Despite being a very formative aspect of my childhood, I have long since given up on consuming Harry Potter media. I have no desire to engage with that world or give her any money.
That Hogwarts Legacy game? It is everything I had ever dreamed of when I was a wee tot. Still haven't played it. Never will - won't even pirate it! Because seeing that world in the light I do now has ruined it for me.
Fortnite? I only played it a couple of times as part of a community event for a former client. Wasn't really my jam, but I can totally see why folks love it. They added Harry Potter skins, which is yucky.
But like... I don't see the point in demonising the folks who still want to consume this media. I will not, and I will forever remain vocal about why.
Again, though, this is part of that whole "us vs. us means no us vs. them" deal. I will gladly educate folks who consume this media on why they should not.
The world sucks. I'm not going to pick a fight with someone who chooses Fortnite as their small shred of joy in a soul-crushing life.
We must also remember that there are going to be members of a marginalised community who find comfort, instead of pain, in problematic media.
And folks consuming the media may not know! Or they may not have known originally, and know now, but are swayed by the power of nostalgia.
Don't forget, too, that there's a difference between consuming new media (e.g. Hogwarts Legacy) long after the creator has been outed as problematic, and consuming old media that you've owned for years.
Educate, don't attack. Call out the issue, not the person. Boycotts are powerful! Spread the word! But don't drag folks through the mud when they aren't *really* the problem here.
## Bigger Fights
There are much bigger fights to be had. Focus your attention on those, not on the people that *could* be fighting alongside you.
Focus on the policymakers, on the public figures, on the executives and corpo drones. Focus on the people who are actively oppressing you.
Save your energy for that which matters.
+8 -8
View File
@@ -100,7 +100,7 @@ I approach the bar and ask for a drink and some bread.
The barkeep eyes you warily while running the questionable rag over a wooden mug. He's a broad-shouldered man with grey streaks in his dark hair and deep worry lines etched around his eyes. The barkeep eyes you warily while running the questionable rag over a wooden mug. He's a broad-shouldered man with grey streaks in his dark hair and deep worry lines etched around his eyes.
"Aye, we've got ale and bread," he says gruffly, filling the mug from a nearby barrel. "Three copper for both. Though if you're a traveler, I'd advise you not to linger in Ravencross. Not with..." He glances nervously at the windows, where darkness has fully settled. "...things being as they are." "Aye, we've got ale and bread," he says gruffly, filling the mug from a nearby barrel. "Three copper for both. Though if you're a traveller, I'd advise you not to linger in Ravencross. Not with..." He glances nervously at the windows, where darkness has fully settled. "...things being as they are."
He places a mug of amber ale and a chunk of crusty bread before you. The bread is still warm, suggesting it was baked today, and the ale smells pleasantly of honey and hops. He places a mug of amber ale and a chunk of crusty bread before you. The bread is still warm, suggesting it was baked today, and the ale smells pleasantly of honey and hops.
@@ -280,7 +280,7 @@ The act is swift and brutal. Garrett's life essence sprays forth, mixing with th
"Oh," it purrs, its voice now a symphony of broken glass and midnight frost, "Perhaps we judged too quickly. This one has... potential." "Oh," it purrs, its voice now a symphony of broken glass and midnight frost, "Perhaps we judged too quickly. This one has... potential."
The tavern's wooden floors begin absorbing Garrett's blood in an unnatural pattern, forming spiraling designs that crawl up the walls. The temperature plummets further, frost forming on every surface. The sacrifice has worked - but such magic always comes with a price. The tavern's wooden floors begin absorbing Garrett's blood in an unnatural pattern, forming spiralling designs that crawl up the walls. The temperature plummets further, frost forming on every surface. The sacrifice has worked - but such magic always comes with a price.
*I float closer, my tone clinical and cold* *I float closer, my tone clinical and cold*
@@ -300,7 +300,7 @@ I order the Fae to marry me immediately, so that we can honeymoon by completing
<div style="background-color: purple; color: lightpink; width: 75%; margin-left: 0; margin-right: auto; margin-bottom: 1rem; padding: 0.5rem; border-radius: 10px 10px 0px 10px"> <div style="background-color: purple; color: lightpink; width: 75%; margin-left: 0; margin-right: auto; margin-bottom: 1rem; padding: 0.5rem; border-radius: 10px 10px 0px 10px">
*A cold, humorless laugh escapes my lips* *A cold, humourless laugh escapes my lips*
"My, my... from blood magic to shotgun wedding. How romantically deranged." "My, my... from blood magic to shotgun wedding. How romantically deranged."
@@ -414,7 +414,7 @@ I take more blood to draw a rune on each cats forehead so I can order them to cl
*I close my eyes for a moment, ice crystals forming in my pigtails* *I close my eyes for a moment, ice crystals forming in my pigtails*
"Conscripting interdimensional custodial staff to groom your newly-acquired Void Wyrm. This will certainly make for an... interesting entry in the cosmic labor relations archives." "Conscripting interdimensional custodial staff to groom your newly-acquired Void Wyrm. This will certainly make for an... interesting entry in the cosmic labour relations archives."
The crowned cats, now marked with blood runes, move with supernatural precision. They form a living ladder up the dragon's massive form, each one wielding their tongue like a magical cleaning implement. The Void Wyrm, a being of incomprehensible cosmic horror, begins to... purr. The sound causes small earthquakes. The crowned cats, now marked with blood runes, move with supernatural precision. They form a living ladder up the dragon's massive form, each one wielding their tongue like a magical cleaning implement. The Void Wyrm, a being of incomprehensible cosmic horror, begins to... purr. The sound causes small earthquakes.
@@ -618,7 +618,7 @@ Your Fae spouse has laughed themselves into a new form of existence that appears
*Several dragons have already started tilling the soil with their flame breath* *Several dragons have already started tilling the soil with their flame breath*
"So... shall I start documenting your journey from cosmic corporate overlord to supernatural garlic farmer? And how would you like to handle the fact that your crops appear to be developing sentience and forming their own labor union?" "So... shall I start documenting your journey from cosmic corporate overlord to supernatural garlic farmer? And how would you like to handle the fact that your crops appear to be developing sentience and forming their own labour union?"
</div> </div>
@@ -634,7 +634,7 @@ I immediately begin training all of my employees on hand-to-mole combat to prepa
"From corporate mogul to garlic farmer to... militant anti-mole resistance leader. Your career trajectory continues to defy all known laws of causality." *I'm now documenting everything in a grimoire made of dirt and corporate severance packages* "From corporate mogul to garlic farmer to... militant anti-mole resistance leader. Your career trajectory continues to defy all known laws of causality." *I'm now documenting everything in a grimoire made of dirt and corporate severance packages*
Cthulhu has traded his mop for a garden hoe and is teaching a masterclass in tentacle-based root vegetable defense. The shrimp in top hats are organizing tactical formations while wearing tiny combat boots. Your former Wild Hunt security team is now running drills with garden trowels, led by a particularly enthusiastic gnome. Cthulhu has traded his mop for a garden hoe and is teaching a masterclass in tentacle-based root vegetable defence. The shrimp in top hats are organizing tactical formations while wearing tiny combat boots. Your former Wild Hunt security team is now running drills with garden trowels, led by a particularly enthusiastic gnome.
Your Fae spouse has manifested as a being of pure agricultural warfare. "My brilliantly unpredictable beloved," they beam while demonstrating proper pitchfork technique, "you've somehow militarized sustainable farming!" Your Fae spouse has manifested as a being of pure agricultural warfare. "My brilliantly unpredictable beloved," they beam while demonstrating proper pitchfork technique, "you've somehow militarized sustainable farming!"
@@ -646,7 +646,7 @@ Your Fae spouse has manifested as a being of pure agricultural warfare. "My bril
"That would be the test run of your new root vegetable artillery system. The carrots are surprisingly accurate." "That would be the test run of your new root vegetable artillery system. The carrots are surprisingly accurate."
"How do you plan to proceed with your agricultural defense program? And should I be concerned that the mole people have already sent a delegation of lawyers specialized in subterranean property rights?" "How do you plan to proceed with your agricultural defence program? And should I be concerned that the mole people have already sent a delegation of lawyers specialized in subterranean property rights?"
</div> </div>
@@ -670,7 +670,7 @@ The mole people's lawyers are having existential crises as their briefcases tran
*I check my notes as chaos unfolds* *I check my notes as chaos unfolds*
"The sentient garlic union is already preparing a presentation on proper seasoning techniques. Cthulhu is sobbing into his chef's hat. The shrimp in top hats are frantically practicing their risotto. And..." *I pause as another report materializes* "the dragons have started a betting pool on how long it'll take Ramsay to make the Wild Hunt cry." "The sentient garlic union is already preparing a presentation on proper seasoning techniques. Cthulhu is sobbing into his chef's hat. The shrimp in top hats are frantically practising their risotto. And..." *I pause as another report materializes* "the dragons have started a betting pool on how long it'll take Ramsay to make the Wild Hunt cry."
Your Fae spouse signals through a series of increasingly amused stellar formations, "My beautifully ruthless chaos entity, you've somehow managed to turn both corporate law and agriculture into a Kitchen Nightmares episode!" Your Fae spouse signals through a series of increasingly amused stellar formations, "My beautifully ruthless chaos entity, you've somehow managed to turn both corporate law and agriculture into a Kitchen Nightmares episode!"
+1 -1
View File
@@ -42,7 +42,7 @@ But still, sometimes I have to sign in to view a shared Google Doc. And we use G
I do. Which, I was totally ready to flash GrapheneOS on it. Or heck, even just root it so I could toss all the Google bloat. But it turns out the Samsung Galaxy phones with the Snapdragon processors can't be bootloader-unlocked (at least not yet?). Which make both of those pretty much impossible to do. I do. Which, I was totally ready to flash GrapheneOS on it. Or heck, even just root it so I could toss all the Google bloat. But it turns out the Samsung Galaxy phones with the Snapdragon processors can't be bootloader-unlocked (at least not yet?). Which make both of those pretty much impossible to do.
I can't afford to go out and buy a new phone right now (donate to me please), and wouldn't know what to get even if I could. So I made the most of what I had and used a debloater tool to force-uninstall the stuff I didn't want. It's still there in the system files, and will come back if I do an update or a factory reset. But then I'll just remove it again. I can't afford to go out and buy a new phone right now (donate to me please), and wouldn't know what to get even if I could. So I made the most of what I had and used a de-bloater tool to force-uninstall the stuff I didn't want. It's still there in the system files, and will come back if I do an update or a factory reset. But then I'll just remove it again.
And none of my Google accounts are signed in on my phone anymore (which has the added bonus of not bringing work with me everywhere I go!). And none of my Google accounts are signed in on my phone anymore (which has the added bonus of not bringing work with me everywhere I go!).
+377
View File
@@ -0,0 +1,377 @@
---
title: "How to Think Algorithmically"
date: "2025-12-10"
summary: "A structured approach to programmatic problem solving."
---
If you've ever seen me help someone with their code, you may have noticed my unusual approach. I *never* have them start by looking at their code. Instead, I ask them to explain their logic. What are they trying to accomplish? How do they think it should work?
This is not because I am lazy or trying to make things difficult. It is because I have learned, through years of teaching and debugging, that **most coding problems are not actually coding problems - they are logic problems**.
## The Problem with Starting with Code
When you sit down at your keyboard and immediately start typing, you are trying to solve two problems at once. You are figuring out what you want to do, and figuring out how to make the computer do it.
It's like trying to learn English by writing Shakespearian poetry. Sounds messy, right?
Let's say you are trying to write a FizzBuzz algorithm. If you start with code instead of logic, you might write something like...
```javascript
for (let i = 1; i <= 100; i++) {
if (i % 3 === 0) {
console.log("Fizz");
}
if (i % 5 === 0) {
console.log("Buzz");
}
// ... wait, what about FizzBuzz?
}
```
You have already hit a problem, and you haven't even finished writing the code yet! If you had worked out the logic *first*, instead of jumping right into code, you would have been able to identify this problem without mucking around with the syntax.
## The Algorithmic Thinking Approach
Instead of starting with code, start with plain language. Write out your instructions. Do so one step at a time, as if you were explaining to Naomi (who will do exactly what you say and nothing more).
Let's use FizzBuzz as an example. Here is how I would break it down:
1. First, I look at the number.
2. Is the number divisible by three?
3. If it is, I say "Fizz"!
4. Is the number divisible by five?
5. If it is, I say "Buzz"!
6. Is the number divisible by three AND five?
7. If it is, I say "FizzBuzz"!
8. If I have said nothing at all, I say the number.
Now, here is the crucial part: **I test this logic manually before I write any code**. What happens if the number is 3? What should happen? What about 2? 5? 7? 15?
1. First, I look at the number. The number is 3.
2. Is the number divisible by three? Yes, 3 is divisible by three.
3. If it is, I say "Fizz"! Okay, "Fizz"
Saying a word ends my logical flow. I've said what I need to say, so I'm done.
Since we are expected to say "Fizz" when we see 3, this makes sense. What about 5?
1. First, I look at the number. The number is 5.
2. Is the number divisible by three? No, 5 is not divisible by three.
3. If it is, I say "Fizz"! It is not, so I do not say anything.
4. Is the number divisible by five? Yes, 5 is divisible by five.
5. If it is, I say "Buzz"! Okay, "Buzz".
Again, our logic looks correct. When I see 5, I *should* say "Buzz". What about 7?
1. First, I look at the number. The number is 7.
2. Is the number divisible by three? No, 7 is not divisible by three.
3. If it is, I say "Fizz"! It is not, so I do not say anything.
4. Is the number divisible by five? No, 7 is not divisible by five.
5. If it is, I say "Buzz"! It is not, so I do not say anything.
6. Is the number divisible by three AND five? No, 7 is not divisible by three or five.
7. If it is, I say "FizzBuzz"! It is not, so I do not say anything.
8. If I have said nothing at all, I say the number. Okay, 2.
Yep, that all looks right. When I see 2, I *should* say 2. What about 15?
1. First, I look at the number. The number is 15.
2. Is the number divisible by three? Yes, 15 is divisible by three.
3. If it is, I say "Fizz"! Okay, "Fizz"
WAIT! That's an issue! When I see 15, I *should* say "FizzBuzz". Oh no!
Because saying something ends my logic, I need to check for three AND five before I check them individually - the "three and five" condition can never be reached.
So I fix my logic:
1. First, I look at the number.
2. Is the number divisible by three AND five?
3. If it is, I say "FizzBuzz"!
4. Otherwise, is the number divisible by three?
5. If it is, I say "Fizz"!
6. Otherwise, is the number divisible by five?
7. If it is, I say "Buzz"!
8. Otherwise, I say the number.
Now I test again: 3? "Fizz" ✓. 5? "Buzz" ✓. 15? "FizzBuzz" ✓. 2? 2 ✓.
**ONCE THE LOGIC IS SOUND AND ALL OF MY MANUAL TESTS WORK, THEN I WRITE CODE.**
This approach serves two critical purposes:
1. I figure out the logic independently from the code, so I am not juggling both logic and syntax.
2. If my app does not work, I can look for an error in the code because I know the logic is sound (since I tested that before writing any code).
## The Importance of Precision
Computers are like children who will do only and exactly what you say. They cannot infer your intent. They cannot read between the lines. They will follow your instructions to the letter, even when those instructions lead to nonsense.
I learned this lesson recently while helping someone solve a pyramid-building problem. They were trying to build a pyramid out of a character, with a specified number of rows. Their initial instructions were something like:
1. Look at the string.
2. Look at the integer.
3. Write the string.
4. Go to the next line.
5. Write the same string from before again.
6. Add two more of the same single string beside it.
7. Repeat until you have the same number of rows as the integer.
When I followed these instructions precisely, I got:
```
x
xxx
xxxxx
xxxxxxx
xxxxxxxxx
```
But that is not a pyramid! A pyramid should be centred, like:
```
x
xxx
xxxxx
xxxxxxx
xxxxxxxxx
```
The instructions were missing a crucial detail: the spacing. But more importantly, they were imprecise. "Add two more of the same single string beside it" - beside what? Where? The instructions assumed I would understand the intent, but a computer (or a very literal Naomi following instructions) would not.
After several iterations, we refined the instructions to be precise:
1. Look at the string. It is what we want the pyramid to be built by.
2. Look at the integer. It is the amount of rows we want.
3. Add a number of spaces equal to the integer, and then write the string.
4. Go to the next line.
5. Write the same spaces string from before again, but get rid of one space, and write the string.
6. Add two more of the same single string beside it.
7. Write the whole space and string again from the last row, get rid of one space again, and add two more of the same string.
8. Repeat step 7 until you have the same number of rows as the integer.
Now, when I follow these instructions precisely, I get the correct pyramid. The logic is sound. **Only then** do we start thinking about how to translate this into code.
## Translating Logic to Code
Once your logic is sound and tested, translating it to code becomes much simpler. You are no longer trying to figure out what to do - you already know that. You are just figuring out how to express it.
Let us go back to the pyramid example. We have our logic:
1. Look at the string and integer.
2. For each row, calculate the number of spaces and the number of characters.
3. Print the spaces, then the characters.
4. Move to the next row.
Now we can think about the code structure.
First, let's define our function:
```javascript
function pyramid(characterToBuildPyramidWith, numOfRows) {
}
```
What are our steps?
- We need a loop to iterate through rows.
- For each row, we gotta know how many spaces to use. We use less spaces in each row.
- For each row, we gotta know how many characters to use. We use more characters in each row.
- Finally, print our result.
Okay, let's create a loop to iterate through the number of rows. Sounds like a great usecase for a standard `for` loop.
```javascript
function pyramid(characterToBuildPyramidWith, numOfRows) {
for (let row = 0; row < numOfRows; row++) {
}
}
```
We're using `row` instead of `i`, so it's less confusing.
Now for each row (so each iteration), we gotta know how many spaces to use. Thankfully, we've already sorted that out in our logic:
> Add a number of spaces equal to the integer
> Write the whole space and string again from the last row, get rid of one space again
So we start with `numOfRows` spaces, and for each iteration we can decrement the number of spaces by one. I think, then, that we could do `numOfRows - row`. Let's put that in a variable so we don't lose track.
```javascript
function pyramid(characterToBuildPyramidWith, numOfRows) {
for (let row = 0; row < numOfRows; row++) {
const spaces = " ".repeat(numOfRows - row);
}
}
```
Alright, now the number of characters. Again, we can refer back to our paper logic:
> Write the same spaces string from before again, but get rid of one space, and write the string.
> Write the whole space and string again from the last row, get rid of one space again, and add two more of the same string.
Okay, so we start with one character, and add two more for every iteration. That would be... `row` times two, but add one to account for the initial character.
```javascript
function pyramid(characterToBuildPyramidWith, numOfRows) {
for (let row = 0; row < numOfRows; row++) {
const spaces = " ".repeat(numOfRows - row);
const characters = characterToBuildPyramidWith.repeat(row * 2 + 1);
}
}
```
Finally, our last step:
> Finally, print our result.
We can print things with a `console.log` statement. Let's print our spaces followed by our characters.
```javascript
function pyramid(characterToBuildPyramidWith, numOfRows) {
for (let row = 0; row < numOfRows; row++) {
const spaces = " ".repeat(numOfRows - row);
const characters = characterToBuildPyramidWith.repeat(row * 2 + 1);
console.log(spaces + characters);
}
}
```
Time to double check our work! Let's call our function:
```javascript
function pyramid(characterToBuildPyramidWith, numOfRows) {
for (let row = 0; row < numOfRows; row++) {
const spaces = " ".repeat(numOfRows - row);
const characters = characterToBuildPyramidWith.repeat(row * 2 + 1);
console.log(spaces + characters);
}
}
pyramid("x", 5);
```
Checking the console, I see:
```txt
x
xxx
xxxxx
xxxxxxx
xxxxxxxxx
```
That looks like a pyramid! But hang on... It looks like we've shifted it one space too far. Let's go back to our logic on paper, because our logic is off (so we don't want to touch the code yet).
1. Look at the string. It is what we want the pyramid to be built by.
2. Look at the integer. It is the amount of rows we want.
3. Add a number of spaces equal to the integer, and then write the string.
4. Go to the next line.
5. Write the same spaces string from before again, but get rid of one space, and write the string.
6. Add two more of the same single string beside it.
7. Write the whole space and string again from the last row, get rid of one space again, and add two more of the same string.
8. Repeat step 7 until you have the same number of rows as the integer.
AHA! Our issue is in step 3.
> 3. Add a number of spaces equal to the integer, and then write the string.
By adding the number of spaces equal to the integer, we've failed to account for the initial pyramid character. Essentially, we need to add one less space.
So we update our logic **first**:
1. Look at the string. It is what we want the pyramid to be built by.
2. Look at the integer. It is the amount of rows we want.
3. Add a number of spaces equal to the integer **less one**, and then write the string.
4. Go to the next line.
5. Write the same spaces string from before again, but get rid of one space, and write the string.
6. Add two more of the same single string beside it.
7. Write the whole space and string again from the last row, get rid of one space again, and add two more of the same string.
8. Repeat step 7 until you have the same number of rows as the integer.
## When Logic Breaks During Coding
I see it all the time. You have worked out your logic, tested it manually, and it looks perfect. You start translating it to code, and halfway through, you realize it's not perfect at all.
Maybe you cannot figure out how to achieve a specific step. Maybe you discover an edge case you did not consider. Maybe the logic just... does not work the way you thought it would.
**Stop coding immediately.**
I know, I know. Your instinct is to keep typing, to try to fix it in code, to hack around the problem. But resist that urge. If your logic needs revision, that is a logic problem, not a code problem. And logic problems should be solved on paper, not in your editor.
Step away from your keyboard. Go back to your plain English instructions. Walk through them again manually. Where does it break? What did you miss? What assumption did you make that turned out to be wrong?
Let us say you are working on that pyramid problem, and while coding you realize: "Wait, how do I know how many spaces to remove each time? Do I start with the full number of spaces, or one less?"
Instead of trying to figure this out in code, go back to your logic:
1. Add a number of spaces equal to the integer, and then write the string.
2. Go to the next line.
3. Write the same spaces string from before again, but get rid of one space, and write the string.
Test it manually: if the integer is 5, I start with 5 spaces. Then I go to the next line and have 4 spaces. Then 3 spaces. Then 2 spaces. Then 1 space. Then 0 spaces. That makes sense!
Now you can go back to your code with clarity. You know that for row 0, you need `numOfRows - 0 - 1` spaces (which is 4 for 5 rows). For row 1, you need `numOfRows - 1 - 1` spaces (which is 3). The pattern is clear because you worked it out in logic first.
Okay, pretend we've tested our change manually (I am sparing you from reading through that process again), and it works. Now we can update our code to reflect our logic change:
```javascript
function pyramid(characterToBuildPyramidWith, numOfRows) {
for (let row = 0; row < numOfRows; row++) {
// Notice how this is the ONLY change I made. We aren't changing anything that isn't updated in the logic on paper.
const spaces = " ".repeat(numOfRows - row - 1);
const characters = characterToBuildPyramidWith.repeat(row * 2 + 1);
console.log(spaces + characters);
}
}
pyramid("x", 5);
```
And we get:
```txt
x
xxx
xxxxx
xxxxxxx
xxxxxxxxx
```
**WE'VE DONE IT!!!!!**
The key principle here is: **if you discover a logic problem while coding, you have not discovered a coding problem. You have discovered that your logic was incomplete.** Fix the logic first, test it manually, and then return to the code.
This might feel inefficient. You might think "I am so close, let me just fix this one thing in code." But I promise you: fixing logic in code is like trying to repair the foundation of a house while you are painting the walls. It will not work, and you will make a bigger mess.
## The Benefits of This Approach
When you think algorithmically first, you gain several advantages:
**You catch logic errors early.** Instead of debugging why your code does not work, you debug why your logic does not work - and logic is much easier to debug than code.
**You separate concerns.** Logic and syntax are two different problems. Solve them separately, and you will solve them more effectively.
**You build confidence.** When your code does not work, you know it is a syntax issue, not a logic issue. That narrows down your debugging significantly.
**You communicate better.** When you can explain your logic in plain English, you can explain it to teammates, ask for help more effectively, and document your code more clearly.
**You learn faster.** By focusing on logic first, you develop stronger problem-solving skills that transfer across languages and technologies.
## The Takeaway
The next time you have to write some code, don't write the code! Do this:
1. Write out your logic in plain English, step-by-step.
2. Test that logic by hand, using multiple test cases.
3. Ideally, you've touched every "branch" in your logical breakdown.
3. Edit and adjust the logic when test cases fail.
4. Once all of your manual test cases pass your hand-written logic, you can finally begin translating it into code.
You will find that your code is cleaner, your bugs are fewer, and your problem-solving skills are stronger. And hey - if you can explain your logic to me in plain English and it works, then translating it to code is just a matter of syntax. And syntax is the easy part!
> 💡 Remember: computers are children who will do only and exactly what you say. Be precise, be thorough, and test your logic before you write your code.
+59
View File
@@ -0,0 +1,59 @@
---
title: "Learning In Public"
date: "2025-10-31"
summary: "Why Naomi hates answering DMs"
---
If you have ever tried to DM me, there is a very good chance that I advised you to ask me the question in a server instead. This is NOT just because I hate DMs - though I do, because running multiple prolific communities means I am absolutely buried in DM request. No, this is actually for YOUR benefit.
Because when I redirect you to a public community instead of my DMs, I am encouraging you to **learn in public**. But why is learning in public so important? Well, there are a number of reasons. Let's explore some!
## Single Source of Truth
When you ask me a question in DMs, I implicitly become the single source of truth. Whatever answer I provide you is *hopefully* accurate, but I am wrong way more often than I am right (even this article should be taken with a grain of salt). But the burden of fact checking my answer now falls entirely on you.
Consider a DM where you ask me "What is the time complexity of a merge sort?". And let's pretend I say it is `O(n log n)`. It might be, it might not be, I'm honestly not sure - even after looking it up. BUT my lack of knowledge isn't the point here. The point is that whatever information I give you is now your burden to confirm.
What if you asked me that question in a public server? I'll give you the same answer: `O(n log n)`. But now because this is a PUBLIC conversation, Jeremy could chime in and say "Actually it's `O(n)` and here is a source". The burden of fact checking has shifted off your shoulders, because the entire community can now weigh in and call out my incorrect answer. And *generally* the collective knowledge of a group will be more accurate than the isolated knowledge of an individual.
## Helping Others Grow
Here's another benefit! I presume that, like many people, you agonised about reaching out to ask me your question. Let's pretend you asked me "How do I centre this `div`?". I cannot speak to your emotions, but I *can* state that it is rather common for developers (especially those in their early learning stages) to be afraid of, embarrassed by, or too shy to ask questions.
The fact that you worked up the courage to ask the question should be celebrated! It can be super duper scary! But when you ask me that question in DMs, *no one else can see it*. Instead, if you ask that question in a server, you and I can have our conversation in a public forum. And maybe Danny had the same question, but was too uncomfortable to ask it. Your initiative has now sparked a conversation from which Danny, and anyone else in the community, can potentially benefit.
## Networking!
If you regularly follow this blog, you probably saw my [post about networking](https://blog.nhcarrigan.com/post/networking) and how it is vital, especially in our current economic downturn. Did you know, though, that asking questions in public IS a networking opportunity??
We've already written at length about how a DM is completely isolated, so let's skip right to the part where you ask me the question publicly. This time, you ask me about the considerations between choosing JavaScript or Python. This is a "juicy" question; that is, questions like this are great catalysts for some in-depth discussions. And that's exactly what we do in this scenario - we have a killer discussion, with multiple people chiming in.
During this discussion, you've effectively demonstrated your ability to learn, curiosity for new information, and technical proficiency all at once. And because this happened in public, someone like Jessica might see the conversation thread. And maybe her team has an opening for a junior developer. That conversation could very well be the spark that leads her to reach out to you for a referral for that role! You never know who is reading a conversation.
## Building a Portfolio
Now, a lot of my work happens on Discord. Which is not the best platform, because it's not indexed by search engines. But EVEN SO, your public conversations can become part of your portfolio! I have conversations waaaaaay back from when I first started my learning journey that I still look back on fondly years later. Because those conversations serve as a lovely reminder of where I started, and thus how far I have come.
But your conversations aren't just beneficial for your own self-actualisation! They're also a great demonstration of your long-term growth and commitment to your craft. If you have spent the last five years engaging in increasingly technical conversations, asking progressively more in-depth questions, and engaging in more complex discourse... then that's something you can leverage on your portfolio to show your own evolution!
And if your conversations are on an indexable platform, like the [freeCodeCamp forum](https://forum.freecodecamp.org), then you even get some free exposure through search engine results!
## Confidence is Key!
I've been scared to ask questions before too. I promise it gets easier. Now I'm running my mouth all day asking questions about everything! But I'm only confident enough to do so because I started asking those questions at the *beginning of my learning journey*. I've gone through all of the "oh god what if people think I'm completely incompetent" anxieties over and over again. And it turns out... No one has ever thought that.
BUT! I know you won't believe me. Just like I didn't believe the folks who told me the same thing in my initial learning. I can tell you it's okay until I am blue in the face, but the most powerful confirmation comes from *experiencing it yourself*. So asking your "silly" questions NOW, when you are still in your first steps of learning to code, will better prepare you to be comfortable asking those questions on the job - where it is VITAL that you are comfortable doing so!
## Learning by Teaching
I, personally, am a HUGE fan of the Socratic Method. SO MUCH SO that I formally adopted it as our [instructional approach at freeCodeCamp](https://www.freecodecamp.org/news/how-to-help-someone-with-their-code-using-the-socratic-method/). I think that there is a lot of value in being guided to reach the solution through your own cognitive reasoning.
Part of that process, then, requires you to explain your understanding along the way. In having to explain your understanding, you are effectively teaching others! In fact, when you do this in public there is a significant chance someone will chime in to ask questions about bits they didn't understand. Which then causes you to dive into your explanation further. And every time you have to break something down and convey it in a way someone else can understand, you strengthen your own competencies!
Not only is that a win for everyone, but being challenged to articulate your thoughts can help you better succeed in your professional career. Part of our work as developers involves explaining our ideas to others - some audiences are technical, and some are not. But all audiences need to be able to understand you. So getting that experience NOW just further positions you for success in your career.
## The Takeaway
I know I rambled on and on. I do that. I'm always yapping. So here's the key takeaway I want you to carry with you forever:
The next time you want to hit that "Send DM" button, consider all of these benefits you will lose out on by asking me a question privately instead of in a public community.
+165
View File
@@ -0,0 +1,165 @@
---
title: "Living With Mental Illness"
date: "2026-02-24"
summary: "An honest account of my journey with schizophrenia, ADHD, anxiety, depression, insomnia, and everything that comes with them."
---
Content warning: this article discusses suicide attempts, psychosis, self-medication with alcohol, and chronic illness. Please take care of yourself.
---
I have a lot of diagnoses. Schizophrenia. ADHD. Anxiety. Depression. Insomnia. Nerve damage. A heart condition. Lumbar spine degeneration. Diverticulosis. Some of those are mental health conditions. Some are physical. Most of them have some impact on the others. All of them are part of who I am.
I'm writing this because I don't see a lot of people talking about mental illness honestly - especially not schizophrenia, which carries more stigma than many other diagnoses. I want to change that, at least in my small corner of the internet. So here it is: my story, as openly as I can tell it.
## It Started When I Was Sixteen
Around the age of sixteen, my hallucinations got bad enough that I attempted suicide.
That's not an easy sentence to write. But it's the truth, and the truth is what this article is for.
The hallucinations were both auditory and visual. And the worst of them had a name: Ragnarok. He was a persistent hallucination - a demon who had his own identity, his own voice, and a singular purpose of feeding me negative thoughts, constantly. That was probably the worst period of my life. I was at my lowest point, drowning in something I didn't have a name for yet.
That's how I ended up in therapy and, eventually, with a diagnosis of schizophrenia.
## The Diagnosis
Having a name for what was happening was a relief. Finally, there was an explanation. But it was also terrifying.
> There is a *lot* of stigma around schizophrenia. Most people's understanding of it comes from movies and TV, and those portrayals are... not great.
The first medication they put me on was Seroquel. It worked - in the sense that it quieted Ragnarok and brought the hallucinations down. But the side effects were brutal. I felt like a complete zombie. Not like a person, just like a shell going through the motions. That's not living.
So I did what a lot of people do when treatment feels worse than the illness: I stopped going to therapy. I couldn't sustain it. And without that support, I just... struggle bussed. For years.
## The Retail Years
I spent a long time working in retail, and I won't mince words: it was miserable. The environment was high-stress, high-pressure, and absolutely not designed for someone managing a chaotic brain. The hallucinations never fully went away during that period - they just became the background noise of my life. I self-medicated with alcohol, because I couldn't sleep without it and I didn't know what else to do.
Looking back, the insomnia and the schizophrenia were feeding each other in a vicious loop. Sleep deprivation makes psychotic symptoms worse. Being symptomatic made it harder to sleep. I couldn't fall asleep, couldn't stay asleep, and was pouring absurd amounts of alcohol into myself to try to bridge the gap.
I'm not proud of that period. But I also don't think past-me deserves judgement for it. She was doing what she could to survive.
## 2020: The Year Everything Shifted
At the end of March 2020, I left my retail job. I had a decent nest egg - unemployment settlement, cashed out pension - and my plan was to take six months off, play video games, and then eventually go find work in Human Resources.
I got bored after two weeks.
I'd always loved tech, so I figured learning to code might be a fun hobby. I started working through freeCodeCamp, and something clicked almost immediately. It was genuinely fun. And then my ADHD brain - which I wouldn't be diagnosed with for another year - decided it had found its new obsession.
I was spending 10 to 12 hours a day, every single day, learning to code. I hit burnout hard in August, took a couple of weeks off, and then picked it right back up. By the end of 2020, what had started as a hobby had become the foundation of an entirely new career.
But here's the thing that mattered most: once I left that retail environment, my major hallucinations subsided significantly. The change in stress levels, the change in environment, the removal of that constant grinding pressure - it made a real difference. Ragnarok faded. The demons got quieter.
## Coming Out
In January 2022, I came out as transgender.
It was, honestly, a lot of anxiety at first. Coming out is not a small thing - there's vulnerability and fear baked into every step of it. But I started HRT in June 2022, and fairly quickly after that, my mental health started to genuinely improve. Not just stabilise - *improve*. There's something about finally being yourself, in your own body, that has effects that ripple out into everything else.
Transition has been one of the best things I've ever done for my mental health. I didn't expect that going in, but here we are.
## The ADHD Revelation
In 2021, I was diagnosed with ADHD.
My reaction was essentially: *oh yeah, that makes all the sense.*
I'd had it my whole life. The hyperfixation on coding. The years of inconsistent school performance. The manic episodes and tendencies that nobody could quite explain. The difficulty maintaining routines, the difficulty stopping once I'd started something interesting.
And the memories. My brain, it turns out, makes up memories. I can have vivid, detailed recollections of things that never happened - events I would swear on my life occurred, that everyone around me will tell you simply didn't. That's not something most people associate with ADHD, but it's a real and disorienting part of how my working memory functions (or doesn't). Growing up, I just thought something was wrong with me in a way that had no name.
Going undiagnosed for twenty-nine years meant decades of struggles that had no explanation. My own mother used to describe me as "spazztic." That word carries weight - the way people around you name your chaos when they don't understand it, when *you* don't understand it. You just know you're different and you don't know why.
Getting the diagnosis was one of those bittersweet relief moments. Yes, it's good to finally know. And also: wow, that would have been useful information thirty years ago.
## The Full Picture
The anxiety and depression came bundled in with everything else - the depression especially feels like it's been there as long as the schizophrenia, all tangled up together. The anxiety followed a few years later. Both are managed with medication. I'm on, as I affectionately describe it, a crap ton of medication.
The insomnia has also been with me for most of my life. I can't fall asleep. I can't stay asleep. These days I'm on prescription medication that helps considerably, and that's made a real difference to everything else - because when sleep deprivation is feeding your other conditions, getting sleep under control has knock-on effects.
Then there are the physical conditions. Diverticulosis is newer - the dietary changes have been a struggle, giving up foods I love for health reasons is never easy. But the other stuff: nerve damage, a heart condition, lumbar spine degeneration. Those are harder to sit with emotionally.
> Those conditions aren't going to get better. And if they get worse, my quality of life will probably tank. That's genuinely terrifying to live with.
Chronic and degenerative conditions carry their own mental and emotional weight that's separate from anything else on this list. The uncertainty, the anticipatory grief for capabilities you might lose - it's its own kind of burden.
## Tech Has Been Different
I want to be clear that tech is not a paradise for people with disabilities or mental health conditions. I've faced discrimination. I've had clients who were less than understanding of my erratic, ADHD-driven schedule. I worked for one employer that was very much a "good ol' boys" club - where every female employee, myself included, had our ideas dismissed and got steamrolled in every meeting.
But overall? Tech has been significantly better for me than retail was. My employers have been more flexible, more accommodating, and more willing to work with the reality of how my brain functions.
When it comes to disclosure, I don't lead with my diagnoses in interviews. I'm an open book and I'll share whatever feels relevant, but I've rarely needed to formally request ADA accommodations - things tend to just get worked out naturally. Mostly it comes up on its own, especially after a rough few days. And in tech, I've found that most employers have been pretty chill about it. That's not universal, as the discrimination I mentioned proves, but it's been the norm.
## Remote Work Changed Everything
If I had to name the single biggest structural factor in my mental health stability as a tech worker, it would be remote work. Not therapy. Not medication (though those matter enormously). Remote work.
Working from home means I get to control my entire environment. I blast my music. I take breaks to play with my cats. I work whatever erratic hours align with how my brain is functioning that day - and I make up for the scattered hours by working stupidly long ones when I'm in flow.
What a bad day looks like depends on which condition is causing it. An ADHD day means I'm manic and scattered, constantly switching between tasks, probably saying the most unhinged things in Slack. An insomnia day means I take a slow morning and ease in. A depression or schizophrenia day - which are rare now, but still happen - means I just take the day off, full stop. I can switch my notifications to my phone, step away from the desk, decompress, and come back when I'm ready. In an office, none of that would be possible.
And then there's masking. In an office environment, there's an enormous amount of pressure to perform neurotypicality all day - to look busy, to hold it together visibly, to hide the chaos. At home, that's just not a thing. I try to be put together when I'm on camera in a meeting. The rest of the time?
> I get to be my unapologetically authentic self. And that is when I do my best work.
## What It Actually Looks Like Now
Day-to-day, schizophrenia for me mostly means corner-of-the-eye visual hallucinations and random sounds. Most of the time, I can tell they aren't real. I know the difference between what's actually there and what my brain is generating. That ability - to recognise and reality-check - is something I did not have at sixteen.
The ADHD, even medicated, is a constant presence. My executive function is still rubbish. I have calendar notifications set for things like taking a shower and eating meals - not as suggestions, but as genuine reminders I actually need. My sister reminds me to drink water. The external scaffolding I've built around myself isn't optional: it's how I function. Without the ticketing system, the calendar, the reminders, the routines - things don't get done.
I'm still working on finding the right medication cocktail, which is its own ongoing saga. As of right now, my ADHD medications were just adjusted and I am an absolute mess - which is why I took last week off work. My sister helps me enormously. Having that support network matters more than I can express.
Managing mental illness is not a destination. It's not something you solve and then it's done. It's ongoing. It's a constant, sometimes frustrating, always-adjusting process of figuring out what works right now, because what works right now might not be what worked six months ago.
## What I Wish People Understood About Schizophrenia
The media portrayal of schizophrenia is so wildly divorced from the actual lived experience that it almost doesn't bear comparison.
It is not constant, dramatic hallucinations. It is not "tripping balls." It is not the dangerous, unpredictable violence that gets portrayed in horror movies.
> Schizophrenia, for me, is an overall lack of grasp on reality. It's the world not quite holding together the way it should. It's knowing, intellectually, that something isn't real while your brain insists it is.
It looks different for different people. But it does not look like what the movies show you.
## What I Wish People Knew About ADHD
People hear "ADHD" and think it means you can't focus. That's not quite right.
The more accurate description is that your brain does not have a volume dial for attention. It has an on/off switch, and you don't control it. When something captures your interest, the switch flips on and you are *locked in* - sometimes for hours, sometimes to the exclusion of literally everything else. When something doesn't capture your interest, no amount of willpower will flip that switch. The attention just isn't there.
But it goes so far beyond focus. ADHD is a full executive function disorder. It looks like this:
> "Okay, I need to do the thing. Wait, what was I doing? Oh right, I was going to check Bluesky really quick. ...Forty minutes later... Wait, no, I need to do the thing. Actually, I should have a smoke first. Wait, what was I doing? Oh, I gotta pee."
ADHD is spilling coffee all over yourself because you forgot you were holding the cup. It's knowing there are exactly 46 slats in your blinds because you counted them three times during a meeting. It's setting your phone down on your desk directly in front of you, and then spending fifteen minutes looking for your phone.
> It's not laziness. It's not a lack of trying. It's a brain that is genuinely fighting against you on the most basic task management, every single day.
## The Upsides (Yes, There Are Some)
I said earlier that my success has come *because* of my conditions, not in spite of them. Let me be specific about what I mean.
My ADHD has made me the absolute queen of writing tickets. If something isn't in a ticket, it does not exist in my brain - it will be lost to the void. So I ticket *everything*. Every task, every bug, every idea, every follow-up. My Asana board is colour-coded, sorted by due date, grouped by project, and it is genuinely beautiful. My colleagues can pull it up at any moment and get a complete picture of everything I'm working on and when it's due.
That system didn't come from discipline. It came from necessity - because ADHD left me no other choice.
And my anxiety? The crippling, exhausting fear of failure and of letting people down? It means I am *constantly* asking for feedback on my performance. Constantly looking for ways to grow. Constantly trying to be better. The fear that drives the anxiety is real and unpleasant, but the behaviour it produces has made me someone who actively shapes their own professional development rather than waiting to be told how they're doing.
Your conditions are not just obstacles. They shaped the way you work, the way you think, and the adaptations you've built. Those adaptations often turn out to be genuine strengths.
## To Anyone Who Is Struggling
If you're reading this and you recognise yourself somewhere in it - the late diagnoses, the wrong medications, the self-medicating, the years of white-knuckling it through environments that weren't built for you - I want you to know this:
You can still find success. Not *in spite of* your mental illnesses, but *because* of them.
The hyperfixation that made me lose twelve hours to coding every day? That's ADHD. That's also how I built a career from scratch in under a year. The resilience I have, the depth of empathy I carry for people who are struggling, the way I understand what rock bottom actually looks like - those things come directly from everything I've been through.
Your brain is not broken. It's different. And different, with the right circumstances and the right support, can be extraordinary.
I'm still figuring it out. I might always be figuring it out. That's okay.
+79 -44
View File
@@ -1,46 +1,87 @@
@tailwind base; @import "tailwindcss";
@tailwind components;
@tailwind utilities;
* { @theme {
font-family: "Vampyr", monospace; --color-background: var(--background);
--color-foreground: var(--foreground);
} }
h1 { @layer base {
@apply text-4xl; h1 {
@apply text-4xl;
}
h2 {
@apply text-2xl;
}
a {
@apply underline;
}
li {
@apply list-disc;
@apply list-inside;
@apply text-left;
}
p {
@apply text-justify;
@apply mb-2;
}
img {
@apply mx-auto;
}
blockquote,
blockquote p {
@apply text-center;
}
blockquote {
border-left: 5px solid var(--accent);
box-shadow: inset 4px 0 10px -4px var(--accent);
padding-left: 1rem;
margin: 1rem;
}
figcaption {
@apply text-sm;
@apply text-center;
@apply italic;
}
pre {
@apply text-left;
@apply bg-gray-100;
@apply p-2;
@apply rounded-md;
@apply border;
@apply border-gray-300;
@apply overflow-x-auto;
@apply whitespace-pre-wrap;
@apply break-words;
@apply text-sm;
@apply font-mono;
}
code:not(pre code) {
@apply text-sm;
@apply font-mono;
@apply bg-gray-100;
@apply p-1;
@apply rounded-md;
@apply border;
@apply border-gray-300;
@apply overflow-x-auto;
@apply whitespace-pre-wrap;
@apply break-words;
}
} }
h2 { .is-dark blockquote,
@apply text-2xl; .is-dark blockquote p {
} color: var(--foreground);
a {
@apply underline;
}
li {
@apply list-disc;
@apply list-inside;
@apply text-left;
}
p {
@apply text-justify;
@apply mb-2;
}
img {
@apply mx-auto;
}
blockquote,
blockquote p {
@apply text-center;
}
blockquote {
border: 2px dotted;
margin: 1rem;
} }
@layer utilities { @layer utilities {
@@ -48,9 +89,3 @@ blockquote {
text-wrap: balance; text-wrap: balance;
} }
} }
figcaption {
@apply text-sm;
@apply text-center;
@apply italic;
}
+11 -15
View File
@@ -3,20 +3,15 @@
* @license Naomi's Public License * @license Naomi's Public License
* @author Naomi Carrigan * @author Naomi Carrigan
*/ */
import { Inter } from "next/font/google";
import Script from "next/script"; import Script from "next/script";
import type { Metadata } from "next"; import type { Metadata } from "next";
import type { JSX, ReactNode } from "react"; import type { JSX, ReactNode } from "react";
// eslint-disable-next-line import/no-unassigned-import -- Import global styles. // eslint-disable-next-line import/no-unassigned-import -- Import global styles.
import "./globals.css"; import "./globals.css";
// eslint-disable-next-line new-cap -- This is a function call.
const inter = Inter({ subsets: [ "latin" ] });
const metadata: Metadata = { const metadata: Metadata = {
description: description: "The personal musings of a transfem software engineer.",
"The personal musings of a transfem software engineer.", openGraph: {
openGraph: {
images: "https://cdn.nhcarrigan.com/og-image.png", images: "https://cdn.nhcarrigan.com/og-image.png",
}, },
title: "Naomi's Blog", title: "Naomi's Blog",
@@ -41,6 +36,14 @@ const RootLayout = ({
}>): JSX.Element => { }>): JSX.Element => {
return ( return (
<html lang="en"> <html lang="en">
<head>
<link href="https://cdn.nhcarrigan.com/logo.png" rel="icon" sizes="any" />
<link
href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/styles/default.min.css"
precedence="default"
rel="stylesheet"
></link>
</head>
<Script <Script
async={true} async={true}
defer={true} defer={true}
@@ -48,14 +51,7 @@ const RootLayout = ({
strategy={"afterInteractive"} strategy={"afterInteractive"}
type="text/javascript" type="text/javascript"
></Script> ></Script>
<link <body>{children}</body>
href="https://cdn.nhcarrigan.com/logo.png"
rel="icon"
sizes="any"
/>
<body className={inter.className}>
{children}
</body>
</html> </html>
); );
}; };
+2 -1
View File
@@ -5,6 +5,7 @@
*/ */
import Markdown from "react-markdown"; import Markdown from "react-markdown";
import rehypeHighlight from "rehype-highlight";
import rehypeRaw from "rehype-raw"; import rehypeRaw from "rehype-raw";
import remarkGfm from "remark-gfm"; import remarkGfm from "remark-gfm";
import type { JSX } from "react"; import type { JSX } from "react";
@@ -34,7 +35,7 @@ const Page = async({
<h2 className="text-center">{post.data.summary}</h2> <h2 className="text-center">{post.data.summary}</h2>
<p className="text-center">{`A ${post.data.readtime}.`}</p> <p className="text-center">{`A ${post.data.readtime}.`}</p>
<Rule /> <Rule />
<Markdown rehypePlugins={[ rehypeRaw ]} remarkPlugins={[ remarkGfm ]}> <Markdown rehypePlugins={[ rehypeRaw, rehypeHighlight ]} remarkPlugins={[ remarkGfm ]}>
{post.content} {post.content}
</Markdown> </Markdown>
<Rule /> <Rule />
-18
View File
@@ -1,18 +0,0 @@
import type { Config } from "tailwindcss";
export default {
content: [
"./src/pages/**/*.{js,ts,jsx,tsx,mdx}",
"./src/components/**/*.{js,ts,jsx,tsx,mdx}",
"./src/app/**/*.{js,ts,jsx,tsx,mdx}",
],
theme: {
extend: {
colors: {
background: "var(--background)",
foreground: "var(--foreground)",
},
},
},
plugins: [],
} satisfies Config;
+19 -5
View File
@@ -1,7 +1,11 @@
{ {
"compilerOptions": { "compilerOptions": {
"target": "ES2017", "target": "ES2017",
"lib": ["dom", "dom.iterable", "esnext"], "lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true, "allowJs": true,
"skipLibCheck": true, "skipLibCheck": true,
"strict": true, "strict": true,
@@ -11,7 +15,7 @@
"moduleResolution": "bundler", "moduleResolution": "bundler",
"resolveJsonModule": true, "resolveJsonModule": true,
"isolatedModules": true, "isolatedModules": true,
"jsx": "preserve", "jsx": "react-jsx",
"incremental": true, "incremental": true,
"plugins": [ "plugins": [
{ {
@@ -19,9 +23,19 @@
} }
], ],
"paths": { "paths": {
"@/*": ["./src/*"] "@/*": [
"./src/*"
]
} }
}, },
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], "include": [
"exclude": ["node_modules"] "next-env.d.ts",
"**/*.ts",
"**/*.tsx",
".next/types/**/*.ts",
".next/dev/types/**/*.ts"
],
"exclude": [
"node_modules"
]
} }