commit bea1f0741d3d63d6fae21b90d9bb0bee46d8fa0e Author: Daniel Kluge Date: Sat Mar 21 14:37:03 2026 +0100 Inital commit of Astro Website diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..16d54bb --- /dev/null +++ b/.gitignore @@ -0,0 +1,24 @@ +# build output +dist/ +# generated types +.astro/ + +# dependencies +node_modules/ + +# logs +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* + + +# environment variables +.env +.env.production + +# macOS-specific files +.DS_Store + +# jetbrains setting folder +.idea/ diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..22a1505 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,4 @@ +{ + "recommendations": ["astro-build.astro-vscode"], + "unwantedRecommendations": [] +} diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..d642209 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,11 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "command": "./node_modules/.bin/astro dev", + "name": "Development server", + "request": "launch", + "type": "node-terminal" + } + ] +} diff --git a/README.md b/README.md new file mode 100644 index 0000000..87b813a --- /dev/null +++ b/README.md @@ -0,0 +1,43 @@ +# Astro Starter Kit: Minimal + +```sh +npm create astro@latest -- --template minimal +``` + +> 🧑‍🚀 **Seasoned astronaut?** Delete this file. Have fun! + +## 🚀 Project Structure + +Inside of your Astro project, you'll see the following folders and files: + +```text +/ +├── public/ +├── src/ +│ └── pages/ +│ └── index.astro +└── package.json +``` + +Astro looks for `.astro` or `.md` files in the `src/pages/` directory. Each page is exposed as a route based on its file name. + +There's nothing special about `src/components/`, but that's where we like to put any Astro/React/Vue/Svelte/Preact components. + +Any static assets, like images, can be placed in the `public/` directory. + +## 🧞 Commands + +All commands are run from the root of the project, from a terminal: + +| Command | Action | +| :------------------------ | :----------------------------------------------- | +| `npm install` | Installs dependencies | +| `npm run dev` | Starts local dev server at `localhost:4321` | +| `npm run build` | Build your production site to `./dist/` | +| `npm run preview` | Preview your build locally, before deploying | +| `npm run astro ...` | Run CLI commands like `astro add`, `astro check` | +| `npm run astro -- --help` | Get help using the Astro CLI | + +## 👀 Want to learn more? + +Feel free to check [our documentation](https://docs.astro.build) or jump into our [Discord server](https://astro.build/chat). diff --git a/astro.config.mjs b/astro.config.mjs new file mode 100644 index 0000000..2f0b61f --- /dev/null +++ b/astro.config.mjs @@ -0,0 +1,39 @@ +import { defineConfig, fontProviders } from 'astro/config'; +import icon from 'astro-icon'; +import mdx from '@astrojs/mdx'; +//import { remarkModifiedTime } from './src/remark-modified-time.mjs'; +import remarkMath from "remark-math"; +import rehypeMathjax from "rehype-mathjax" +import rehypeCallouts from "rehype-callouts"; +// @ts-ignore +import { remarkKroki } from "remark-kroki"; + +export default defineConfig({ + fonts: [{ + provider: fontProviders.fontsource(), + name: "Cascadia Code", + cssVariable: "--font-cascadia-code", + fallbacks: ["monospace"], + }], + integrations: [icon(), mdx()], + markdown: { + remarkPlugins: [ +// remarkModifiedTime, + remarkMath, [ + remarkKroki, { + alias: ["mermaid", "tikz"], + server: "https://kroki.io", + target: "mdx3", + output: "inline-svg" + } + ]], + rehypePlugins: [rehypeMathjax, [rehypeCallouts, { theme: "obsidian" }]], + shikiConfig: { + theme: "one-dark-pro", + }, + syntaxHighlight: { + type: 'shiki', + excludeLangs: ['mermaid', 'math'], + }, + } +}); \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..8ff090e --- /dev/null +++ b/package-lock.json @@ -0,0 +1,4563 @@ +{ + "name": "website-ag", + "version": "0.0.1", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "website-ag", + "version": "0.0.1", + "dependencies": { + "@astrojs/mdx": "^5.0.2", + "@iconify-json/lucide": "^1.2.98", + "@iconify-json/simple-icons": "^1.2.74", + "astro": "^6.0.8", + "astro-icon": "^1.1.5", + "rehype-callouts": "^2.1.2", + "rehype-mathjax": "^7.1.0", + "remark-kroki": "^0.3.8", + "remark-math": "^6.0.0" + }, + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/@antfu/install-pkg": { + "version": "1.1.0", + "license": "MIT", + "dependencies": { + "package-manager-detector": "^1.3.0", + "tinyexec": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@antfu/utils": { + "version": "8.1.1", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@astrojs/compiler": { + "version": "3.0.1", + "license": "MIT" + }, + "node_modules/@astrojs/internal-helpers": { + "version": "0.8.0", + "license": "MIT", + "dependencies": { + "picomatch": "^4.0.3" + } + }, + "node_modules/@astrojs/markdown-remark": { + "version": "7.0.1", + "license": "MIT", + "dependencies": { + "@astrojs/internal-helpers": "0.8.0", + "@astrojs/prism": "4.0.1", + "github-slugger": "^2.0.0", + "hast-util-from-html": "^2.0.3", + "hast-util-to-text": "^4.0.2", + "js-yaml": "^4.1.1", + "mdast-util-definitions": "^6.0.0", + "rehype-raw": "^7.0.0", + "rehype-stringify": "^10.0.1", + "remark-gfm": "^4.0.1", + "remark-parse": "^11.0.0", + "remark-rehype": "^11.1.2", + "remark-smartypants": "^3.0.2", + "shiki": "^4.0.0", + "smol-toml": "^1.6.0", + "unified": "^11.0.5", + "unist-util-remove-position": "^5.0.0", + "unist-util-visit": "^5.1.0", + "unist-util-visit-parents": "^6.0.2", + "vfile": "^6.0.3" + } + }, + "node_modules/@astrojs/mdx": { + "version": "5.0.2", + "license": "MIT", + "dependencies": { + "@astrojs/markdown-remark": "7.0.1", + "@mdx-js/mdx": "^3.1.1", + "acorn": "^8.16.0", + "es-module-lexer": "^2.0.0", + "estree-util-visit": "^2.0.0", + "hast-util-to-html": "^9.0.5", + "piccolore": "^0.1.3", + "rehype-raw": "^7.0.0", + "remark-gfm": "^4.0.1", + "remark-smartypants": "^3.0.2", + "source-map": "^0.7.6", + "unist-util-visit": "^5.1.0", + "vfile": "^6.0.3" + }, + "engines": { + "node": ">=22.12.0" + }, + "peerDependencies": { + "astro": "^6.0.0" + } + }, + "node_modules/@astrojs/prism": { + "version": "4.0.1", + "license": "MIT", + "dependencies": { + "prismjs": "^1.30.0" + }, + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/@astrojs/telemetry": { + "version": "3.3.0", + "license": "MIT", + "dependencies": { + "ci-info": "^4.2.0", + "debug": "^4.4.0", + "dlv": "^1.1.3", + "dset": "^3.1.4", + "is-docker": "^3.0.0", + "is-wsl": "^3.1.0", + "which-pm-runs": "^1.1.0" + }, + "engines": { + "node": "18.20.8 || ^20.3.0 || >=22.0.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.2", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.0", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@capsizecss/unpack": { + "version": "4.0.0", + "license": "MIT", + "dependencies": { + "fontkitten": "^1.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@clack/core": { + "version": "1.1.0", + "license": "MIT", + "dependencies": { + "sisteransi": "^1.0.5" + } + }, + "node_modules/@clack/prompts": { + "version": "1.1.0", + "license": "MIT", + "dependencies": { + "@clack/core": "1.1.0", + "sisteransi": "^1.0.5" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.27.4", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@iconify-json/lucide": { + "version": "1.2.98", + "license": "ISC", + "dependencies": { + "@iconify/types": "*" + } + }, + "node_modules/@iconify-json/simple-icons": { + "version": "1.2.74", + "license": "CC0-1.0", + "dependencies": { + "@iconify/types": "*" + } + }, + "node_modules/@iconify/tools": { + "version": "4.2.0", + "license": "MIT", + "dependencies": { + "@iconify/types": "^2.0.0", + "@iconify/utils": "^2.3.0", + "cheerio": "^1.1.2", + "domhandler": "^5.0.3", + "extract-zip": "^2.0.1", + "local-pkg": "^1.1.2", + "pathe": "^2.0.3", + "svgo": "^3.3.2", + "tar": "^7.5.2" + } + }, + "node_modules/@iconify/tools/node_modules/svgo": { + "version": "3.3.3", + "license": "MIT", + "dependencies": { + "commander": "^7.2.0", + "css-select": "^5.1.0", + "css-tree": "^2.3.1", + "css-what": "^6.1.0", + "csso": "^5.0.5", + "picocolors": "^1.0.0", + "sax": "^1.5.0" + }, + "bin": { + "svgo": "bin/svgo" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/svgo" + } + }, + "node_modules/@iconify/tools/node_modules/svgo/node_modules/commander": { + "version": "7.2.0", + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/@iconify/tools/node_modules/svgo/node_modules/css-tree": { + "version": "2.3.1", + "license": "MIT", + "dependencies": { + "mdn-data": "2.0.30", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + } + }, + "node_modules/@iconify/tools/node_modules/svgo/node_modules/css-tree/node_modules/mdn-data": { + "version": "2.0.30", + "license": "CC0-1.0" + }, + "node_modules/@iconify/types": { + "version": "2.0.0", + "license": "MIT" + }, + "node_modules/@iconify/utils": { + "version": "2.3.0", + "license": "MIT", + "dependencies": { + "@antfu/install-pkg": "^1.0.0", + "@antfu/utils": "^8.1.0", + "@iconify/types": "^2.0.0", + "debug": "^4.4.0", + "globals": "^15.14.0", + "kolorist": "^1.8.0", + "local-pkg": "^1.0.0", + "mlly": "^1.7.4" + } + }, + "node_modules/@img/colour": { + "version": "1.1.0", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.2.4", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.2.4", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.34.5", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.34.5", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.2.4" + } + }, + "node_modules/@isaacs/fs-minipass": { + "version": "4.0.1", + "license": "ISC", + "dependencies": { + "minipass": "^7.0.4" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "license": "MIT" + }, + "node_modules/@mdx-js/mdx": { + "version": "3.1.1", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdx": "^2.0.0", + "acorn": "^8.0.0", + "collapse-white-space": "^2.0.0", + "devlop": "^1.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "estree-util-scope": "^1.0.0", + "estree-walker": "^3.0.0", + "hast-util-to-jsx-runtime": "^2.0.0", + "markdown-extensions": "^2.0.0", + "recma-build-jsx": "^1.0.0", + "recma-jsx": "^1.0.0", + "recma-stringify": "^1.0.0", + "rehype-recma": "^1.0.0", + "remark-mdx": "^3.0.0", + "remark-parse": "^11.0.0", + "remark-rehype": "^11.0.0", + "source-map": "^0.7.0", + "unified": "^11.0.0", + "unist-util-position-from-estree": "^2.0.0", + "unist-util-stringify-position": "^4.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/@mdx-js/mdx/node_modules/estree-walker": { + "version": "3.0.3", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/@oslojs/encoding": { + "version": "1.1.0", + "license": "MIT" + }, + "node_modules/@rollup/pluginutils": { + "version": "5.3.0", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/pluginutils/node_modules/estree-walker": { + "version": "2.0.2", + "license": "MIT" + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.59.0", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.59.0", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@shikijs/core": { + "version": "4.0.2", + "license": "MIT", + "dependencies": { + "@shikijs/primitive": "4.0.2", + "@shikijs/types": "4.0.2", + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.4", + "hast-util-to-html": "^9.0.5" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/engine-javascript": { + "version": "4.0.2", + "license": "MIT", + "dependencies": { + "@shikijs/types": "4.0.2", + "@shikijs/vscode-textmate": "^10.0.2", + "oniguruma-to-es": "^4.3.4" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/engine-oniguruma": { + "version": "4.0.2", + "license": "MIT", + "dependencies": { + "@shikijs/types": "4.0.2", + "@shikijs/vscode-textmate": "^10.0.2" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/langs": { + "version": "4.0.2", + "license": "MIT", + "dependencies": { + "@shikijs/types": "4.0.2" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/primitive": { + "version": "4.0.2", + "license": "MIT", + "dependencies": { + "@shikijs/types": "4.0.2", + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.4" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/themes": { + "version": "4.0.2", + "license": "MIT", + "dependencies": { + "@shikijs/types": "4.0.2" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/types": { + "version": "4.0.2", + "license": "MIT", + "dependencies": { + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.4" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/vscode-textmate": { + "version": "10.0.2", + "license": "MIT" + }, + "node_modules/@types/debug": { + "version": "4.1.13", + "license": "MIT", + "dependencies": { + "@types/ms": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "license": "MIT" + }, + "node_modules/@types/estree-jsx": { + "version": "1.0.5", + "license": "MIT", + "dependencies": { + "@types/estree": "*" + } + }, + "node_modules/@types/hast": { + "version": "3.0.4", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/katex": { + "version": "0.16.8", + "license": "MIT" + }, + "node_modules/@types/mathjax": { + "version": "0.0.40", + "license": "MIT" + }, + "node_modules/@types/mdast": { + "version": "4.0.4", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/mdx": { + "version": "2.0.13", + "license": "MIT" + }, + "node_modules/@types/ms": { + "version": "2.1.0", + "license": "MIT" + }, + "node_modules/@types/nlcst": { + "version": "2.0.3", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/node": { + "version": "25.5.0", + "license": "MIT", + "optional": true, + "dependencies": { + "undici-types": "~7.18.0" + } + }, + "node_modules/@types/unist": { + "version": "3.0.3", + "license": "MIT" + }, + "node_modules/@types/yauzl": { + "version": "2.10.3", + "license": "MIT", + "optional": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.0", + "license": "ISC" + }, + "node_modules/@xmldom/xmldom": { + "version": "0.9.8", + "license": "MIT", + "engines": { + "node": ">=14.6" + } + }, + "node_modules/acorn": { + "version": "8.16.0", + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/anymatch/node_modules/picomatch": { + "version": "2.3.1", + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "license": "Python-2.0" + }, + "node_modules/aria-query": { + "version": "5.3.2", + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/array-iterate": { + "version": "2.0.1", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/astring": { + "version": "1.9.0", + "license": "MIT", + "bin": { + "astring": "bin/astring" + } + }, + "node_modules/astro": { + "version": "6.0.8", + "license": "MIT", + "dependencies": { + "@astrojs/compiler": "^3.0.0", + "@astrojs/internal-helpers": "0.8.0", + "@astrojs/markdown-remark": "7.0.1", + "@astrojs/telemetry": "3.3.0", + "@capsizecss/unpack": "^4.0.0", + "@clack/prompts": "^1.0.1", + "@oslojs/encoding": "^1.1.0", + "@rollup/pluginutils": "^5.3.0", + "aria-query": "^5.3.2", + "axobject-query": "^4.1.0", + "ci-info": "^4.4.0", + "clsx": "^2.1.1", + "common-ancestor-path": "^2.0.0", + "cookie": "^1.1.1", + "devalue": "^5.6.3", + "diff": "^8.0.3", + "dlv": "^1.1.3", + "dset": "^3.1.4", + "es-module-lexer": "^2.0.0", + "esbuild": "^0.27.3", + "flattie": "^1.1.1", + "fontace": "~0.4.1", + "github-slugger": "^2.0.0", + "html-escaper": "3.0.3", + "http-cache-semantics": "^4.2.0", + "js-yaml": "^4.1.1", + "magic-string": "^0.30.21", + "magicast": "^0.5.2", + "mrmime": "^2.0.1", + "neotraverse": "^0.6.18", + "obug": "^2.1.1", + "p-limit": "^7.3.0", + "p-queue": "^9.1.0", + "package-manager-detector": "^1.6.0", + "piccolore": "^0.1.3", + "picomatch": "^4.0.3", + "rehype": "^13.0.2", + "semver": "^7.7.4", + "shiki": "^4.0.0", + "smol-toml": "^1.6.0", + "svgo": "^4.0.0", + "tinyclip": "^0.1.6", + "tinyexec": "^1.0.2", + "tinyglobby": "^0.2.15", + "tsconfck": "^3.1.6", + "ultrahtml": "^1.6.0", + "unifont": "~0.7.4", + "unist-util-visit": "^5.1.0", + "unstorage": "^1.17.4", + "vfile": "^6.0.3", + "vite": "^7.3.1", + "vitefu": "^1.1.2", + "xxhash-wasm": "^1.1.0", + "yargs-parser": "^22.0.0", + "zod": "^4.3.6" + }, + "bin": { + "astro": "bin/astro.mjs" + }, + "engines": { + "node": ">=22.12.0", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/astrodotbuild" + }, + "optionalDependencies": { + "sharp": "^0.34.0" + } + }, + "node_modules/astro-icon": { + "version": "1.1.5", + "license": "MIT", + "dependencies": { + "@iconify/tools": "^4.0.5", + "@iconify/types": "^2.0.0", + "@iconify/utils": "^2.1.30" + } + }, + "node_modules/axobject-query": { + "version": "4.1.0", + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/bail": { + "version": "2.0.2", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/boolbase": { + "version": "1.0.0", + "license": "ISC" + }, + "node_modules/buffer-crc32": { + "version": "0.2.13", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/ccount": { + "version": "2.0.1", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities": { + "version": "2.0.2", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-html4": { + "version": "2.1.0", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-legacy": { + "version": "3.0.0", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-reference-invalid": { + "version": "2.0.1", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/cheerio": { + "version": "1.2.0", + "license": "MIT", + "dependencies": { + "cheerio-select": "^2.1.0", + "dom-serializer": "^2.0.0", + "domhandler": "^5.0.3", + "domutils": "^3.2.2", + "encoding-sniffer": "^0.2.1", + "htmlparser2": "^10.1.0", + "parse5": "^7.3.0", + "parse5-htmlparser2-tree-adapter": "^7.1.0", + "parse5-parser-stream": "^7.1.2", + "undici": "^7.19.0", + "whatwg-mimetype": "^4.0.0" + }, + "engines": { + "node": ">=20.18.1" + }, + "funding": { + "url": "https://github.com/cheeriojs/cheerio?sponsor=1" + } + }, + "node_modules/cheerio-select": { + "version": "2.1.0", + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-select": "^5.1.0", + "css-what": "^6.1.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/chokidar": { + "version": "5.0.0", + "license": "MIT", + "dependencies": { + "readdirp": "^5.0.0" + }, + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/chownr": { + "version": "3.0.0", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/ci-info": { + "version": "4.4.0", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/clsx": { + "version": "2.1.1", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/collapse-white-space": { + "version": "2.1.0", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/comma-separated-tokens": { + "version": "2.0.3", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/commander": { + "version": "11.1.0", + "license": "MIT", + "engines": { + "node": ">=16" + } + }, + "node_modules/common-ancestor-path": { + "version": "2.0.0", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">= 18" + } + }, + "node_modules/confbox": { + "version": "0.2.4", + "license": "MIT" + }, + "node_modules/cookie": { + "version": "1.1.1", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/cookie-es": { + "version": "1.2.2", + "license": "MIT" + }, + "node_modules/crossws": { + "version": "0.3.5", + "license": "MIT", + "dependencies": { + "uncrypto": "^0.1.3" + } + }, + "node_modules/css-select": { + "version": "5.2.2", + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-tree": { + "version": "3.2.1", + "license": "MIT", + "dependencies": { + "mdn-data": "2.27.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + } + }, + "node_modules/css-what": { + "version": "6.2.2", + "license": "BSD-2-Clause", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/csso": { + "version": "5.0.5", + "license": "MIT", + "dependencies": { + "css-tree": "~2.2.0" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", + "npm": ">=7.0.0" + } + }, + "node_modules/csso/node_modules/css-tree": { + "version": "2.2.1", + "license": "MIT", + "dependencies": { + "mdn-data": "2.0.28", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", + "npm": ">=7.0.0" + } + }, + "node_modules/csso/node_modules/css-tree/node_modules/mdn-data": { + "version": "2.0.28", + "license": "CC0-1.0" + }, + "node_modules/data-uri-to-buffer": { + "version": "4.0.1", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decode-named-character-reference": { + "version": "1.3.0", + "license": "MIT", + "dependencies": { + "character-entities": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/defu": { + "version": "6.1.4", + "license": "MIT" + }, + "node_modules/dequal": { + "version": "2.0.3", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/destr": { + "version": "2.0.5", + "license": "MIT" + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "license": "Apache-2.0", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/devalue": { + "version": "5.6.4", + "license": "MIT" + }, + "node_modules/devlop": { + "version": "1.1.0", + "license": "MIT", + "dependencies": { + "dequal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/diff": { + "version": "8.0.3", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/dlv": { + "version": "1.1.3", + "license": "MIT" + }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/dom-serializer/node_modules/entities": { + "version": "4.5.0", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "BSD-2-Clause" + }, + "node_modules/domhandler": { + "version": "5.0.3", + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "3.2.2", + "license": "BSD-2-Clause", + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/dset": { + "version": "3.1.4", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/encoding-sniffer": { + "version": "0.2.1", + "license": "MIT", + "dependencies": { + "iconv-lite": "^0.6.3", + "whatwg-encoding": "^3.1.1" + }, + "funding": { + "url": "https://github.com/fb55/encoding-sniffer?sponsor=1" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.5", + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/entities": { + "version": "6.0.1", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/es-module-lexer": { + "version": "2.0.0", + "license": "MIT" + }, + "node_modules/esast-util-from-estree": { + "version": "2.0.0", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "devlop": "^1.0.0", + "estree-util-visit": "^2.0.0", + "unist-util-position-from-estree": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/esast-util-from-js": { + "version": "2.0.1", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "acorn": "^8.0.0", + "esast-util-from-estree": "^2.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/esbuild": { + "version": "0.27.4", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.4", + "@esbuild/android-arm": "0.27.4", + "@esbuild/android-arm64": "0.27.4", + "@esbuild/android-x64": "0.27.4", + "@esbuild/darwin-arm64": "0.27.4", + "@esbuild/darwin-x64": "0.27.4", + "@esbuild/freebsd-arm64": "0.27.4", + "@esbuild/freebsd-x64": "0.27.4", + "@esbuild/linux-arm": "0.27.4", + "@esbuild/linux-arm64": "0.27.4", + "@esbuild/linux-ia32": "0.27.4", + "@esbuild/linux-loong64": "0.27.4", + "@esbuild/linux-mips64el": "0.27.4", + "@esbuild/linux-ppc64": "0.27.4", + "@esbuild/linux-riscv64": "0.27.4", + "@esbuild/linux-s390x": "0.27.4", + "@esbuild/linux-x64": "0.27.4", + "@esbuild/netbsd-arm64": "0.27.4", + "@esbuild/netbsd-x64": "0.27.4", + "@esbuild/openbsd-arm64": "0.27.4", + "@esbuild/openbsd-x64": "0.27.4", + "@esbuild/openharmony-arm64": "0.27.4", + "@esbuild/sunos-x64": "0.27.4", + "@esbuild/win32-arm64": "0.27.4", + "@esbuild/win32-ia32": "0.27.4", + "@esbuild/win32-x64": "0.27.4" + } + }, + "node_modules/escape-string-regexp": { + "version": "5.0.0", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/esm": { + "version": "3.2.25", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/estree-util-attach-comments": { + "version": "3.0.0", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-util-build-jsx": { + "version": "3.0.1", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "devlop": "^1.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "estree-walker": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-util-build-jsx/node_modules/estree-walker": { + "version": "3.0.3", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/estree-util-is-identifier-name": { + "version": "3.0.0", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-util-scope": { + "version": "1.0.0", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "devlop": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-util-to-js": { + "version": "2.0.0", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "astring": "^1.8.0", + "source-map": "^0.7.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-util-visit": { + "version": "2.0.0", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/eventemitter3": { + "version": "5.0.4", + "license": "MIT" + }, + "node_modules/exsolve": { + "version": "1.0.8", + "license": "MIT" + }, + "node_modules/extend": { + "version": "3.0.2", + "license": "MIT" + }, + "node_modules/extract-zip": { + "version": "2.0.1", + "license": "BSD-2-Clause", + "dependencies": { + "debug": "^4.1.1", + "get-stream": "^5.1.0", + "yauzl": "^2.10.0" + }, + "bin": { + "extract-zip": "cli.js" + }, + "engines": { + "node": ">= 10.17.0" + }, + "optionalDependencies": { + "@types/yauzl": "^2.9.1" + } + }, + "node_modules/fd-slicer": { + "version": "1.1.0", + "license": "MIT", + "dependencies": { + "pend": "~1.2.0" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fetch-blob": { + "version": "3.2.0", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "paypal", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "dependencies": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + }, + "engines": { + "node": "^12.20 || >= 14.13" + } + }, + "node_modules/flattie": { + "version": "1.1.1", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/fontace": { + "version": "0.4.1", + "license": "MIT", + "dependencies": { + "fontkitten": "^1.0.2" + } + }, + "node_modules/fontkitten": { + "version": "1.0.3", + "license": "MIT", + "dependencies": { + "tiny-inflate": "^1.0.3" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/formdata-polyfill": { + "version": "4.0.10", + "license": "MIT", + "dependencies": { + "fetch-blob": "^3.1.2" + }, + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/get-stream": { + "version": "5.2.0", + "license": "MIT", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/github-slugger": { + "version": "2.0.0", + "license": "ISC" + }, + "node_modules/globals": { + "version": "15.15.0", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/h3": { + "version": "1.15.9", + "license": "MIT", + "dependencies": { + "cookie-es": "^1.2.2", + "crossws": "^0.3.5", + "defu": "^6.1.4", + "destr": "^2.0.5", + "iron-webcrypto": "^1.2.1", + "node-mock-http": "^1.0.4", + "radix3": "^1.1.2", + "ufo": "^1.6.3", + "uncrypto": "^0.1.3" + } + }, + "node_modules/hast-util-from-html": { + "version": "2.0.3", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "devlop": "^1.1.0", + "hast-util-from-parse5": "^8.0.0", + "parse5": "^7.0.0", + "vfile": "^6.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-from-parse5": { + "version": "8.0.3", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "devlop": "^1.0.0", + "hastscript": "^9.0.0", + "property-information": "^7.0.0", + "vfile": "^6.0.0", + "vfile-location": "^5.0.0", + "web-namespaces": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-is-element": { + "version": "3.0.0", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-parse-selector": { + "version": "4.0.0", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-raw": { + "version": "9.1.0", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "@ungap/structured-clone": "^1.0.0", + "hast-util-from-parse5": "^8.0.0", + "hast-util-to-parse5": "^8.0.0", + "html-void-elements": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "parse5": "^7.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0", + "web-namespaces": "^2.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-estree": { + "version": "3.1.3", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "devlop": "^1.0.0", + "estree-util-attach-comments": "^3.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "hast-util-whitespace": "^3.0.0", + "mdast-util-mdx-expression": "^2.0.0", + "mdast-util-mdx-jsx": "^3.0.0", + "mdast-util-mdxjs-esm": "^2.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0", + "style-to-js": "^1.0.0", + "unist-util-position": "^5.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-html": { + "version": "9.0.5", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "ccount": "^2.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-whitespace": "^3.0.0", + "html-void-elements": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0", + "stringify-entities": "^4.0.0", + "zwitch": "^2.0.4" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-jsx-runtime": { + "version": "2.3.6", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "devlop": "^1.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "hast-util-whitespace": "^3.0.0", + "mdast-util-mdx-expression": "^2.0.0", + "mdast-util-mdx-jsx": "^3.0.0", + "mdast-util-mdxjs-esm": "^2.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0", + "style-to-js": "^1.0.0", + "unist-util-position": "^5.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-parse5": { + "version": "8.0.1", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "devlop": "^1.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0", + "web-namespaces": "^2.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-text": { + "version": "4.0.2", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "hast-util-is-element": "^3.0.0", + "unist-util-find-after": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-whitespace": { + "version": "3.0.0", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hastscript": { + "version": "9.0.1", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-parse-selector": "^4.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/html-escaper": { + "version": "3.0.3", + "license": "MIT" + }, + "node_modules/html-void-elements": { + "version": "3.0.0", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/htmlparser2": { + "version": "10.1.0", + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.2.2", + "entities": "^7.0.1" + } + }, + "node_modules/htmlparser2/node_modules/entities": { + "version": "7.0.1", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/http-cache-semantics": { + "version": "4.2.0", + "license": "BSD-2-Clause" + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/inline-style-parser": { + "version": "0.2.7", + "license": "MIT" + }, + "node_modules/iron-webcrypto": { + "version": "1.2.1", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/brc-dd" + } + }, + "node_modules/is-alphabetical": { + "version": "2.0.1", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-alphanumerical": { + "version": "2.0.1", + "license": "MIT", + "dependencies": { + "is-alphabetical": "^2.0.0", + "is-decimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-decimal": { + "version": "2.0.1", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-docker": { + "version": "3.0.0", + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-hexadecimal": { + "version": "2.0.1", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-inside-container": { + "version": "1.0.0", + "license": "MIT", + "dependencies": { + "is-docker": "^3.0.0" + }, + "bin": { + "is-inside-container": "cli.js" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-plain-obj": { + "version": "4.1.0", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-wsl": { + "version": "3.1.1", + "license": "MIT", + "dependencies": { + "is-inside-container": "^1.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/js-yaml": { + "version": "4.1.1", + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/katex": { + "version": "0.16.40", + "funding": [ + "https://opencollective.com/katex", + "https://github.com/sponsors/katex" + ], + "license": "MIT", + "dependencies": { + "commander": "^8.3.0" + }, + "bin": { + "katex": "cli.js" + } + }, + "node_modules/katex/node_modules/commander": { + "version": "8.3.0", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/kebab-case": { + "version": "2.0.2", + "license": "MIT" + }, + "node_modules/kolorist": { + "version": "1.8.0", + "license": "MIT" + }, + "node_modules/local-pkg": { + "version": "1.1.2", + "license": "MIT", + "dependencies": { + "mlly": "^1.7.4", + "pkg-types": "^2.3.0", + "quansync": "^0.2.11" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/longest-streak": { + "version": "3.1.0", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/lru-cache": { + "version": "11.2.7", + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/magicast": { + "version": "0.5.2", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", + "source-map-js": "^1.2.1" + } + }, + "node_modules/markdown-code-block-meta": { + "version": "0.0.2", + "license": "MIT", + "engines": { + "node": "^16.13.0 || >=18.0.0" + } + }, + "node_modules/markdown-extensions": { + "version": "2.0.0", + "license": "MIT", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/markdown-table": { + "version": "3.0.4", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/mathjax-full": { + "version": "3.2.1", + "license": "Apache-2.0", + "dependencies": { + "esm": "^3.2.25", + "mhchemparser": "^4.1.0", + "mj-context-menu": "^0.6.1", + "speech-rule-engine": "^4.0.6" + } + }, + "node_modules/mdast-util-definitions": { + "version": "6.0.0", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "unist-util-visit": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-find-and-replace": { + "version": "3.0.2", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "escape-string-regexp": "^5.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-from-markdown": { + "version": "2.0.3", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark": "^4.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm": { + "version": "3.1.0", + "license": "MIT", + "dependencies": { + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-gfm-autolink-literal": "^2.0.0", + "mdast-util-gfm-footnote": "^2.0.0", + "mdast-util-gfm-strikethrough": "^2.0.0", + "mdast-util-gfm-table": "^2.0.0", + "mdast-util-gfm-task-list-item": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-autolink-literal": { + "version": "2.0.1", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "ccount": "^2.0.0", + "devlop": "^1.0.0", + "mdast-util-find-and-replace": "^3.0.0", + "micromark-util-character": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-footnote": { + "version": "2.1.0", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.1.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-strikethrough": { + "version": "2.0.0", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-table": { + "version": "2.0.0", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "markdown-table": "^3.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-task-list-item": { + "version": "2.0.0", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-math": { + "version": "3.0.0", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "longest-streak": "^3.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.1.0", + "unist-util-remove-position": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx": { + "version": "3.0.0", + "license": "MIT", + "dependencies": { + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-mdx-expression": "^2.0.0", + "mdast-util-mdx-jsx": "^3.0.0", + "mdast-util-mdxjs-esm": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx-expression": { + "version": "2.0.1", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx-jsx": { + "version": "3.2.0", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "ccount": "^2.0.0", + "devlop": "^1.1.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "parse-entities": "^4.0.0", + "stringify-entities": "^4.0.0", + "unist-util-stringify-position": "^4.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdxjs-esm": { + "version": "2.0.1", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-phrasing": { + "version": "4.1.0", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-hast": { + "version": "13.2.1", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@ungap/structured-clone": "^1.0.0", + "devlop": "^1.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "trim-lines": "^3.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-markdown": { + "version": "2.1.2", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "longest-streak": "^3.0.0", + "mdast-util-phrasing": "^4.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "unist-util-visit": "^5.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-string": { + "version": "4.0.0", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdn-data": { + "version": "2.27.1", + "license": "CC0-1.0" + }, + "node_modules/mhchemparser": { + "version": "4.2.1", + "license": "Apache-2.0" + }, + "node_modules/micromark": { + "version": "4.0.2", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-core-commonmark": { + "version": "2.0.3", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-destination": "^2.0.0", + "micromark-factory-label": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-title": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-html-tag-name": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-gfm": { + "version": "3.0.0", + "license": "MIT", + "dependencies": { + "micromark-extension-gfm-autolink-literal": "^2.0.0", + "micromark-extension-gfm-footnote": "^2.0.0", + "micromark-extension-gfm-strikethrough": "^2.0.0", + "micromark-extension-gfm-table": "^2.0.0", + "micromark-extension-gfm-tagfilter": "^2.0.0", + "micromark-extension-gfm-task-list-item": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-autolink-literal": { + "version": "2.1.0", + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-footnote": { + "version": "2.1.0", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-strikethrough": { + "version": "2.1.0", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-table": { + "version": "2.1.1", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-tagfilter": { + "version": "2.0.0", + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-task-list-item": { + "version": "2.1.0", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-math": { + "version": "3.1.0", + "license": "MIT", + "dependencies": { + "@types/katex": "^0.16.0", + "devlop": "^1.0.0", + "katex": "^0.16.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-mdx-expression": { + "version": "3.0.1", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-mdx-expression": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-events-to-acorn": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-mdx-jsx": { + "version": "3.0.2", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "devlop": "^1.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "micromark-factory-mdx-expression": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-events-to-acorn": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-mdx-md": { + "version": "2.0.0", + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-mdxjs": { + "version": "3.0.0", + "license": "MIT", + "dependencies": { + "acorn": "^8.0.0", + "acorn-jsx": "^5.0.0", + "micromark-extension-mdx-expression": "^3.0.0", + "micromark-extension-mdx-jsx": "^3.0.0", + "micromark-extension-mdx-md": "^2.0.0", + "micromark-extension-mdxjs-esm": "^3.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-mdxjs-esm": { + "version": "3.0.0", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-events-to-acorn": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-position-from-estree": "^2.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-factory-destination": { + "version": "2.0.1", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-label": { + "version": "2.0.1", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-mdx-expression": { + "version": "2.0.3", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-events-to-acorn": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-position-from-estree": "^2.0.0", + "vfile-message": "^4.0.0" + } + }, + "node_modules/micromark-factory-space": { + "version": "2.0.1", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-title": { + "version": "2.0.1", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-whitespace": { + "version": "2.0.1", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-character": { + "version": "2.1.1", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-chunked": { + "version": "2.0.1", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-classify-character": { + "version": "2.0.1", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-combine-extensions": { + "version": "2.0.1", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-chunked": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-numeric-character-reference": { + "version": "2.0.2", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-string": { + "version": "2.0.1", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-encode": { + "version": "2.0.1", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-events-to-acorn": { + "version": "2.0.3", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "@types/unist": "^3.0.0", + "devlop": "^1.0.0", + "estree-util-visit": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "vfile-message": "^4.0.0" + } + }, + "node_modules/micromark-util-html-tag-name": { + "version": "2.0.1", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-normalize-identifier": { + "version": "2.0.1", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-resolve-all": { + "version": "2.0.1", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-sanitize-uri": { + "version": "2.0.1", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-subtokenize": { + "version": "2.1.0", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-symbol": { + "version": "2.0.1", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-types": { + "version": "2.0.2", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/mimic-function": { + "version": "5.0.1", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minipass": { + "version": "7.1.3", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/minizlib": { + "version": "3.1.0", + "license": "MIT", + "dependencies": { + "minipass": "^7.1.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/mj-context-menu": { + "version": "0.6.1", + "license": "Apache-2.0" + }, + "node_modules/mlly": { + "version": "1.8.2", + "license": "MIT", + "dependencies": { + "acorn": "^8.16.0", + "pathe": "^2.0.3", + "pkg-types": "^1.3.1", + "ufo": "^1.6.3" + } + }, + "node_modules/mlly/node_modules/pkg-types": { + "version": "1.3.1", + "license": "MIT", + "dependencies": { + "confbox": "^0.1.8", + "mlly": "^1.7.4", + "pathe": "^2.0.1" + } + }, + "node_modules/mlly/node_modules/pkg-types/node_modules/confbox": { + "version": "0.1.8", + "license": "MIT" + }, + "node_modules/mrmime": { + "version": "2.0.1", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/neotraverse": { + "version": "0.6.18", + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/nlcst-to-string": { + "version": "4.0.0", + "license": "MIT", + "dependencies": { + "@types/nlcst": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/node-domexception": { + "version": "1.0.0", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "engines": { + "node": ">=10.5.0" + } + }, + "node_modules/node-fetch": { + "version": "3.3.2", + "license": "MIT", + "dependencies": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" + } + }, + "node_modules/node-fetch-native": { + "version": "1.6.7", + "license": "MIT" + }, + "node_modules/node-mock-http": { + "version": "1.0.4", + "license": "MIT" + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/nth-check": { + "version": "2.1.1", + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/obug": { + "version": "2.1.1", + "funding": [ + "https://github.com/sponsors/sxzz", + "https://opencollective.com/debug" + ], + "license": "MIT" + }, + "node_modules/ofetch": { + "version": "1.5.1", + "license": "MIT", + "dependencies": { + "destr": "^2.0.5", + "node-fetch-native": "^1.6.7", + "ufo": "^1.6.1" + } + }, + "node_modules/ohash": { + "version": "2.0.11", + "license": "MIT" + }, + "node_modules/once": { + "version": "1.4.0", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/oniguruma-parser": { + "version": "0.12.1", + "license": "MIT" + }, + "node_modules/oniguruma-to-es": { + "version": "4.3.5", + "license": "MIT", + "dependencies": { + "oniguruma-parser": "^0.12.1", + "regex": "^6.1.0", + "regex-recursion": "^6.0.2" + } + }, + "node_modules/p-limit": { + "version": "7.3.0", + "license": "MIT", + "dependencies": { + "yocto-queue": "^1.2.1" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-memoize": { + "version": "8.0.0", + "license": "MIT", + "dependencies": { + "mimic-function": "^5.0.1", + "type-fest": "^4.41.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sindresorhus/p-memoize?sponsor=1" + } + }, + "node_modules/p-queue": { + "version": "9.1.0", + "license": "MIT", + "dependencies": { + "eventemitter3": "^5.0.1", + "p-timeout": "^7.0.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-timeout": { + "version": "7.0.1", + "license": "MIT", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-manager-detector": { + "version": "1.6.0", + "license": "MIT" + }, + "node_modules/parse-entities": { + "version": "4.0.2", + "license": "MIT", + "dependencies": { + "@types/unist": "^2.0.0", + "character-entities-legacy": "^3.0.0", + "character-reference-invalid": "^2.0.0", + "decode-named-character-reference": "^1.0.0", + "is-alphanumerical": "^2.0.0", + "is-decimal": "^2.0.0", + "is-hexadecimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/parse-entities/node_modules/@types/unist": { + "version": "2.0.11", + "license": "MIT" + }, + "node_modules/parse-latin": { + "version": "7.0.0", + "license": "MIT", + "dependencies": { + "@types/nlcst": "^2.0.0", + "@types/unist": "^3.0.0", + "nlcst-to-string": "^4.0.0", + "unist-util-modify-children": "^4.0.0", + "unist-util-visit-children": "^3.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/parse5": { + "version": "7.3.0", + "license": "MIT", + "dependencies": { + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5-htmlparser2-tree-adapter": { + "version": "7.1.0", + "license": "MIT", + "dependencies": { + "domhandler": "^5.0.3", + "parse5": "^7.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5-parser-stream": { + "version": "7.1.2", + "license": "MIT", + "dependencies": { + "parse5": "^7.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/pathe": { + "version": "2.0.3", + "license": "MIT" + }, + "node_modules/pend": { + "version": "1.2.0", + "license": "MIT" + }, + "node_modules/piccolore": { + "version": "0.1.3", + "license": "ISC" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.3", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pkg-types": { + "version": "2.3.0", + "license": "MIT", + "dependencies": { + "confbox": "^0.2.2", + "exsolve": "^1.0.7", + "pathe": "^2.0.3" + } + }, + "node_modules/postcss": { + "version": "8.5.8", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/prismjs": { + "version": "1.30.0", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/property-information": { + "version": "7.1.0", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/pump": { + "version": "3.0.4", + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/quansync": { + "version": "0.2.11", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/antfu" + }, + { + "type": "individual", + "url": "https://github.com/sponsors/sxzz" + } + ], + "license": "MIT" + }, + "node_modules/radix3": { + "version": "1.1.2", + "license": "MIT" + }, + "node_modules/readdirp": { + "version": "5.0.0", + "license": "MIT", + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/recma-build-jsx": { + "version": "1.0.0", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-util-build-jsx": "^3.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/recma-jsx": { + "version": "1.0.1", + "license": "MIT", + "dependencies": { + "acorn-jsx": "^5.0.0", + "estree-util-to-js": "^2.0.0", + "recma-parse": "^1.0.0", + "recma-stringify": "^1.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + }, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/recma-parse": { + "version": "1.0.0", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "esast-util-from-js": "^2.0.0", + "unified": "^11.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/recma-stringify": { + "version": "1.0.0", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-util-to-js": "^2.0.0", + "unified": "^11.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/regex": { + "version": "6.1.0", + "license": "MIT", + "dependencies": { + "regex-utilities": "^2.3.0" + } + }, + "node_modules/regex-recursion": { + "version": "6.0.2", + "license": "MIT", + "dependencies": { + "regex-utilities": "^2.3.0" + } + }, + "node_modules/regex-utilities": { + "version": "2.3.0", + "license": "MIT" + }, + "node_modules/rehype": { + "version": "13.0.2", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "rehype-parse": "^9.0.0", + "rehype-stringify": "^10.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-callouts": { + "version": "2.1.2", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.4", + "hast-util-from-html": "^2.0.3", + "hast-util-is-element": "^3.0.0", + "hastscript": "^9.0.1", + "unist-util-visit": "^5.0.0" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + } + }, + "node_modules/rehype-mathjax": { + "version": "7.1.0", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mathjax": "^0.0.40", + "hast-util-to-text": "^4.0.0", + "hastscript": "^9.0.0", + "mathjax-full": "^3.0.0", + "unified": "^11.0.0", + "unist-util-visit-parents": "^6.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-parse": { + "version": "9.0.1", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-from-html": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-raw": { + "version": "7.0.0", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-raw": "^9.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-recma": { + "version": "1.0.0", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "@types/hast": "^3.0.0", + "hast-util-to-estree": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-stringify": { + "version": "10.0.1", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-to-html": "^9.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-gfm": { + "version": "4.0.1", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-gfm": "^3.0.0", + "micromark-extension-gfm": "^3.0.0", + "remark-parse": "^11.0.0", + "remark-stringify": "^11.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-kroki": { + "version": "0.3.8", + "license": "MIT", + "dependencies": { + "is-plain-obj": "^4.1.0", + "kebab-case": "^2.0.2", + "markdown-code-block-meta": "^0.0.2", + "node-fetch": "^3.3.2", + "p-memoize": "^8.0.0", + "unist-util-visit": "^5.0.0" + }, + "engines": { + "node": ">=18.0.0 || ^16.13.0" + } + }, + "node_modules/remark-math": { + "version": "6.0.0", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-math": "^3.0.0", + "micromark-extension-math": "^3.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-mdx": { + "version": "3.1.1", + "license": "MIT", + "dependencies": { + "mdast-util-mdx": "^3.0.0", + "micromark-extension-mdxjs": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-parse": { + "version": "11.0.0", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-from-markdown": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-rehype": { + "version": "11.1.2", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "mdast-util-to-hast": "^13.0.0", + "unified": "^11.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-smartypants": { + "version": "3.0.2", + "license": "MIT", + "dependencies": { + "retext": "^9.0.0", + "retext-smartypants": "^6.0.0", + "unified": "^11.0.4", + "unist-util-visit": "^5.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/remark-stringify": { + "version": "11.0.0", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-to-markdown": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/retext": { + "version": "9.0.0", + "license": "MIT", + "dependencies": { + "@types/nlcst": "^2.0.0", + "retext-latin": "^4.0.0", + "retext-stringify": "^4.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/retext-latin": { + "version": "4.0.0", + "license": "MIT", + "dependencies": { + "@types/nlcst": "^2.0.0", + "parse-latin": "^7.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/retext-smartypants": { + "version": "6.2.0", + "license": "MIT", + "dependencies": { + "@types/nlcst": "^2.0.0", + "nlcst-to-string": "^4.0.0", + "unist-util-visit": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/retext-stringify": { + "version": "4.0.0", + "license": "MIT", + "dependencies": { + "@types/nlcst": "^2.0.0", + "nlcst-to-string": "^4.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rollup": { + "version": "4.59.0", + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.59.0", + "@rollup/rollup-android-arm64": "4.59.0", + "@rollup/rollup-darwin-arm64": "4.59.0", + "@rollup/rollup-darwin-x64": "4.59.0", + "@rollup/rollup-freebsd-arm64": "4.59.0", + "@rollup/rollup-freebsd-x64": "4.59.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.59.0", + "@rollup/rollup-linux-arm-musleabihf": "4.59.0", + "@rollup/rollup-linux-arm64-gnu": "4.59.0", + "@rollup/rollup-linux-arm64-musl": "4.59.0", + "@rollup/rollup-linux-loong64-gnu": "4.59.0", + "@rollup/rollup-linux-loong64-musl": "4.59.0", + "@rollup/rollup-linux-ppc64-gnu": "4.59.0", + "@rollup/rollup-linux-ppc64-musl": "4.59.0", + "@rollup/rollup-linux-riscv64-gnu": "4.59.0", + "@rollup/rollup-linux-riscv64-musl": "4.59.0", + "@rollup/rollup-linux-s390x-gnu": "4.59.0", + "@rollup/rollup-linux-x64-gnu": "4.59.0", + "@rollup/rollup-linux-x64-musl": "4.59.0", + "@rollup/rollup-openbsd-x64": "4.59.0", + "@rollup/rollup-openharmony-arm64": "4.59.0", + "@rollup/rollup-win32-arm64-msvc": "4.59.0", + "@rollup/rollup-win32-ia32-msvc": "4.59.0", + "@rollup/rollup-win32-x64-gnu": "4.59.0", + "@rollup/rollup-win32-x64-msvc": "4.59.0", + "fsevents": "~2.3.2" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "license": "MIT" + }, + "node_modules/sax": { + "version": "1.6.0", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=11.0.0" + } + }, + "node_modules/semver": { + "version": "7.7.4", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/sharp": { + "version": "0.34.5", + "hasInstallScript": true, + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@img/colour": "^1.0.0", + "detect-libc": "^2.1.2", + "semver": "^7.7.3" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.34.5", + "@img/sharp-darwin-x64": "0.34.5", + "@img/sharp-libvips-darwin-arm64": "1.2.4", + "@img/sharp-libvips-darwin-x64": "1.2.4", + "@img/sharp-libvips-linux-arm": "1.2.4", + "@img/sharp-libvips-linux-arm64": "1.2.4", + "@img/sharp-libvips-linux-ppc64": "1.2.4", + "@img/sharp-libvips-linux-riscv64": "1.2.4", + "@img/sharp-libvips-linux-s390x": "1.2.4", + "@img/sharp-libvips-linux-x64": "1.2.4", + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", + "@img/sharp-libvips-linuxmusl-x64": "1.2.4", + "@img/sharp-linux-arm": "0.34.5", + "@img/sharp-linux-arm64": "0.34.5", + "@img/sharp-linux-ppc64": "0.34.5", + "@img/sharp-linux-riscv64": "0.34.5", + "@img/sharp-linux-s390x": "0.34.5", + "@img/sharp-linux-x64": "0.34.5", + "@img/sharp-linuxmusl-arm64": "0.34.5", + "@img/sharp-linuxmusl-x64": "0.34.5", + "@img/sharp-wasm32": "0.34.5", + "@img/sharp-win32-arm64": "0.34.5", + "@img/sharp-win32-ia32": "0.34.5", + "@img/sharp-win32-x64": "0.34.5" + } + }, + "node_modules/shiki": { + "version": "4.0.2", + "license": "MIT", + "dependencies": { + "@shikijs/core": "4.0.2", + "@shikijs/engine-javascript": "4.0.2", + "@shikijs/engine-oniguruma": "4.0.2", + "@shikijs/langs": "4.0.2", + "@shikijs/themes": "4.0.2", + "@shikijs/types": "4.0.2", + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.4" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "license": "MIT" + }, + "node_modules/smol-toml": { + "version": "1.6.0", + "license": "BSD-3-Clause", + "engines": { + "node": ">= 18" + }, + "funding": { + "url": "https://github.com/sponsors/cyyynthia" + } + }, + "node_modules/source-map": { + "version": "0.7.6", + "license": "BSD-3-Clause", + "engines": { + "node": ">= 12" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/space-separated-tokens": { + "version": "2.0.2", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/speech-rule-engine": { + "version": "4.1.2", + "license": "Apache-2.0", + "dependencies": { + "@xmldom/xmldom": "0.9.8", + "commander": "13.1.0", + "wicked-good-xpath": "1.3.0" + }, + "bin": { + "sre": "bin/sre" + } + }, + "node_modules/speech-rule-engine/node_modules/commander": { + "version": "13.1.0", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/stringify-entities": { + "version": "4.0.4", + "license": "MIT", + "dependencies": { + "character-entities-html4": "^2.0.0", + "character-entities-legacy": "^3.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/style-to-js": { + "version": "1.1.21", + "license": "MIT", + "dependencies": { + "style-to-object": "1.0.14" + } + }, + "node_modules/style-to-object": { + "version": "1.0.14", + "license": "MIT", + "dependencies": { + "inline-style-parser": "0.2.7" + } + }, + "node_modules/svgo": { + "version": "4.0.1", + "license": "MIT", + "dependencies": { + "commander": "^11.1.0", + "css-select": "^5.1.0", + "css-tree": "^3.0.1", + "css-what": "^6.1.0", + "csso": "^5.0.5", + "picocolors": "^1.1.1", + "sax": "^1.5.0" + }, + "bin": { + "svgo": "bin/svgo.js" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/svgo" + } + }, + "node_modules/tar": { + "version": "7.5.12", + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.1.0", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/tiny-inflate": { + "version": "1.0.3", + "license": "MIT" + }, + "node_modules/tinyclip": { + "version": "0.1.12", + "license": "MIT", + "engines": { + "node": "^16.14.0 || >= 17.3.0" + } + }, + "node_modules/tinyexec": { + "version": "1.0.4", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/trim-lines": { + "version": "3.0.1", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/trough": { + "version": "2.2.0", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/tsconfck": { + "version": "3.1.6", + "license": "MIT", + "bin": { + "tsconfck": "bin/tsconfck.js" + }, + "engines": { + "node": "^18 || >=20" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/type-fest": { + "version": "4.41.0", + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ufo": { + "version": "1.6.3", + "license": "MIT" + }, + "node_modules/ultrahtml": { + "version": "1.6.0", + "license": "MIT" + }, + "node_modules/uncrypto": { + "version": "0.1.3", + "license": "MIT" + }, + "node_modules/undici": { + "version": "7.24.5", + "license": "MIT", + "engines": { + "node": ">=20.18.1" + } + }, + "node_modules/undici-types": { + "version": "7.18.2", + "license": "MIT", + "optional": true + }, + "node_modules/unified": { + "version": "11.0.5", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "bail": "^2.0.0", + "devlop": "^1.0.0", + "extend": "^3.0.0", + "is-plain-obj": "^4.0.0", + "trough": "^2.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unifont": { + "version": "0.7.4", + "license": "MIT", + "dependencies": { + "css-tree": "^3.1.0", + "ofetch": "^1.5.1", + "ohash": "^2.0.11" + } + }, + "node_modules/unist-util-find-after": { + "version": "5.0.0", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-is": { + "version": "6.0.1", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-modify-children": { + "version": "4.0.0", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "array-iterate": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-position": { + "version": "5.0.0", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-position-from-estree": { + "version": "2.0.0", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-remove-position": { + "version": "5.0.0", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-visit": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit": { + "version": "5.1.0", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit-children": { + "version": "3.0.0", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit-parents": { + "version": "6.0.2", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unstorage": { + "version": "1.17.4", + "license": "MIT", + "dependencies": { + "anymatch": "^3.1.3", + "chokidar": "^5.0.0", + "destr": "^2.0.5", + "h3": "^1.15.5", + "lru-cache": "^11.2.0", + "node-fetch-native": "^1.6.7", + "ofetch": "^1.5.1", + "ufo": "^1.6.3" + }, + "peerDependencies": { + "@azure/app-configuration": "^1.8.0", + "@azure/cosmos": "^4.2.0", + "@azure/data-tables": "^13.3.0", + "@azure/identity": "^4.6.0", + "@azure/keyvault-secrets": "^4.9.0", + "@azure/storage-blob": "^12.26.0", + "@capacitor/preferences": "^6 || ^7 || ^8", + "@deno/kv": ">=0.9.0", + "@netlify/blobs": "^6.5.0 || ^7.0.0 || ^8.1.0 || ^9.0.0 || ^10.0.0", + "@planetscale/database": "^1.19.0", + "@upstash/redis": "^1.34.3", + "@vercel/blob": ">=0.27.1", + "@vercel/functions": "^2.2.12 || ^3.0.0", + "@vercel/kv": "^1 || ^2 || ^3", + "aws4fetch": "^1.0.20", + "db0": ">=0.2.1", + "idb-keyval": "^6.2.1", + "ioredis": "^5.4.2", + "uploadthing": "^7.4.4" + }, + "peerDependenciesMeta": { + "@azure/app-configuration": { + "optional": true + }, + "@azure/cosmos": { + "optional": true + }, + "@azure/data-tables": { + "optional": true + }, + "@azure/identity": { + "optional": true + }, + "@azure/keyvault-secrets": { + "optional": true + }, + "@azure/storage-blob": { + "optional": true + }, + "@capacitor/preferences": { + "optional": true + }, + "@deno/kv": { + "optional": true + }, + "@netlify/blobs": { + "optional": true + }, + "@planetscale/database": { + "optional": true + }, + "@upstash/redis": { + "optional": true + }, + "@vercel/blob": { + "optional": true + }, + "@vercel/functions": { + "optional": true + }, + "@vercel/kv": { + "optional": true + }, + "aws4fetch": { + "optional": true + }, + "db0": { + "optional": true + }, + "idb-keyval": { + "optional": true + }, + "ioredis": { + "optional": true + }, + "uploadthing": { + "optional": true + } + } + }, + "node_modules/vfile": { + "version": "6.0.3", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-location": { + "version": "5.0.3", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-message": { + "version": "4.0.3", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vite": { + "version": "7.3.1", + "license": "MIT", + "dependencies": { + "esbuild": "^0.27.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vitefu": { + "version": "1.1.2", + "license": "MIT", + "workspaces": [ + "tests/deps/*", + "tests/projects/*", + "tests/projects/workspace/packages/*" + ], + "peerDependencies": { + "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-beta.0" + }, + "peerDependenciesMeta": { + "vite": { + "optional": true + } + } + }, + "node_modules/web-namespaces": { + "version": "2.0.1", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/web-streams-polyfill": { + "version": "3.3.3", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/whatwg-encoding": { + "version": "3.1.1", + "license": "MIT", + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/whatwg-mimetype": { + "version": "4.0.0", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/which-pm-runs": { + "version": "1.1.0", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/wicked-good-xpath": { + "version": "1.3.0", + "license": "MIT" + }, + "node_modules/wrappy": { + "version": "1.0.2", + "license": "ISC" + }, + "node_modules/xxhash-wasm": { + "version": "1.1.0", + "license": "MIT" + }, + "node_modules/yallist": { + "version": "5.0.0", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/yargs-parser": { + "version": "22.0.0", + "license": "ISC", + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=23" + } + }, + "node_modules/yauzl": { + "version": "2.10.0", + "license": "MIT", + "dependencies": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + }, + "node_modules/yocto-queue": { + "version": "1.2.2", + "license": "MIT", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zod": { + "version": "4.3.6", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zwitch": { + "version": "2.0.4", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..9c3af3d --- /dev/null +++ b/package.json @@ -0,0 +1,25 @@ +{ + "name": "website-ag", + "type": "module", + "version": "0.0.1", + "engines": { + "node": ">=22.12.0" + }, + "scripts": { + "dev": "astro dev", + "build": "astro build", + "preview": "astro preview", + "astro": "astro" + }, + "dependencies": { + "@astrojs/mdx": "^5.0.2", + "@iconify-json/lucide": "^1.2.98", + "@iconify-json/simple-icons": "^1.2.74", + "astro": "^6.0.8", + "astro-icon": "^1.1.5", + "rehype-callouts": "^2.1.2", + "rehype-mathjax": "^7.1.0", + "remark-kroki": "^0.3.8", + "remark-math": "^6.0.0" + } +} diff --git a/public/apple-touch-icon.png b/public/apple-touch-icon.png new file mode 100644 index 0000000..696f14d Binary files /dev/null and b/public/apple-touch-icon.png differ diff --git a/public/favicon-16x16.png b/public/favicon-16x16.png new file mode 100644 index 0000000..a1979ec Binary files /dev/null and b/public/favicon-16x16.png differ diff --git a/public/favicon-32x32.png b/public/favicon-32x32.png new file mode 100644 index 0000000..fe1795e Binary files /dev/null and b/public/favicon-32x32.png differ diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 0000000..6c4d9e1 Binary files /dev/null and b/public/favicon.ico differ diff --git a/public/favicon.svg b/public/favicon.svg new file mode 100644 index 0000000..2f99376 --- /dev/null +++ b/public/favicon.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/og-image.png b/public/og-image.png new file mode 100644 index 0000000..b161c4b Binary files /dev/null and b/public/og-image.png differ diff --git a/src/assets/backgrounds/bubble-static.svg b/src/assets/backgrounds/bubble-static.svg new file mode 100644 index 0000000..b24d3e9 --- /dev/null +++ b/src/assets/backgrounds/bubble-static.svg @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/assets/backgrounds/bubble.svg b/src/assets/backgrounds/bubble.svg new file mode 100644 index 0000000..3c31f1a --- /dev/null +++ b/src/assets/backgrounds/bubble.svg @@ -0,0 +1,226 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/assets/backgrounds/curve-ripple-static.svg b/src/assets/backgrounds/curve-ripple-static.svg new file mode 100644 index 0000000..cb23942 --- /dev/null +++ b/src/assets/backgrounds/curve-ripple-static.svg @@ -0,0 +1,101 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/assets/backgrounds/curve-ripple.svg b/src/assets/backgrounds/curve-ripple.svg new file mode 100644 index 0000000..1059cbe --- /dev/null +++ b/src/assets/backgrounds/curve-ripple.svg @@ -0,0 +1,601 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/assets/backgrounds/dual-ripples-static.svg b/src/assets/backgrounds/dual-ripples-static.svg new file mode 100644 index 0000000..6b419e9 --- /dev/null +++ b/src/assets/backgrounds/dual-ripples-static.svg @@ -0,0 +1,144 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/assets/backgrounds/dual-ripples.svg b/src/assets/backgrounds/dual-ripples.svg new file mode 100644 index 0000000..0f60d46 --- /dev/null +++ b/src/assets/backgrounds/dual-ripples.svg @@ -0,0 +1,859 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/assets/backgrounds/ripple-line-static.svg b/src/assets/backgrounds/ripple-line-static.svg new file mode 100644 index 0000000..8ea35a8 --- /dev/null +++ b/src/assets/backgrounds/ripple-line-static.svg @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/assets/backgrounds/ripple-line.svg b/src/assets/backgrounds/ripple-line.svg new file mode 100644 index 0000000..3e3b762 --- /dev/null +++ b/src/assets/backgrounds/ripple-line.svg @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/assets/logo.svg b/src/assets/logo.svg new file mode 100644 index 0000000..2f99376 --- /dev/null +++ b/src/assets/logo.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/components/BookNavigation.astro b/src/components/BookNavigation.astro new file mode 100644 index 0000000..09016f5 --- /dev/null +++ b/src/components/BookNavigation.astro @@ -0,0 +1,111 @@ +--- +interface Props { + prevUrl?: string; + prevTitle?: string; + nextUrl?: string; + nextTitle?: string; +} +const { prevUrl, prevTitle, nextUrl, nextTitle } = Astro.props; +--- +
+ +
+ + diff --git a/src/components/GlassContainer.astro b/src/components/GlassContainer.astro new file mode 100644 index 0000000..9b646e0 --- /dev/null +++ b/src/components/GlassContainer.astro @@ -0,0 +1,9 @@ +--- +interface Props { + class?: string; +} +const { class: className = '' } = Astro.props; +--- +
+ +
diff --git a/src/components/Navigation.astro b/src/components/Navigation.astro new file mode 100644 index 0000000..4d4b2e8 --- /dev/null +++ b/src/components/Navigation.astro @@ -0,0 +1,101 @@ +--- +const currentPath = Astro.url.pathname; +import Logo from "../assets/logo.svg"; +--- + + + + + + diff --git a/src/components/WelcomeTypewriter.astro b/src/components/WelcomeTypewriter.astro new file mode 100644 index 0000000..7bfc3b2 --- /dev/null +++ b/src/components/WelcomeTypewriter.astro @@ -0,0 +1,186 @@ +--- +--- + +
+ + +
+ + + + \ No newline at end of file diff --git a/src/content.config.ts b/src/content.config.ts new file mode 100644 index 0000000..3bbb024 --- /dev/null +++ b/src/content.config.ts @@ -0,0 +1,46 @@ +import { z, defineCollection, reference } from 'astro:content'; +import { glob, file } from 'astro/loaders'; + +const blogCollection = defineCollection({ + loader: glob({ pattern: "**/*.(md|mdx)", base: "./src/content/blog" }), + schema: z.object({ + title: z.string(), + summary: z.string(), + pubDate: z.date(), + }), +}); + +const portfolioCollection = defineCollection({ + loader: glob({ pattern: "**/*.(md|mdx)", base: "./src/content/portfolio" }), + schema: z.object({ + title: z.string(), + summary: z.string(), + tags: z.array(z.string()).optional(), + pubDate: z.date(), + }), +}); + +const bookChapters = defineCollection({ + loader: file("./src/content/book/chapters.json"), + schema: z.object({ + name: z.string(), + order: z.number(), + subtitle: z.string(), + }) +}); + +const bookCollection = defineCollection({ + loader: glob({ pattern: "**/*.(md|mdx)", base: "./src/content/book" }), + schema: z.object({ + chapter: reference("bookChapters"), + title: z.string(), + summary: z.string().optional(), + part: z.number(), + }), +}); + +export const collections = { + blog: blogCollection, + portfolio: portfolioCollection, + book: bookCollection, +}; diff --git a/src/content/blog/first-post.md b/src/content/blog/first-post.md new file mode 100644 index 0000000..e34c17a --- /dev/null +++ b/src/content/blog/first-post.md @@ -0,0 +1,9 @@ +--- +title: "Dummy Post" +summary: "Der Blog steht, jetzt fehlt noch Inhalt..." +pubDate: 2026-03-20 +--- + +## Hallo Welt + +Demnächst kommen hier hoffentlich ab und so mal Gedanken hin. diff --git a/src/content/book/chapters.json b/src/content/book/chapters.json new file mode 100644 index 0000000..8c28d4e --- /dev/null +++ b/src/content/book/chapters.json @@ -0,0 +1,7 @@ +{ + "grundlagen": { + "name": "Grundlagen", + "order": 0, + "subtitle": "Hier geht's los" + } +} \ No newline at end of file diff --git a/src/content/book/grundlagen/0-0_der_anfang.mdx b/src/content/book/grundlagen/0-0_der_anfang.mdx new file mode 100644 index 0000000..9295c7a --- /dev/null +++ b/src/content/book/grundlagen/0-0_der_anfang.mdx @@ -0,0 +1,84 @@ +--- +title: Einleitung +chapter: grundlagen +part: 0 +subtitle: Hallo Welt! +--- + +Jeder, der schonmal eine Programmiersprache lernen wollte, kennt diese zwei Worte. + +> "Hallo Welt!" + +Die Ausgabe dieser Zeichenfolge oder des englischen Äquivalents wird meist das Minimalbeispiel für eine Programmiesprache gezeigt. + +Aber was braucht es eigentlich dafür, diesen simplen Satz anzuzeigen? +Auf Papier wäre das ganze trivial, es braucht einen Stift und ein paar gekonnte Handbewegungen. +Doch ein Prozessor kennt keine Handbewegungen. Und unsere Digitaltechnik kennt auch keine Buchstaben oder das Konzept davon. +Das einzige, was es gibt, ist "an" (1), "aus" (0) und Logik, die diese Zustände verknüpft. +Gehen wir also durch, was alles überhaupt passieren muss, damit diese zwei Wörter auf dem Bildschirm erscheinen können: + +1. Wir müssen einen Programmcode schreiben, welcher aussagt, diese beiden Worte auszugeben. +2. Der Programmcode muss in ein Format kodiert werden, welches unsere Maschine lesen kann. +3. Der Maschine muss der Befehl gegeben werden, als nächstes unser kodiertes Programm auszuführen. +4. Die Ausgabe des Programmcode wird visuell auf dem Monitor angezeigt. + +Das klingt erstmal ganz einfach. +Aber sobald man etwas tiefer geht, stellen sich viele Fragen: + +- Wie kann ein Programm überhaupt beschreiben, dass Buchstaben ausgegeben werden, wenn nur Einsen und Nullen existieren? +- Wie kodieren wir denn etwas für eine CPU? Und in welchem Format? +- Wie und woher wird unser Programm den aufgerufen? +- Wie entscheidet sich, wann unser Programm ausgeführt wird? Unser Prozessor muss ja auch einen Desktop anzeigen und vieles mehr. +- Wie landen unsere Buchstaben auf dem Display? Und wie redet der Prozessor mit dem Display? +- ... + +Mich selbst fasziniert die Welt der Digitaltechnik und daher habe ich mich entschlossen, einfach mal meinen Abstieg ins Hasenloch schriftlich festzuhalten, für alle die folgen wollen. + +## Über den Autor + +Vielleicht sollte ich mich erst einmal vorstellen. +Hallo, ich bin Daniel. +Ich habe Informationssystemtechnik studiert und habe Programmieren auch schon vorher als Hobby genannt. + +Was habe ich für Qualifikationen, dass du ausgrechnet diese Seiten lesen solltest? +Nun – keine besonderen. +Ich habe keine Zertifikate, keine Lehrberechtigung und habe auch keine öffentlichen großen Projekte vorzuweisen, die man kennt. +Was ich allerdings habe, ist eine langjährige Leidenschaft für die verschiedensten Themen im Bereich Informatik. +Auf meiner [Startseite](/) hab ich auch mal versucht aufzulisten, in welchen Themengebieten ich mich schon verloren habe. +Einzig und allein das ist letzlich meine Motivation, dies hier anzugehen. + +Zum Programmieren lernen gibt es schon viele Ressourcen in jeder Sprache. +Warum noch eine schaffen? +Aus persönlichem Anreiz. +Und das Folgende ist auch eine rein persönliche Sichtweise. +Gerade im Bereich der Informatik ist mir schon häufig aufgefallen, dass Lernressourcen gerne zeigen, wie man etwas _macht_, aber viel zu selten, wie es _funktioniert_. + +Machen wir das mal an einem Beispiel, das greifbarer ist: +Ein Lied auf einem Klavier lernen und spielen kann prinzipiell jeder nach kurzer Zeit. +Um eigene Melodien zu kreieren, die gut klingen, braucht es aber Hintergrundwissen. +Tonalitäten, Taktarten, Akkorde – nur um mal die wenigen Begriffe in den Raum zu werfen, die ich kenne. +In einer Band muss man dann sogar Rücksicht auf andere Leute nehmen. +Und je mehr man sich mit dem Thema Musik und allem drumherum beschäftigt, desto besser wird man. + +Sehr ähnlich verhält sich damit dem Programmieren: +Ein Programm für sich zu schreiben, um bspw. eine Aufgabe zu automatisieren, geht super schnell und ist überhaupt nicht schwer, wenn man etwas Zeit reinsteckt. +Und selbst ohne Zeit ist die Wahrscheinlichkeit heuzutage hoch, dass man mit nettem Fragen eine KI dazu bekommt, den erforderlichen Code nebst Anwendungsdokumentation auszuspucken. +Wenn das eine einmalige Sache bleiben soll, kann man in diesem Moment auch aufhören. +Aber um programmieren zu lernen, muss das gewonnene Wissen auch verstanden und verinnerlicht werden. +Und dazu gehört auch zu verstehen, was der Code eigentlich an welcher Stelle macht. +Häufig ist es sogar wichtig zu wissen, was man explizit nicht machen sollte, besonders für sicherheitskritische Anwendungen. +Nur so kann man am Ende Code schreiben, der lesbar und wartbar ist und natürlich funktioniert. +Das ist besonders wichtig, wenn man nicht die einzige Person ist, die jemals mit diesem Code arbeitet. +Wenn man dann noch die Hintergründe versteht, wie so ein Prozessor oder ein Betriebssystem überhaupt funktionieren, hat man den praktischen Teil der Informatik eigentlich auch schon durchgespielt. +Dafür lohnt sich dann aber vielleicht doch schon das Studium. + +Mein Ziel für diesen Blog ist es, die Leser auf ihrem Wissensstand abzuholen. +Wenn kein Wissen da ist, kein Problem; dann fängt man einfach ganz vorn an. +Dich langweilt Gerede über Kontrollfluss und objektorientierte Programmierung? Vielleicht gibt's ja am Ende ein paar Themen, die dich interessieren. +Vor allem aber möchte ich, dass auch das Niveau des Textes auf einem greifbaren Level bleibt. +Alles perfekt präzise auszudrücken ist vielleicht akademisch besser, aber ich möchte hier praktische Informatik zeigen, keine Paper schreiben. + +## Bereit loszulegen? + +Während ich diesen ersten Eintrag schreibe, ist die ganze Seite noch im Aufbau. +Aber ich bin mir sicher, jemand baut hier eine schöne Navigation hin... diff --git a/src/content/book/grundlagen/0-1_einsen_und_nullen.mdx b/src/content/book/grundlagen/0-1_einsen_und_nullen.mdx new file mode 100644 index 0000000..5ebe878 --- /dev/null +++ b/src/content/book/grundlagen/0-1_einsen_und_nullen.mdx @@ -0,0 +1,224 @@ +--- +title: Einsen und Nullen +chapter: grundlagen +part: 1 +--- +import { Image } from "astro:assets"; +import transistor from "./assets/01-transistor.svg"; +import cmos from "./assets/02-cmos.svg"; + +Jap, wir fangen ganz von vorn an. + +Die meisten Computer-Produkte heutzutage sind Digitaltechnik. +Digital bedeutet in diesem Fall, dass alle Signale in unserem Gerät zwei Zustände haben können: an oder aus, 1 oder 0, _high_ oder _low_. +Letzteres kommt dabei aus der Elektrotechnik und bezieht sich auf die Spannung. +Denn auch das Spannungssignal kann bei unserer Digitaltechnik nur zwei Werte haben. + +Wie können denn solche einfachen Signale so komplexe Routinen bilden? + +> [!info] +> Der eben gelesene Abschnitt ist in der modernen Technik natürlich stark vereinfacht. +> Für die grundlegende Vorstellung ist es aber komplett ausreichend und wahr genug. + +## Zahlen + +Normalerweise benutzen wir für unsere Zahlen die Ziffern 0-9. +Und weil das insgesamt zehn Ziffern ergibt und Wissenschaft sich gerne bei Griechisch und Latein borgt, nennen wir das ganze Dezimalsystem. + +Bei unseren Einsen und Nullen können wir natürlich aber nur die Ziffern 0-1 benutzen. +Das nennt man dann Binärsystem. + +Grundsätzlich bleiben die Rechenregeln gleich: +Wir zählen von 0 bis 9 aufwärts und wenn wir 9 + 1 rechnen wollen, gibt es einen Überlauf. +Wir schreiben dann eine 1 vorn hin und fangen hinten wieder mit der 0 an - eine 10 ergibt sich. +Analog dazu das Binärsystem. +Wir zählen 0, 1 und 1+1 ergibt dann einen Überlauf und wir kommen auf 10. + +> [!warning] +> Wir reden hier erstmal nur von positiven Ganzzahlen. +> Wie negative Zahlen gehandhabt werden, kommt später. +> Gleitkommazahlen sind nochmal ein ganz anderes Kaliber. + +Wie wir jetzt schon sehen, ist 2 im Dezimalsystem (folgend $2_{dez}$) äquivalent zu 10 im Binärsystem (folgend $10_{bin}$). +Jede Zahl des Dezimalsystems können wir als Binärzahl darstellen und andersrum. + +## Logik + +Wenn wir in der Informatik von Logik reden, ist dies deutlich elementarer gemeint, als die im Alltag benutze Logik. +Logik ist hier ein stumpfer Teil der Mathematik. + +Anfangs soll uns die sogenannte _boolsche_ Logik interessieren. +Diese legt Rechenregeln für das Rechnen mit _wahr_ und _falsch_ fest. +Klingt komisch, aber sobald wir wahr mit 1 und falsch mit 0 ersetzen, sieht es plötzlich wieder nach Mathe aus. + +Wie sehen diese Regeln nun aus? +Nutzen wir wieder mal eine Analogie für die Vorstellung. +Wir wollen für unser morgiges Frühstück einkaufen. + +1. "Bring bitte Eier **und** Milch mit." - Unsere Aufgabe ist nur erledigt, wenn wir beides mitbringen. Bringen wir nur eins oder keins mit, müssen wir nochmal los einkaufen. +2. "Bring bitte Gouda **oder** Edamer mit." - Wir können eins von beidem mitbringen oder beides, in jedem Fall ist der Auftrag erfüllt. +3. "Wir brauchen noch **entweder** Vollkornbrot **oder** Toast." - Beides wäre zu viel. Nur genau eins von beiden wollen wir haben. +4. "Bring **nicht** schon wieder Süßigkeiten mit!" - Hier ist unsere Aufgabe erfüllt, wenn wir etwas nicht tun. +5. "Könntest du noch Orangensaft oder Apfelsaft mitbringen? Nur wenn du willst, Hauptsache **nicht beides**." - Der einzige Weg hier den Auftrag nicht zu erfüllen, ist beides mitzubringen. +6. "Ich möchte **weder** Müsli, **noch** Joghurt auf dem Tisch sehen!" - Keines der beiden Sachen darf eingekauft werden, sonst schlägt unser Einkaufsauftrag fehl. + +Klingt dich erstmal nach einer soliden Einkaufsliste. +Und ohne es zu wollen, benutzen wir hier boolsche Mathematik. + +Schauen wir uns das doch mal am ersten Beispiel an: + +| Eier mitgebracht | Milch mitgebracht | Einkauf erfolgreich (Eier **und** Milch) | +|------------------|-------------------|---------------------| +| nein/falsch | nein/falsch | nein/falsch | +| nein/falsch | ja/wahr | nein/falsch | +| ja/wahr | nein/falsch | nein/falsch | +| ja/wahr | ja/wahr | ja/wahr | + +Was wir hier genutzt haben, ist eine Wahrheitstabelle. +Sie gibt alle Kombinationen unserer Eingangswerte und die zugehörigen Ausgangswerte wieder. +In der Tabelle sehen wir ganz gut, wie die Und-Verknüpfung funktioniert. + +Zurück zur Mathematik. +Statt unseres konkreten Beispiels, nutzen wir doch etwas akademischeres: + +- $a$ und $b$ als Eingangswerte +- $\land$ statt immer "und" zu schreiben +- $1$ für ja/wahr +- $0$ für nein/falsch + +| $a$ | $b$ | $a \land b$ | +|-----|-----|-------------| +| $0$ | $0$ | $0$ | +| $0$ | $1$ | $0$ | +| $1$ | $0$ | $0$ | +| $1$ | $1$ | $1$ | + +Wow, sieht direkt viel krasser aus, ist aber noch genau das gleiche wie vorher! +Im Englischen heißt die Operation übrigens auch einfach "and". + +Kommen wir doch zu den anderen Beispielen. + +Als nächstes steht "Gouda **oder** Edamer" an. +Ob wir eins oder beides mitbringen führt immer zu einem erfolgreichen Einkauf, nichts mitzubringen nicht. +Für "oder" (engl. "or") wollen wir das Symbol $\lor$ benutzen. +Somit ergibt sich auch gleich folgende Wahrheitstabelle. + +| $a$ | $b$ | $a \lor b$ | +|-----|-----|------------| +| $0$ | $0$ | $0$ | +| $0$ | $1$ | $1$ | +| $1$ | $0$ | $1$ | +| $1$ | $1$ | $1$ | + +Oh übrigens, lustige Sache – die Spalten $a$ und $b$ sehen immer gleich aus. +Schließlich sind das alle möglichen Kombinationen der Werte und damit müssen wir sie nicht ändern. + +Jetzt der Fall "**entweder** Vollkornbrot **oder** Toast", wo nur genau eine Sache mitgebracht werden darf. +Das ganze nennt man auch "**exklusives oder**" (engl. "exclusive or" → "xor"). +Als Symbol dafür wollen wir $\veebar$ benutzen. + +| $a$ | $b$ | $a \veebar b$ | +|-----|-----|---------------| +| $0$ | $0$ | $0$ | +| $0$ | $1$ | $1$ | +| $1$ | $0$ | $1$ | +| $1$ | $1$ | $0$ | + +Langsam wird klar, wie dieser ganze Logik-Kram funktioniert, oder? +Sieht aus wie Magie, mit genug Erklärung kann das aber jeder. + +Für das nächste Beispiel müssen wir aus dem gewohnten Umfeld ausbrechen. +Warum? +Weil wir nur eine Bedingung haben: "**nicht** Süßigkeiten mitbringen". +Wenn wir etwas nicht machen, sind wir erfolgreich. +Effektiv drehen wir unsere Bedingung des Mitbringens einfach um. +Dies hat viele Namen, normalerweise wird es im deutschen mit "**nicht**" oder "**invertiert**" bezeichnet, im Englischen ist es dann "not" und "inverted". +Es gibt auch mehrere Varianten zur Kennzeichnung: $\lnot$, $\sim$ und auch $!$ bezeichnen alle dasselbe. +Eine weitere Variante ist ein vertikaler Strich über dem Term, um ihn zu negieren (also $\overline{(\text{Term})}$). +Im Folgenden nutzen wir immer die letzte Variante. +Eine Wahrheitstabelle gib es natürlich auch, wenn auch kleiner: + +| $a$ | $\lnot a$ | +|-----|-----------| +| $0$ | $1$ | +| $1$ | $0$ | + +Weiter geht's mit "Orangensaft oder Apfelsaft, **nicht beides**". +Interessant, wir kennen sowohl ein Symbol für "beides" (und) und "nicht". +Und an diesem Punkt lernen wir die ersten Kombinationen. +Diese hier nennen wir "**nicht und**" (engl. "not and" → "nand") und wie schon beschrieben ist es die Kombination der Zeichen $\overline{(\text{Term})}$ und $\land$. + +| $a$ | $b$ | $\overline{a \land b}$ | +|-----|-----|---------------| +| $0$ | $0$ | $1$ | +| $0$ | $1$ | $1$ | +| $1$ | $0$ | $1$ | +| $1$ | $1$ | $0$ | + +Und als allerletztes betrachten wir das "**weder** Müsli, **noch** Joghurt". +Das ist etwas tricky formuliert, aber auch hier ist es eine Kombination aus nur zwei Symbolen. +Zum Spaß machen wir es doch andersrum, wir stellen zuerst die Wahrheitstabelle auf und schauen dann mal, was es sein könnte. +Wir wissen , dass wir nichts von beidem mitbringen sollen, ist auch nur eine Sache mitgebracht, war der Einkauf nicht ohne Fehler. +Tragen wir das doch mal genau so ein: + +| $a$ | $b$ | ? | +|-----|-----|-----| +| $0$ | $0$ | $1$ | +| $0$ | $1$ | $0$ | +| $1$ | $0$ | $0$ | +| $1$ | $1$ | $0$ | + +Probier doch mal, wie wir die Zeichen von Oben kombinieren können, um auf dasselbe Ergebnis zu kommen. + +Bist du auch auf $\overline{a \lor b}$ gekommen? +Oder hast du $\overline{a} \land \overline{b}$ rausbekommen? +Oha, es gibt also mehrere Lösungen! +Genauer gesagt kann man sogar logische Terme ineinander umwandeln, die wichtigste Umformungen hierfür sind die sogenannten [De-morgansche Gesetze](https://de.wikipedia.org/wiki/De-morgansche_Gesetze). + +Aber das geht jetzt zu weit. +Schweifen lieber kurz ab und danach geht es im nächsten Kapitel darum, wie wir mit Logik rechnen können. + +
+Abschweifung: Wie bekommen wir Logik in Strom? + +Wir reden die ganze Zeit über "einfache" Logik, aber man fragt sich nun, wie wir Strom dazu bekommen, dieser Logik zu folgen. + +Das ist gar nicht so schwer: Mithilfe von Transistoren. + +Okay, vielleicht muss ich doch etwas mehr erzählen. +Ein Transistor ist ein aktives elektrisches Bauelement. +Es gibt verschiedene Arten, aber zu Beginn schauen wir uns einen n-Kanal MOSFET an. +Im Normalfall hat dieser drei nutzbare Konnektoren und macht richtig interessante Sachen in Analogschaltungen, wie Verstärkung von Signalen wie Ton. +Aber analoge Sachen interessieren uns hier nicht. +Und dann wird es plötzlich viel einfacher. + +Betrachten wir den folgenden Schaltkreis: + +Ein n-Kanal MOSFET Transistor mit Eingang In an Source, a am Gate und Ausgang Out am Drain + +Gehen wir davon aus, dass wir am Eingang $In$ eine Spannung von 5V anlegen. +Solange an $a$ keine Spannung anliegt, ist der Transistor "aus" und es fließt kein Strom zum Ausgang $Out$. +An $Out$ liegt also 0V an. +Also das behaupte ich jetzt einfach mal, in Wirklichkeit ist der Knoten gerade in "Schwebe", also undefiniert. + +Sobald wir aber am Gate $a$ auch eine Spannung von 5V anlegen, "schaltet" der Transistor durch und es fließt Strom von $In$ zu $Out$. +Wenn wir den Transistor und unsere Spannungen gut wählen, liegt an $Out$ dann auch fast genau 5V an. +Wenn wir nur damit jetzt ein Und-Gatter bauen wollen, brauchen wir nur noch einen zweiten Transistor. + +Um den Ausgang nicht in Schwebe zu lassen, können wir bei der sogenannten CMOS-Technik (Complementary Metal-Oxide-Semiconductor) noch einen p-Kanal MOSFET hinzufügen. +Dieser funktioniert genau andersrum: Wenn am Gate eine Spannung anliegt, ist der Transistor aus, wenn keine Spannung anliegt, ist er an. +Dadurch ist dann unser Ausgang immer definiert. +Hier nochmal als Schaubild: + +Eine CMOS-Schaltung mit a und b als Eingänge, die ein Und-Gatter bilden. + +Statt je nachdem, ob bei $a$ und $b$ Spannung anliegt, fließt der Strom von "oben" oder "unten" durch die Transistoren und unser $Out$ nimmt die Spannung von $High$ oder $Low$ an. +$High$ und $Low$ sind dabei die Spannungen, die für 1 und 0 stehen. +Was genau die zugehörigen Spannung ist, hängt vom jeweiligen System ab. +Die Spannungen sind so zu wählen, dass sie von den Transistoren sicher erkannt werden können. + +Aber gut, jetzt haben wir schon ein Und-Gatter. +Man kann sich nun selbst überlegen, wie die anderen Gatter mit Transistoren aufgebaut werden können. +Dazu noch der Hinweis: Wir können auch Signale in CMOS sehr einfach invertieren. +Einfach den p-Kanal Transistor an High und den n-Kanal Transistor an Low anschließen. +
diff --git a/src/content/book/grundlagen/0-2_einmal_zahlen_bitte.mdx b/src/content/book/grundlagen/0-2_einmal_zahlen_bitte.mdx new file mode 100644 index 0000000..392db4d --- /dev/null +++ b/src/content/book/grundlagen/0-2_einmal_zahlen_bitte.mdx @@ -0,0 +1,151 @@ +--- +title: Einmal Zahlen bitte! +chapter: grundlagen +part: 2 +--- + +Bevor wir jetzt anfangen können zu rechnen, brauchen wir natürlich Zahlen. +Und zwar mehr als nur 0 und 1. +Also schauen wir uns mal an, wie wir Zahlen im Binärsystem darstellen können. + +## Positive Zahlen +Im Dezimalsystem, also dem Zahlensystem, das wir im Alltag benutzen, haben wir zehn Ziffern: 0 bis 9. +Im Binärsystem gibt's aber nur zwei Ziffern: 0 und 1. +Interessanterweise kann man alle Prinzipien unseres Dezimalsystems auch im Binärsystem anwenden. +Was bedeuted das? + +Zählen wir erstmal hoch. 0, 1, 2, ..., 9, und jetzt? +Wir haben ja nur die Ziffern 0 bis 9. +Also zählen wir die Stelle vor der 9 einmal hoch und fangen neu an. 09 wird also zu 10. +Ok, ich schwöre wir sind nicht im Kindergarten und das ganze hat einen tieferen Sinn! + +Machen wir doch das gleiche im Binärsystem +Wir zählen also: 0, 1, und jetzt? +Jetzt machen wir das gleiche: Wir zählen die Stelle vor der 1 hoch und fangen neu an. 01 wird also zu 10. + +Was in unserem Dezimalsystem eine 2 ist, ist binär ausgedrückt also eine 10. +Und so können wir natürlich weiter zählen. + +| Dezimal | Binär | +|---------|-------| +| 0 | 0 | +| 1 | 1 | +| 2 | 10 | +| 3 | 11 | +| 4 | 100 | +| 5 | 101 | +| 6 | 110 | +| 7 | 111 | +| 8 |1000 | +| ... | ... | + +Das Prinzip sollte klar sein. + +Aber zum Umrechnen wollen wir ja nicht immer hochzählen müssen bis wir zufällig die richtige zahl finden. +Natürlich gibt es aber Umrechnungsformeln, die wir benutzen können. + +### Umrechnung Binär zu Dezimal +Um eine Binärzahl in eine Dezimalzahl umzuwandeln, können wir jede Stelle der Binärzahl mit der entsprechenden Potenz von 2 multiplizieren und die Ergebnisse addieren. +Ihh, das klingt nach Mathe. +Aber so schwierig ist es nicht. +Nehmen wir zum Beispiel die Binärzahl $1101$. + +| Stelle | Wert der Stelle | Potenz von 2 | Berechnung | +|--------|-----------------|---------------|------------------| +| 3 | 1 | $2^3 = 8$ | $1 * 8 = 8$ | +| 2 | 1 | $2^2 = 4$ | $1 * 4 = 4$ | +| 1 | 0 | $2^1 = 2$ | $0 * 2 = 0$ | +| 0 | 1 | $2^0 = 1$ | $1 * 1 = 1$ | +| | | | $8 + 4 + 0 + 1 = 13$ | + +Was hier schon auffällt, dass wir die Stellen von rechts nach links durchnummerieren, beginnend bei 0. +Das können wir übrigens auch im Dezimalsystem machen. + +$42 = 4 * 10^1 + 2 * 10^0 = 4 * 10 + 2 * 1 = 40 + 2 = 42$ + +Die händische Umrechnung ist in der Theorie ganz einfach, praktisch ist es natürlich bei großen Zahlen etwas umständlich. +Daher lasst ihr am besten einen Computer die Umrechnung machen. +Der kennt das schon. + +### Umrechnung Dezimal zu Binär +Um eine Dezimalzahl in eine Binärzahl umzuwandeln, können wir die Zahl durch 2 teilen und den Rest notieren. +Wir wiederholen diesen Vorgang mit dem Ergebnis der Division, bis das Ergebnis 0 ist. +Die Binärzahl ergibt sich dann aus den notierten Resten, gelesen von unten nach oben. +Das klingt doch schon wieder so kompliziert... + +Ist es aber auch nicht, ebenfalls nur aufwendig. + +Nehmen wir zum Beispiel die Dezimalzahl 13 von oben und rechnen wieder zurück: + +| Division | Ergebnis | Rest | +|----------|----------|------| +| $13 / 2$ | 6 | 1 | +| $6 / 2$ | 3 | 0 | +| $3 / 2$ | 1 | 1 | +| $1 / 2$ | 0 | 1 | + +Die Reste von unten nach oben gelesen ergeben die Binärzahl $1101$. + +Auch hier gilt wieder: Probiert es gern mal aus, aber bei großen Zahlen lasst lieber den Computer die Arbeit machen. + +### Wo hoch kann ich gehen? + +In PCs arbeiten wir im Normalfall mit einer festen Anzahl an Bits für eine Ganzzahl. +Je nach Definition im Code kann das unterschiedlich lang sein, aber im Normalfall hat man die Auswahl aus 8, 16, 32, 64 und vielleicht sogar 128 Bit. +Die höchste Zahl in der jeweiligen "Feldbreite" ist dann natürlich die, wo alle Bits auf 1 gesetzt sind. +Also für z.B. 8 Bit: $11111111_2 = 255_{10}$ +Zusammen mit der 0 sind das also $256$ verschiedene Zahlen, die man darstellen kann. + +Man kann diese beiden Werte auch ganz einfach berechnen: +- Die Anzahl der darstellbaren Zahlen (inkl. 0) ist $2^n$, wobei $n$ die Anzahl der Bits ist. +- Die höchste Zahl ist $2^n - 1$, wobei $n$ die Anzahl der Bits ist. + +> [!info] Anzeige des Zahlenraums +> Damit wir nicht durcheinanderkommen, wenn ich mal irgendwo eine Hundert schreibe (Ist es eine $100$? Oder doch $4$?) benutzen wir kleine Hinweise an den Zahlen. +> $10_{10}$ ist eine Zehn im "normalen" Dezimalsystem und wäre binär dargestellt eine $1010_2$. +> $10_2$ muss man wiederum binär lesen und kommt so auf eine Zwei. +> Diese Schreibweise wird ab jetzt häufiger vorkommen, um Missverständnisse zu vermeiden. + +## Negative Zahlen +Bisher haben wir nur positive Zahlen betrachtet. +Wie sieht es aber mit negativen Zahlen aus? +Im Alltag im Dezimalsystem schreiben wir einfach ein Minuszeichen vor die Zahl. +Und das können wir natürlich auch vor unsere Binärzahlen schreiben - aber der PC kann nur 0 und 1, der kann kein Minus. + +Um trotzdem negative Zahlen darstellen zu können, benutzen Computer das sogenannte Zweierkomplement. +Das haben sich ein paar schlaue Köpfe ausgedacht und es ist cool, weil unsere normalen Rechenregeln für Addition und Substraktion weiterhin funktionieren. + +Eine Zahl im Zweierkomplement darzustellen funktioniert so: +1. Man nimmt die Binärdarstellung der positiven Zahl +2. Man invertiert alle Bits (0 wird zu 1 und 1 wird zu 0) +3. Man addiert 1 zur invertierten Zahl + +Schritt 2 ist tricky, weil man etwas bedenken muss: Vor der Zahl gibt es eine feste Anzahl an Bits, die alle 0 sind. +Im Zweierkomplement muss unser Zahlenraum (in Bit angegeben) als begrenzt sein und berücksichtig werden. +Nehmen wir also an, wir arbeiten in einem 8-Bit-System. + +Nehmen wir zum Beispiel die Zahl $-13$: +1. Die Binärdarstellung von $13_{10}$ ist $00001101_2$ +2. Invertieren wir alle Bits: $11110010_2$ +3. Addieren wir $1$: $11110010_2 + 1 = 11110011_2$ + +Also ist die Darstellung von $-13$ im Zweierkomplement $11110011_2$ (in einem 8-Bit-System). + +Die Rechnung zurück ist übrigens exakt gleich. +Nehmen wir also die Zahl $11110011_2$ von eben: +1. Invertieren wir alle Bits: $00001100_2$ +2. Addieren wir $1$: $00001100_2 + 1 = 00001101_2$ +3. Die Binärdarstellung $00001101_2$ ist $13_{10}$ im Dezimalsystem, also ist die ursprüngliche Zahl $-13$. + +Woher wissen wir nun, ob eine Zahl im Zweierkomplement positiv oder negativ ist? +Wir schauen uns einfach das höchstwertige Bit (das ganz links) an. +Wenn es eine $0$ ist, ist die Zahl positiv, wenn es eine $1$ ist, ist die Zahl negativ. +Damit haben wir quasi ein Minus vor die Zahl geschrieben. +Man spricht im englischen auch von "sign bit" und nennt Ganzzahlen mit Vorzeichen "signed integer". +Das Gegenteil ist dann "unsigned integer". + +Jetzt haben wir aus einem 8-Bit-System nur noch 7 Bits für die Zahl selbst übrig, da die erste Stelle, ja das Vorzeichen angibt. +Durch das Nutzen von Vorzeichen, schränken wir die möglichen Zahlen ein. + +Während wir mit 8 Bit noch 255 positive Zahlen und die 0 darstellen konnten, sind es nun nur noch 127 positive Zahlen (0 bis 127) und die 0, aber auch 128 negative Zahlen (-1 bis -128). +Wenn wir später rechnen, müssen wir hier eher aufpassen: $127 + 1$ ist im Zweierkomplement $10000000_2$, und damit $-128$! diff --git a/src/content/book/grundlagen/0-3_addieren_mit_bits.mdx b/src/content/book/grundlagen/0-3_addieren_mit_bits.mdx new file mode 100644 index 0000000..438b524 --- /dev/null +++ b/src/content/book/grundlagen/0-3_addieren_mit_bits.mdx @@ -0,0 +1,236 @@ +--- +title: Addieren mit Bits +chapter: grundlagen +part: 3 +--- + +Nachdem wir zuletzt geklärt haben, wie wir Einsen und Nullen mit Logikgattern darstellen können, wollen wir uns jetzt anschauen, wie wir mit diesen Bits rechnen können. +Und das ist einfacher, als man vielleicht denkt. + + +## Addieren mit 1 Bits +Schauen wir uns zuerst an, wie eine simple Addition mit einer Stelle funktioniert. + +| $a$ | $b$ | $a + b$ | +|-----|-----|---------| +| $0$ | $0$ | $0$ | +| $0$ | $1$ | $1$ | +| $1$ | $0$ | $1$ | +| $1$ | $1$ | $10$ | + +Nun, bis jetzt haben wir nur mit einzelnen Bits gerechnet, aber das Ergebnis von 1 + 1 ist 10, also zwei Bits. +Darum machen wir uns gleich Gedanken. +Aber wenn man nur die hinteren Stellen betrachtet, fällt etwas auf: + +**Die Addition von $a$ und $b$ entspricht genau der Funktion eines XOR-Gatters.** + +Und wenn wir uns den Übertrag ansehen, der im Fall von 1 + 1 entsteht, sehen wir, dass dieser genau dann 1 ist, wenn sowohl $a$ als auch $b$ 1 sind. + +**Der Übertrag entspricht genau der Funktion eines UND-Gatters.** + +Am Ende können wir also sagen, dass eine Addition von zwei Bits $a$ und $b$ folgendes ergibt: +- Das Ergebnis-Bit ist $a$ XOR $b$. +- Der Übertrag ist $a$ UND $b$. + +Wenn wir das nun als Schaubild darstellen, sieht das so aus: + +```mermaid +%%{ init: { 'flowchart': { 'curve': 'linear' }, 'theme': 'dark' } }%% +flowchart TD + A@{ shape: circle, label: "a" } --> XOR1[XOR] + B@{ shape: circle, label: "b" } --> XOR1 + XOR1 --> S@{ shape: dbl-circ, label: "Summe\ns" } + + A --> AND1[AND] + B --> AND1 + AND1 --> C@{ shape: circle, label: "Übertrag\nc" } +``` + +Damit können wir schon die Binärzahlen mit einer Stelle addieren! + +Nun haben wir aber vielleicht auch gigantische Zahlen, wie 4 oder 7! +Dafür brauchen wir mehr als ein Bit. +Aber das Prinzip bleibt das gleiche. +Nur, dass wir nun auch noch den Übertrag beachten müssen. + +Aber halb so wild, denn was macht man mit einem Übertrag im normalen schriftlichen Addieren? +Man addiert ihn einfach zur nächsten Stelle dazu! +Machen wir das also auch hier. +Statt nur $a$ und $b$ zu addieren, addieren wir nun $a$, $b$ und den Übertrag $c_{in}$ der vorherigen Stelle: + +| $a$ | $b$ | $c_{in}$ | $s$ | $c_{out}$ | +|-----|-----|----------|-------|-----------| +| $0$ | $0$ | $0$ | $0$ | $0$ | +| $0$ | $0$ | $1$ | $1$ | $0$ | +| $0$ | $1$ | $1$ | $0$ | $1$ | +| $0$ | $1$ | $1$ | $0$ | $1$ | +| $1$ | $0$ | $0$ | $1$ | $0$ | +| $1$ | $0$ | $1$ | $0$ | $1$ | +| $1$ | $1$ | $0$ | $0$ | $1$ | +| $1$ | $1$ | $1$ | $1$ | $1$ | + +Unser Schaltplan, sieht dann wieder genauso aus, nur dass wir jetzt noch einen weiteren Eingang für den Übertrag haben: + +```mermaid +%%{ init: { 'flowchart': { 'curve': 'linear' }, 'theme': 'dark' } }%% +flowchart TD + A@{ shape: circle, label: "a" } --> XOR1 + B@{ shape: circle, label: "b" } --> XOR1 + + XOR1[XOR] --> XOR2 + Cin@{ shape: circle, label: "c_in" } --> XOR2 + XOR2[XOR] --> S@{ shape: dbl-circ, label: "s" } + + A --> AND1 + B --> AND1 + AND1[AND] --> OR1 + + XOR1 --> AND2 + Cin --> AND2 + AND2[AND] --> OR1 + + OR1[OR] --> Cout@{ shape: dbl-circ, label: "c" } +``` + +Das Ganze nennt man dann einen Volladdierer. +Das vorher (ohne Übertragseingang) ist ein Halbaddierer. +Wir sehen hier auch, dass unser Volladdierer (FA) aus zwei Halbaddierern (HA) und einem ODER-Gatter besteht: + +```mermaid +%%{ init: { 'flowchart': { 'curve': 'linear' }, 'theme': 'dark' } }%% +flowchart TD + A0@{ shape: circle, label: "a" } --> HA1 + B0@{ shape: circle, label: "b" } --> HA1 + HA1[HA] -- s --> HA2 + Cin@{ shape: circle, label: "c_in" } --> HA2 + HA2[HA] --> s_0@{ shape: dbl-circ, label: "s" } + HA1 -- c --> OR + HA2 -- c --> OR + OR[OR] --> Cout@{ shape: dbl-circ, label: "c" } +``` + +> [!Info] +> Das ist ein Vorgeschmack darauf, wie es weitergeht - wir erdenken uns Baublöcke und kombinieren sie, um komplexere Funktionen zu bauen. +> Hier wird eben aus einfachen Logikgattern erst ein Halbaddierer und daraus dann ein Volladdierer. + +## Größere Zahlen addieren + +Normalerweise kommt ein Rechner auch in die Verlegenheit, mehr als ein Bit addieren zu müssen. +Auch hier können wir uns aber anschauen, wie wir das mit Stift und Papier machen. +Bei mehrstelligen dezimalen Zahlen rechnen wir von hinten nach vorn die einzelnen Stellen zusammen, wobei wir den Übertrag der vorherigen Stelle mit einbeziehen. +Und genau so machen wir das auch im Binären. + +Wenn wir nun also zwei 4-Bit-Zahlen addieren wollen, brauchen wir 4 Volladdierer, die hintereinander geschaltet und deren Überträge miteinander verbunden sind: + +
+```mermaid +%%{ init: { 'flowchart': { 'curve': 'linear' }, 'theme': 'dark' } }%% +flowchart TD + subgraph FA3 + A3@{ shape: circle, label: "a_3" } --> FA31 + B3@{ shape: circle, label: "b_3" } --> FA31 + FA31[FA] --> s_3@{ shape: dbl-circ, label: "s_3" } + FA31 -- c --> Cout0@{ shape: dbl-circ, label: "c_out" } + end + subgraph FA2 + A2@{ shape: circle, label: "a_2" } --> FA21 + B2@{ shape: circle, label: "b_2" } --> FA21 + FA21[FA] --> s_2@{ shape: dbl-circ, label: "s_2" } + FA21 -- c --> FA31 + end + subgraph FA1 + A1@{ shape: circle, label: "a_1" } --> FA11 + B1@{ shape: circle, label: "b_1" } --> FA11 + FA11[FA] --> s_1@{ shape: dbl-circ, label: "s_1" } + FA11 -- c --> FA21 + end + subgraph FA0 + Cin0@{ shape: circle, label: "c_in" } --> FA01 + A0@{ shape: circle, label: "a_0" } --> FA01 + B0@{ shape: circle, label: "b_0" } --> FA01 + FA01[FA] --> s_0@{ shape: dbl-circ, label: "s_0" } + FA01 -- c_0 --> FA11 + end +``` +
+ +> [!info] Nomenklatur +> Wie gerade schon gesehen, werden die Bits von rechts nach links durchnummeriert, beginnend mit 0. +> Das von hinten nach vorn zu machen hat gleichzeitig den Vorteil, dass die Nummerierung der Bits mit der Potenz von 2 übereinstimmt, die sie repräsentieren. +> Zu merken ist aber einfach nur: $a_0$ ist das niederwertigste Bit ganz rechts. + +Heraus kommt eine Form, die ein wenig aussieht wie ein Wasserfall, würde man es direkt nebeneinander rendern, käme würde der Übertrag eine Art "Welle" darstellen. +Daher nennt man diese Form auch Ripple-Carry-Adder, da der Übertrag von einem Volladdierer zum nächsten "rippelt". + +Aber was macht denn das c_in dort eigentlich noch? +Normalerweise hat man ja keinen Übertrag in der ersten Stelle, oder? +Prinzipiell richtig. +Beim Addieren ist dieser Eingang normalerweise auch 0, aber er hilft später noch richtig, wenn wir nicht nur addieren, sondern auch subtrahieren wollen. + +## Die Andere Richtung + +Subtrahieren müssen wir natürlich auch können. +Wie negative Zahlen dargestellt werden, haben wir im [vorherigen Kapitel](0-2_einmal_zahlen_bitte.mdx) schon geklärt, aber wie subtrahieren wir eigentlich? +Nun hilft uns das kompliziert aussehende Zweierkomplement, denn es stellt sich heraus, dass die Subtraktion von $b$ von $a$ genau der Addition von $a$ und dem Zweierkomplement von $b$ entspricht. +Das Zweierkomplement zu bilden ist nicht schwer und wird noch einfacher wenn, wir nun unseren Addierer mit $c_{in}$ haben. + +Grundsätzlich sieht die Mathe so aus: +$$a - b = a + (-b) = a + (\overline{b} + 1)$$ +Was jetzt auffällt ist das $+1$ am Ende. +Das ist genau das, was wir mit $c_{in}$ machen können, denn wenn wir $c_{in}$ auf 1 setzen, addieren wir genau die $1$, die wir brauchen. +An dem Punkt müssen wir gar nichts weiter tun, als unser $b$ nur zu invertieren und $c_{in}$ auf 1 zu setzen, um die Subtraktion durchzuführen. + +Und jetzt wird es wild: Das wird noch einfacher! +Wir können nämlich einfach vor jeden Eingang von $b$ ein XOR-Gatter setzen, das mit $c_{in}$ verbunden ist. +Wenn $c_{in}$ 0 ist, passiert nichts, wir addieren also normal. +Wenn $c_{in}$ aber 1 ist, wird jedes Bit von $b$ invertiert, was genau das ist, was wir brauchen, um die Subtraktion durchzuführen. + +Wer mag, kann sich das gern mal aufmalen, aber das Ergebnis ist ein Addierer, der sowohl addieren als auch subtrahieren kann, je nachdem, ob $c_{in}$ 0 oder 1 ist. + +## Achtung, Vorzeichen! + +Der letzt Satz des vorherigen Kapitels hat es schon angedeutet, aber hier müssen wir aufpassen, denn das Problem ist für eine ganze Menge Sicherheitslücken und Fehler verantwortlich: +Angenommen, ich rechne mit 8-Bit-Zahlen mit Vorzeichen und möchte $127 + 1$ rechnen? +Die Binärdarstellung von $127$ ist $01111111_2$. +Wenn ich nun 1 addiere, erhalte ich $10000000_2$, was im Zweierkomplement $-128$ entspricht. +Das ist natürlich nicht das, was wir erwartet haben, aber es ist das, was unser Addierer ausspuckt. +Das Ganze nennt man einen "Integer Overflow" und zusammen mit dem Underflow (wenn zwei negative Zahlen eine positive ergeben) ist er ein großes Problem, da es zu unvorhergesehenen Ergebnissen führen kann. + +Daher sollten wir immer Folgendes prüfen: +- Wenn wir zwei positive Zahlen addieren, sollte das Ergebnis positiv sein, sonst haben wir einen Overflow. +- Wenn wir zwei negative Zahlen addieren, sollte das Ergebnis negativ sein, sonst haben wir einen Underflow. + +Und die Behauptung ist: Wenn das Vorzeichen zweier Zahlen gleich ist, aber ungleich des Vorzeichens des Ergebnisses, dann ist ein Überlauf aufgetreten. + +Nehmen wir wieder an, wir sind im Bereich 8bit signed und stellen einmal die Tabelle auf, um das zu überprüfen: + +| $a_8$ | $b_8$ | $s_8$ | Overflow? ($o$) | +|-------|-------|-------|-----------| +| $0$ | $0$ | $0$ | Nein | +| $0$ | $0$ | $1$ | Ja | +| $0$ | $1$ | $0$ | Nein | +| $0$ | $1$ | $1$ | Nein | +| $1$ | $0$ | $0$ | Nein | +| $1$ | $0$ | $1$ | Nein | +| $1$ | $1$ | $0$ | Ja | +| $1$ | $1$ | $1$ | Nein | + +Das ist sogar gleich mal eine Übung in Logik, wie wir aus der Tabelle jetzt eine Formel für die Überlaufbedingung ableiten können. +Die Formel lautet: +$$o = (a_8 \land b_8 \land \lnot s_8) \lor (\lnot a_8 \land \lnot b_8 \land s_8)$$ + +Nun können wir sicher sein, dass wir immer die richtigen Ergebnisse bekommen, wenn wir die Überlaufbedingung überprüfen und entsprechend handeln, wenn sie erfüllt ist. + +Im nächsten Kapitel schauen wir uns an, wie wir aus diesem einfachen Rechner eine vollwertige Logikeinheit bauen können. + +
+Zusatzaufgabe: Multiplikation + +Etwas komplizierter, aber gar nicht so schwer, ist die Multiplikation. +Wer möchte kann sich an dieser Stelle gerne selbst mal überlegen, wie so etwas zumindest in der Theorie aufgebaut sein könnte. +Die simpelste Variante könnte so aussehen wie eine Treppe, wenn man es sich aufmalt. + +Ein Dividierer wiederum ist hochkompliziert... +
+ + diff --git a/src/content/book/grundlagen/0-4_voll_logisch.mdx b/src/content/book/grundlagen/0-4_voll_logisch.mdx new file mode 100644 index 0000000..d9e47a7 --- /dev/null +++ b/src/content/book/grundlagen/0-4_voll_logisch.mdx @@ -0,0 +1,7 @@ +--- +title: Voll Logisch! +chapter: grundlagen +part: 4 +--- + +Hier wird noch gebaut. \ No newline at end of file diff --git a/src/content/book/grundlagen/assets/01-transistor.json b/src/content/book/grundlagen/assets/01-transistor.json new file mode 100644 index 0000000..5b6cde0 --- /dev/null +++ b/src/content/book/grundlagen/assets/01-transistor.json @@ -0,0 +1,117 @@ +{ + "version": "0.1", + "components": [ + { + "type": "node", + "position": { + "x": 377.953, + "y": -377.953 + }, + "id": "nfet" + }, + { + "type": "rect", + "position": { + "x": 333.071, + "y": -377.953 + }, + "stroke": { + "opacity": 0 + }, + "size": { + "x": 23.622, + "y": 18.898 + }, + "text": { + "text": "a", + "showPlaceholderText": true + } + }, + { + "type": "wire", + "points": [ + { + "x": 377.953, + "y": -377.953 + }, + { + "x": 377.953, + "y": -407.055 + } + ], + "stroke": { + "width": "0.4pt" + }, + "directions": [ + "-|" + ] + }, + { + "type": "rect", + "position": { + "x": 377.953, + "y": -415.748 + }, + "stroke": { + "opacity": 0 + }, + "size": { + "x": 28.346, + "y": 28.346 + }, + "text": { + "text": "In" + } + }, + { + "type": "rect", + "position": { + "x": 377.953, + "y": -340.157 + }, + "stroke": { + "opacity": 0 + }, + "size": { + "x": 37.795, + "y": 28.346 + }, + "text": { + "text": "Out", + "showPlaceholderText": true + } + }, + { + "type": "node", + "position": { + "x": 377.953, + "y": -407.055 + }, + "id": "circ" + }, + { + "type": "node", + "position": { + "x": 377.953, + "y": -348.851 + }, + "id": "circ" + }, + { + "type": "node", + "position": { + "x": 340.914, + "y": -377.953 + }, + "id": "circ" + }, + { + "type": "node", + "position": { + "x": 377.931, + "y": -388.213 + }, + "id": "circ" + } + ] +} \ No newline at end of file diff --git a/src/content/book/grundlagen/assets/01-transistor.svg b/src/content/book/grundlagen/assets/01-transistor.svg new file mode 100644 index 0000000..9f2886f --- /dev/null +++ b/src/content/book/grundlagen/assets/01-transistor.svg @@ -0,0 +1,201 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/content/book/grundlagen/assets/02-cmos.json b/src/content/book/grundlagen/assets/02-cmos.json new file mode 100644 index 0000000..c6b116d --- /dev/null +++ b/src/content/book/grundlagen/assets/02-cmos.json @@ -0,0 +1,411 @@ +{ + "version": "0.1", + "components": [ + { + "type": "node", + "position": { + "x": 377.953, + "y": -377.953 + }, + "id": "nfet" + }, + { + "type": "rect", + "position": { + "x": 321.26, + "y": -349.606 + }, + "stroke": { + "opacity": 0 + }, + "size": { + "x": 23.622, + "y": 18.898 + }, + "text": { + "text": "a", + "showPlaceholderText": true + } + }, + { + "type": "wire", + "points": [ + { + "x": 377.953, + "y": -377.953 + }, + { + "x": 377.953, + "y": -407.055 + } + ], + "stroke": { + "width": "0.4pt" + }, + "directions": [ + "-|" + ] + }, + { + "type": "rect", + "position": { + "x": 368.504, + "y": -477.165 + }, + "stroke": { + "opacity": 0 + }, + "size": { + "x": 28.346, + "y": 28.346 + }, + "text": { + "text": "High" + } + }, + { + "type": "rect", + "position": { + "x": 401.575, + "y": -349.606 + }, + "stroke": { + "opacity": 0 + }, + "size": { + "x": 37.795, + "y": 28.346 + }, + "text": { + "text": "Out", + "showPlaceholderText": true + } + }, + { + "type": "node", + "position": { + "x": 377.953, + "y": -319.748 + }, + "id": "pfet" + }, + { + "type": "wire", + "points": [ + { + "x": 340.914, + "y": -377.953 + }, + { + "x": 333.071, + "y": -349.606 + } + ], + "stroke": { + "width": "0.4pt" + }, + "directions": [ + "|-" + ] + }, + { + "type": "wire", + "points": [ + { + "x": 340.914, + "y": -319.748 + }, + { + "x": 333.071, + "y": -349.606 + } + ], + "stroke": { + "width": "0.4pt" + }, + "directions": [ + "|-" + ] + }, + { + "type": "node", + "position": { + "x": 333.071, + "y": -349.606 + }, + "id": "circ" + }, + { + "type": "node", + "position": { + "x": 340.914, + "y": -349.606 + }, + "id": "circ" + }, + { + "type": "node", + "position": { + "x": 377.953, + "y": -387.402 + }, + "id": "circ" + }, + { + "type": "node", + "position": { + "x": 377.953, + "y": -309.921 + }, + "id": "circ" + }, + { + "type": "wire", + "points": [ + { + "x": 377.953, + "y": -319.748 + }, + { + "x": 377.953, + "y": -309.921 + } + ], + "stroke": { + "width": "0.4pt" + }, + "directions": [ + "|-" + ] + }, + { + "type": "rect", + "position": { + "x": 372.283, + "y": -223.937 + }, + "stroke": { + "opacity": 0 + }, + "size": { + "x": 26.457, + "y": 22.677 + }, + "text": { + "text": "Low", + "showPlaceholderText": true + } + }, + { + "type": "node", + "position": { + "x": 377.953, + "y": -349.606 + }, + "id": "circ" + }, + { + "type": "node", + "position": { + "x": 387.402, + "y": -349.606 + }, + "id": "circ" + }, + { + "type": "wire", + "points": [ + { + "x": 377.953, + "y": -349.606 + }, + { + "x": 387.402, + "y": -349.606 + } + ], + "stroke": { + "width": "0.4pt" + }, + "directions": [ + "-|" + ] + }, + { + "type": "node", + "position": { + "x": 377.953, + "y": -438.614 + }, + "id": "nfet" + }, + { + "type": "node", + "position": { + "x": 377.953, + "y": -448.819 + }, + "id": "circ" + }, + { + "type": "node", + "position": { + "x": 377.953, + "y": -467.717 + }, + "id": "circ" + }, + { + "type": "wire", + "points": [ + { + "x": 377.953, + "y": -438.614 + }, + { + "x": 377.953, + "y": -448.819 + } + ], + "stroke": { + "width": "0.4pt" + }, + "directions": [ + "-|" + ] + }, + { + "type": "node", + "position": { + "x": 377.953, + "y": -260.598 + }, + "id": "pfet" + }, + { + "type": "wire", + "points": [ + { + "x": 377.953, + "y": -289.701 + }, + { + "x": 377.953, + "y": -290.646 + } + ], + "stroke": { + "width": "0.4pt" + }, + "directions": [ + "-|" + ] + }, + { + "type": "wire", + "points": [ + { + "x": 377.953, + "y": -407.055 + }, + { + "x": 377.953, + "y": -409.512 + } + ], + "stroke": { + "width": "0.4pt" + }, + "directions": [ + "|-" + ] + }, + { + "type": "node", + "position": { + "x": 377.953, + "y": -250.394 + }, + "id": "circ" + }, + { + "type": "node", + "position": { + "x": 377.953, + "y": -231.496 + }, + "id": "circ" + }, + { + "type": "node", + "position": { + "x": 340.914, + "y": -260.598 + }, + "id": "circ" + }, + { + "type": "node", + "position": { + "x": 340.914, + "y": -438.614 + }, + "id": "circ" + }, + { + "type": "wire", + "points": [ + { + "x": 377.953, + "y": -260.598 + }, + { + "x": 377.953, + "y": -250.394 + } + ], + "stroke": { + "width": "0.4pt" + }, + "directions": [ + "-|" + ] + }, + { + "type": "rect", + "position": { + "x": 328.346, + "y": -444.094 + }, + "stroke": { + "opacity": 0 + }, + "size": { + "x": 14.173, + "y": 9.449 + }, + "text": { + "text": "b", + "showPlaceholderText": true + } + }, + { + "type": "rect", + "position": { + "x": 328.347, + "y": -264.567 + }, + "stroke": { + "opacity": 0 + }, + "size": { + "x": 14.173, + "y": 9.449 + }, + "text": { + "text": "b", + "showPlaceholderText": true + } + } + ] +} \ No newline at end of file diff --git a/src/content/book/grundlagen/assets/02-cmos.svg b/src/content/book/grundlagen/assets/02-cmos.svg new file mode 100644 index 0000000..4f5187c --- /dev/null +++ b/src/content/book/grundlagen/assets/02-cmos.svg @@ -0,0 +1,511 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/content/portfolio/assets/screenshot.jpg b/src/content/portfolio/assets/screenshot.jpg new file mode 100644 index 0000000..b94c635 Binary files /dev/null and b/src/content/portfolio/assets/screenshot.jpg differ diff --git a/src/content/portfolio/assets/screenshot.jpg:Zone.Identifier b/src/content/portfolio/assets/screenshot.jpg:Zone.Identifier new file mode 100644 index 0000000..d6c1ec6 Binary files /dev/null and b/src/content/portfolio/assets/screenshot.jpg:Zone.Identifier differ diff --git a/src/content/portfolio/astro-rewrite.mdx b/src/content/portfolio/astro-rewrite.mdx new file mode 100644 index 0000000..1043b5b --- /dev/null +++ b/src/content/portfolio/astro-rewrite.mdx @@ -0,0 +1,11 @@ +--- +title: Astro Rewrite +summary: "Next.js entwickelt sich rasend - zu schnell für mich, um Features und Sicherheitsupdates immer zu verfolgen. Und React für einen Blog? Unnötig. Eine einfache statische Lösung aber mit Komponenten? Astro ist genau das. Also: Willkommen zum neuen Anstrich in Astro!" +repository: "https://git.c0ntroller.de/c0ntroller/frontpage" +pubDate: 2023-04-03 +tags: + - Astro + - SSR + - Webentwicklung +--- + diff --git a/src/content/portfolio/infoscreen.mdx b/src/content/portfolio/infoscreen.mdx new file mode 100644 index 0000000..7a530d2 --- /dev/null +++ b/src/content/portfolio/infoscreen.mdx @@ -0,0 +1,133 @@ +--- +title: Infoscreen +summary: "Aus einem alten Monitor und einem Raspberry Pi wurde ein Küchen-Infoscreen, der Wetter, Kalender, Nachrichten und mehr anzeigt. Über mehrere Iterationen hinweg entwickelte sich das Projekt von einfachem JavaScript zu Electron und schließlich zurück zu einer Gatsby-basierten Anwendung." +repository: "https://git.c0ntroller.de/c0ntroller/infoscreen" +pubDate: 2022-06-13 +tags: + - Heimprojekt + - JavaScript +--- + +import { Image } from "astro:assets"; +import screenshot from "./assets/screenshot.jpg"; + +Hier gehts um einen Bildschirm der Informationen anzeigt. +Überraschend, ich weiß. + +## Wie sieht es aus? + +Screenshot des Infoscreens + +Ich habe ein paar Dinge unkenntlich gemacht, um meine persönlichen Daten zu schützen. + +## Wie es begann + +Eines Tages habe ich mir einen coolen neuen und viel besseren Monitor für meinen PC gekauft. +Aber was macht man mit dem alten, wenn man keinen Platz für einen dritten hat? + +Die Idee war simpel: Einfach einen Raspberry Pi anschließen und nützliche Informationen anzeigen lassen. +Wetter, Nachrichten, Kalender und so weiter. +Und um immer gut informiert zu sein, wurde der Bildschirm in der Küche platziert. + +## Geschichte +### Die erste Iteration + +Am Anfang war es einfach eine Webseite in Vanilla JS und natürlich dem üblichen HTML und CSS. +Ich hatte zu dem Zeitpunkt nicht viel Erfahrung in JS, aber ich versuchte "gute" Software-Entwicklungspraktiken zu verwenden, wie Klassenvererbung und [ES-Module](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules). + +Warum Web Technologien nutzen, wenn ich davon wenig Ahnung habe? +Weil ich noch weniger Ahnung von anderen GUI-Technologien hatte. +Außerdem nutzen viele Quellen, die ich benutzen wollte, REST-Schnittstellen und JSON, was einfach in JavaScript super einfach zu handhaben ist. + +Das ganze wurde lokal mit dem Standard-[PHP HTTP-Server](https://www.php.net/manual/en/features.commandline.webserver.php) gehostet und Chromium-Browser im Kiosk-Modus geöffnet. + +Wen der Code wirklich interessiert, der kann [hier](https://github.com/C0ntroller/infoscreen-old) den Repository der zweiten Version finden, die den Großteil des Codes der ersten Version nutzt. + +### Die zweite Iteration + +Beim zweiten Mal war die Idee, statt einen Chromium-Browser zu öffnen, eine Electron App zu schreiben. +Klingt erstmal nicht so viel anders, aber ich dachte, dass es vielleicht performanter sein könnte, da zumindest etwas Bloat fehlt. + +Naja, es war nicht performanter. +Der Code dafür ist trotzdem noch [hier](https://github.com/C0ntroller/infoscreen-old) zu finden. + +### Die dritte Iteration + +Einige Zeit später habe ich angefangen, mich mehr mit React zu beschäftigen. +Irgendwie kam mir der Gedanke, dass was ich damals mit Vanilla JS versucht habe, in React viel einfacher und sauberer umzusetzen wäre. +Also habe ich angefangen, den Code zu portieren. + +Ich habe auch schon von Frameworks für React gehört, die statische Seiten generieren können, wie [Next.JS](https://nextjs.org/) und [Gatsby](https://www.gatsbyjs.com/). +Gatsby schien mir eine gute Wahl zu sein, also habe ich mich dafür entschieden. + +Diesmal wurde es in einem Docker-Container auf meinem eigenen Heimserver gehostet und der Pi öffnet wieder einen Chromium-Browser. + +Damit endet die Entwicklungsgeschichte. + +### Das Ende der Geschichte + +Und nun?
+Die brennensten Fragen mal geklärt: + +> Wird der Bildschirm genutzt? + +Nein. Er war eine ganze Weile in Betrieb, aber mittlerweile bin ich umgezogen und habe keinen schönen Platz mehr. Zudem muss ich sagen, dass man über die Zeit immer weniger drauf schaut... + +> Was gab es für coole Erweiterungen? + +Das war eigentlich eine der coolsten Sachen an dem Projekt. +Denn um Nachts nicht geblendet zu werden, gab es einen Nachtmodus. +Dieser bestand aus einem dunklen Hintergrund, dunkleren Farben und des Deaktivieren der großen Wetterkarte.
+Außerdem gab es einen Bewegungsmelder. Nach 10 Minuten ohne Bewegung wurde der Bildschirm dunkel geschaltet. +Sobald jemand den Raum betrat, wurde der Bildschirm wieder aktiviert.
+Zuletzt hab ich unseren [librespot](https://github.com/librespot-org/librespot) Server noch selbst dazu gebracht, den aktuell laufenden Song dem Infoscreen mitzuteilen. + +## Was war denn nun auf dem Bildschirm? + +Folgende Infos wurden zuletzt angezeigt: +- Zeit +- Wetter und Wetterkarte +- zwei RSS News Feeds +- ein Google Kalender +- Abfahrten der nächsten Straßenbahnstation +- derzeit laufendes Spotify oder (falls nichts läuft) Infos von meinen Pflanzensensoren + +Außerdem wird der Hintergrund durch ca. 20 Bilder rotiert. + +Wenn es spät wird, wird ein Nachtmodus aktiviert und die Wetterkarte durch ein süßes GIF von einem schlafenden Pikachu ersetzt. + +### Wetter + +Für das Wetter nutze ich zuerst die [DarkSky API](https://darksky.net/). +Als Apple den Dienst gekauft und den kostenlosen Zugang beschränkt hat, war klar, dass ich eine Alternative brauche. + +Meine Wahl fiel auf [Pirate Weather](https://pirateweather.net/). +Doch der Dienst war oft nicht zuverlässig. +Wahrscheinlich sollte man eher einen anderen Dienst nutzen. + +### RSS News + +Hier hab ich mir einen Spaß erlaubt: +Dem Feed der [Tagesschau](https://www.tagesschau.de/) wurden einige wenige Artikel des [Postillons](https://www.der-postillon.com/) zugefügt. +Manchmal musste man aber echt raten... + +### Google Calendar + +Hier wurde einfach die [Google Calendar API](https://developers.google.com/calendar) genutzt und das Ergebnis in einer Liste nach Tagen gruppiert angezeigt. + +### Haltestellenauskunft + +Die lokalen Verkehrsbetriebe haben eine API bereitgestellt, um die nächsten Abfahrten an einer Haltestelle abzufragen. +Dokumentiert oder offiziell ist sie nicht so recht und ich habe gehört, dass sie seitdem auch stärker limitiert ist. +Auf jeden Fall wurden einfach die nächsten beiden Haltestellen abgefragt und die Ankuftszeiten inkl. Verspätungen angezeigt. + +### Spotify + +Wie das funktioniert hat, ist gar nicht so simpel. +Vielleicht sollte ich mal einen eigenen Blogpost dazu schreiben. + +Falls nichts lief, wurden stattdessen Infos von meinen Pflanzensensoren angezeigt. + +### Pflanzensensoren + +Hier wurden einfach einige Daten aus meinem [Home Assistant](https://www.home-assistant.io/) zu den Pflanzen in der Küche angezeigt. diff --git a/src/content/portfolio/ol-git.mdx b/src/content/portfolio/ol-git.mdx new file mode 100644 index 0000000..70add78 --- /dev/null +++ b/src/content/portfolio/ol-git.mdx @@ -0,0 +1,81 @@ +--- +title: Overleaf Sync with Git +summary: "Overleaf ist ein webbasierter LaTeX-Editor einfach echt gut in seinem Job. Aber ein Projekt backupen ohne Geld zu zahlen? Unmöglich, sogar wenn man seine Instanz selbst hosted. Das fand ich schlimm und hab ein Script geschrieben, dass ein bestimmtes Projekt regelmäßig über git sichert." +repository: "https://git.c0ntroller.de/c0ntroller/overleaf-git-sync" +pubDate: 2022-07-29 +tags: + - Scripting + - Git +--- + +## Das Problem + +Als ich in der Uni wichtige Paper wie meine Abschlussarbeit schreiben musste, wollte ich nicht böse überrascht werden und stattdessen so viele Backups wie möglich haben. +Denn was wenn meine eigene Festplatte oder die der Overleaf-Instanz kaputt geht? + +Da die Arbeit eh mit LaTeX geschrieben werden sollte, bot sich eine selbstgehostete Instanz von [Overleaf](https://overleaf.com/) an. +Overleaf ist einer der besten Editoren für LaTeX und da es webbasiert ist, kann ich von all meinen Geräten darauf zugreifen. +Und da es am Ende nur ein Frontend für Ordner und Dateien ist, sollte es doch eine einfache Möglichkeit geben, die Projekte zu sichern, oder? + +Nö!
+Nicht nur gibt es kann die selbstgehostete Community-Version keine Projekte mit Cloud-Speicher oder git synchronisieren. +Es gibt diese Möglichkeit in der zentralen Version, die von Overleaf selbst gehostet wird aber diese befindet sich hinter einer Paywall. + +Und der Preis dafür ist nichtmal gering, selbst als Student sollte ich damals 70€/Jahr zahlen! +Das ist einfach nur Abzocke und Blödsinn. + +Da muss es doch einen besseren Weg geben. + +## Wo sind meine Daten? + +Erstmal habe ich Overleaf mit Docker aufgesetzt und ein kleines Projekt erstellt. + +Jetzt müssten die Dateien doch irgendwo auf der Festplatte liegen, oder? + +Wieder falsch.
+Also teilweise, Bilder findet man auf der Festplatte. +Aber ich vermute den ganzen LaTeX-Kram pipen sie in die MongoDB, die der Service nutzt. + +Also nochmal von vorn. + +Gibt es eine API? +Klar, das Frontend nutzt ja auch eine. +Aber dokumentiert ist da nichts. +Wir können dank OpenSource aber einfach ins Repo schauen - und finden eine Datei [`router.js`](https://github.com/overleaf/overleaf/blob/main/services/web/app/src/router.js). +Jetzt musste ich nur noch herausfinden, wie man die API nutzt. + +## \*Hackervoice\*: I'm in + +Im Frontend kann man ganz gut nachvollziehen, wie das geht. +Zuerst einmal erfolgt die Authentifizierung über Sessions-IDs. +Beim Einloggen sendet man einen POST-Request an `/login` mit den Zugangsdaten und einem CSRF-Token, in diesem Moment wird die Session-ID gültig. + +Über die zuvor erwähnte Datei war gut nachvollziehbar, welche Routen wir nun nutzen konnten. +Ich brauchte nur eine: Den Download eines ganzen Projekts. + +Und das war einfacher als erwartet, denn man kann auch das im Browser nachvollziehen: `/project/{id}/download/zip` + +Jetzt hatte ich alle Puzzleteile und konnte mir ein einfaches Python-Script schreiben, das diese Requests durchführt. + +## Was tun mit der Zip? + +Theoretisch hätte man hier etwas abkürzen können und einfach alle Befehle in Python mit `subprocess` ausführen können. +Aber das fühlte sich falsch an und stattdessen habe ich liber zwei Bash-Skripte geschrieben. + +Das erste setzt den git-Ordner auf. +Es klont das Repository, in dem wir unsere Sachen ablegen wollen und wechselt auf den Branch, den wir nutzen wollen. +Dann wird die zuvor heruntergeladene Zip-Datei in diesem Ordner entpackt. + +Das zweite Bash-Script erstellt einen git-Commit und pusht die Änderungen ins Remote-Repository. + +Das wars auch schon. + +## Was könnte man besser machen? + +Das ganze ist mehr Script als nutzbares Programm, daher gibt es natürlich Dinge zu verbessern: + +- Eine `.env` Datei benutzen, statt ein Python-Dictionary für die Einstellungen und Zugangsdaten. +- Mehr Sync-Methoden wie verschiedene Cloud-Speicher einbauen. +- OAuth-Support (Werde ich wahrscheinlich eh nie machen). + +Das Projekt ist aber für mich hier abgeschlossen, also werden die anderen Punkte wohl auch nichts... diff --git a/src/content/portfolio/photo-sync.mdx b/src/content/portfolio/photo-sync.mdx new file mode 100644 index 0000000..acc57a2 --- /dev/null +++ b/src/content/portfolio/photo-sync.mdx @@ -0,0 +1,39 @@ +--- +title: Google Photo Sync +summary: "Irgendwelchen Apps vollen Zugriff auf seine Google Photos geben? Uff. Vor allem wenn es um Kleinigkeiten wie Bildschirmschoner geht. Also habe ich kurz gebastelt, um ein Album lokal zu halten und regelmäßig zu synchronisieren." +repository: https://git.c0ntroller.de/c0ntroller/google-photo-album-sync +pubDate: 2022-08-08 +tags: + - Google API + - Scripting +--- + +Warum sollte ich für einen Bildschirmschoner auf meine Privatsphäre verzichten? + +## Storytime + +Auf meinem NVidia Shield mit Android TV wollte ich einen Bildschirmschoner einrichten. +Es gibt natürlich viele davon und Google Photos als Quelle ist natürlich ein sehr einfacher Weg, wenn die eigenen Bilder sowieso dort liegen. + +Also die nächstbeste App installiert und einloggen. + +Oder auch nicht. + +Denn die App wollte nicht nur Zugriff auf meine Bilder. +Sie wollte auch Zugriff auf meine persönlichen Daten, E-Mail-Adresse, Kontaktliste und andere Dinge. +Ja ich weiß, ich rede hier über Privatsphäre und Datenschutz, aber nutze Google Photos - aber lieber hat Google meine Daten als irgendeine App, der ich nicht vertrauen kann. + +Aber vielleicht gibt es ja doch eine Möglichkeit - einfach die Bilder offline bereitstellen, dann bleibt der Google Account privat. + +## Die Lösung + +Ich hatte schon einige Erfahrung mit den Google APIs vom [Infoscreen](/projekt/infoscreen) und dem [Simple Callback Server](/projekt/simple-cb). + +Um auch Leuten ohne diese Erfahrung etas Hilfe zu geben, habe ich gleich eine CLI-Anwendung für das Setup erstellt. +In der `README` sollte eigentlich ein guter Startpunkt sein, um seine eigenen Bilder zu synchen. + +## Was noch? + +Hat man mehrere Bilder mit gleichem Namen, kann noch etaws kaputtgehen. +Grundsätzlich funktioniert aber auch dieser Fall. +Theoretisch könnte man noch Hashing wie MD5 benutzen, um die Bilder besser zu unterscheiden. diff --git a/src/content/portfolio/simple-cb.mdx b/src/content/portfolio/simple-cb.mdx new file mode 100644 index 0000000..8c18792 --- /dev/null +++ b/src/content/portfolio/simple-cb.mdx @@ -0,0 +1,37 @@ +--- +title: Simple Callback Server +summary: "Um große APIs wie von Google oder Spotify zu nutzen, wird häufig ein OAuth2-Flow benutzt. Für meine kleinen privaten Projekte reicht aber eigentlich auch der Refresh-Token, den man nach dem initialen Setup immer wieder verwenden kann. Um diesen Token aus dem Flow zu bekommen, habe ich einen Mini-Webserver erstellt, welcher einfach den Body der Request printed, sodass man den Token ganz einfach bekommt. Simpel aber effektiv." +repository: "https://git.c0ntroller.de/c0ntroller/simple-callback-server" +pubDate: 2022-08-08 +tags: + - JavaScript + - Scripting +--- + +Das wahrscheinlich einfachste Projekt. + +## Was macht es? + +Das einfachste, was man sich vorstellen kann: Ein `express`-Server printed einfach alle Header und den POST-Body oder die GET-Parameter eines Aufrufs. +Manchmal braucht man sowas eben. + +## Für was kann ich das benutzen? + +Am Ende eines OAuth2-Flows gibt es im Normalfall einen "Callback" an den "Dienst", den man betreibt und wo man den Nutzer authentifizieren will. +Callback bedeutet hier einfach nur, dass die konfigurierte Server-URL mit einigen Parametern wie Tokens aufgerufen wird. + +In meinen eigenen Projekten bin der User aber ich selbst und jedes mal den Flow zu durchlaufen, ist mühselig. + +Stattdessen gibt es häufig einen Refresh-Token, der mit dem Callback mitgesendet wird. +Der steckt aber nur im Callback und um ihn auszulesen, muss man den Callback auslesen. + +Nun, hier schließt sich der Kreis. + +## Klingt unnötig kompliziert + +Ist es. Aber OAuth2 ist nunmal kompliziert. + +## Und was ist mit `whoami`? + +Dass es einen Docker-Container gibt, der quasi genau das macht, ist mir leider erst viel später aufgefallen. +Naja, dümmer bin ich nicht geworden. diff --git a/src/content/portfolio/terminal.mdx b/src/content/portfolio/terminal.mdx new file mode 100644 index 0000000..d8d3bd8 --- /dev/null +++ b/src/content/portfolio/terminal.mdx @@ -0,0 +1,109 @@ +--- +title: Terminal (veraltet) +summary: "Das hier beschriebene \"Terminal\" war meine erste eigene Webseite. Nur mit JavaScript programmiert, konnte man die Seite mit Befehlen navigieren. Später war dieses Terminal auch auf der zweiten Iteration eingebunden. Es gab echt viele Funktionen, Eastereggs und Shortcuts. Auch wenn es so nicht mehr verfügbar ist, habe ich hier noch die Dokumentation dazu." +repository: https://git.c0ntroller.de/c0ntroller/frontpage +pubDate: 2022-02-04 +tags: + - Veraltet + - JavaScript + - React +--- + +> [!info] Dieses Projekt ist veraltet. +> Als Kontext: Die ersten Versionen meiner Website waren nur eine "CLI" in React. +> Unschwer zu erkennen, dass sich das geändert hat. +> Mittlerweile habe ich die CLI komplett entfernt. +> Aus nostalgischen Gründen möchte ich die Dokumentation aber hier lassen. + +Hi und Willkommen auf meiner Website. +Dies ist die Dokumentation für meine CLI. +Probiert sie mal aus, wenn ihr es noch nicht gemacht habt! + +## Warum? + +Vor allem aus Langeweile und weil ich Codeschreiben mag. +Und auf meiner eigenen Webseite kann ich auch einfach nerdig sein und wilde Dinge programmieren. + +## Wie? +### Frameworks + +Die Webseite ist komplett in [Next.js](https://nextjs.org/) geschrieben, einem JavaScript/React-Framework. +Also ist das Backend letzlich in [Node.js](https://nodejs.org/). +Die Projekteinträge sind in [AsciiDoc](https://projects.eclipse.org/projects/asciidoc.asciidoc-lang) verfasst. +Das ganze hoste ich in einerm [Docker](https://www.docker.com/) Container auf meinem eigenen Server. + +### Der Beginn +#### Ein eigener Platz im Internet +Ich wollte schon immer mal eine eigene Webseite haben. +Aber langweilig sollte sie auch nicht einfach sein. +Eines Tages kam mir dann die Idee - wie wäre es mit einer Webseite, die im Grunde eine Konsole ist. +Klingt eigentlich nicht so schwer, es besteht ja nur aus einem Eingabefeld und einem Log/Verlauf. + +Aber ich wollte mehr: +Der Code sollte so modular wie möglich sein, damit es in Zukunft einfach ist, Befehle, Projektlogs und Komponenten hinzuzufügen. +Außerdem wollte ich coole Features wie Tab-Vervollständigung und Shortcuts. + +Aber da es schwer ist, Freunden oder Arbeitgebern, die sich nicht mit Informatik auskennen, ein CLI ohne Erklärung hinzuwerfen, entschied ich mich, auch einen "normalen" Frontend-Teil hinzuzufügen. + +#### Implementierung + +> Klingt eigentlich nicht so schwer [...] + +Unsinn. Es war schwer. Aber am Ende hat es sich gelohnt. +Alle Kommandos sind in einer Quelldatei nach einem spezifischen Format definiert, sodass man super einfach neue hinzufügen oder alte entfernen kann. +Man kann Flags und andere Attribute hinzufügen und die Funktion definieren, die ausgeführt wird, wenn das Kommando ausgeführt wird. +Die Projektdateien sind in einer separaten Repository und Ordner auf meinem Server, der als Docker-Volume eingebunden ist. +Somit kann man sie super einfach aktualisieren, ohne das ganze Projekt neu bauen zu müssen. + +Als die CLI fertig war, war sie für einige Monate meine Hauptseite. +Dann habe ich den "normalen" Teil erstellt, der im Grunde ein Blog und mein Portfolio ist. + +#### Autodeployment +Wir mögen hier maximale Automatisierung, also musste auch eine CI/CD-Pipeline her. +Da ich mein eigenes Gitea zum Spielen habe, musste auch die Pipeline selbstgehostet sein. +Also habe ich [Drone](https://www.drone.io/) benutzt. +Der Docker-Container landet in der eigenen Registry, also ist es sicher, wenn ich mal geheime oder private Daten in das Projekt einfügen will. + +Die Projektdateien werden zuerst mit [Asciidoctor](https://asciidoctor.org/) gebaut, um ihre Gültigkeit zu überprüfen. +Dann zieht ein Skript sie auf den Server. +Da das Git-Verzeichnis der Projekte auf dem Server nur im Docker-Container eingebunden ist, reicht ein schnelles Neuladen, damit die Dateien auf dem Server verfügbar sind. + +## Die Zuuukuuunft + +Was wird es noch für Features geben? +Keine Ahnung. + +Wahrscheinlich wird es so sein, wie alles hier: +Ich hab eine Idee (ob die gut oder schlecht ist, ist irrelevant) und setze sie entweder sofort um oder füge sie als Issue hinzu. + +## CLI Shortcuts +Wie vorher erwähnt, gibt es einige Shortcuts, um die Bedienung zu erleichtern. + +| Key | Effect | +|-----|--------| +| Tab | Fügt die aktuellen Vorschläge aus der Autovervollständigung ein. | +| / | Durchsuchen der letzten Befehle. | +| Ctrl+L | Löscht den Verlauf. Ähnlich wie `clear`. | +| Ctrl+D | Verlässt die Seite. Wenn das nicht funktioniert ([JavaScript-Einschränkung](https://developer.mozilla.org/en-US/docs/Web/API/Window/close)) geht es zurück in die Seitenhistorie. Ähnlich wie `exit`. | +| Ctrl+U/Ctrl+C | Entfernt die aktuelle Eingabe. | +| Esc | Schließt das Dialogmodal. | + +## Zeug, auf das ich stolz bin +- Jede Zeile der Historie wird in einem eigenen Format geparst. + * `%{command}` wird als klickbares Kommando dargestellt. + * `#{link text|url}` wird als Link dargestellt. +- Die Projektlogs werden dynamisch geladen. Sie können jederzeit aktualisiert werden. + * Aber sie werden im Backend gerendert. Für die Hauptseite (nicht die CLI) ist theoretisch kein JS notwendig. +- Es gibt viele Eastereggs. Einige sind für bestimmte Personen, einige für mich und einige einfach nur zum Spaß. +- Ich habe einige benutzerdefinierte Anmerkungen für Codeblöcke erstellt, die fehlerhaften Code anzeigen (falsche Syntax/wird nicht kompiliert/etc.). + +
+```rust +// So sieht ein fehlerhafter Codeblock aus +fn main() { + let x = 5; + x = 6; +} +``` +
+ diff --git a/src/content/portfolio/tufast.mdx b/src/content/portfolio/tufast.mdx new file mode 100644 index 0000000..8d2d77f --- /dev/null +++ b/src/content/portfolio/tufast.mdx @@ -0,0 +1,47 @@ +--- +title: TUfast TUD +summary: "TUfast ist eine Browser-Erweiterung, die von mehreren tausend Studierenden der TU Dresden genutzt wird. Sie bietet Auto-Login auf den wichtigsten Portalen der TU Dresden, Shortcuts, Redirects und weitere QoL-Features. Ich war einer der Entwickler." +repository: https://github.com/TUfast-TUD/TUfast_TUD +relatedWebsite: https://tu-fast.de/ +pubDate: 2022-06-23 +tags: + - "Öffentlich" + - TypeScript + - Open Source +--- + +Meine Arbeit an einer Browser-Extension, die von tausenden Stundierenden genutzt wird. + +## Was ist TUfast? +TUfast ist einen Browser-Erweiterung, die von einigen Studierenden der TU Dresden entwickelt wurde. +Ursprünglich ein Ein-Mann-Projekt hab ich schon früh Pull Requests gestellt und somit mitentwickelt. +Nach einer Zeit hat sich ein Team gebildet und die Extension wurde immer weiter verbessert und erweitert. + +Die Hauptfunktion besteht darin, einen Auto-Login auf all den verschiedenen Portalen der TU Dresden zu ermöglichen. +Zusätzlich gibt es noch viele weitere Features, wie Shortcuts, Redirects von Suchmaschinen andere QoL-Features. +Insgesamt hat die Extension mehrere tausend Nutzer. + +Das Projekt ist Open Source und [auf Github gehostet](https://github.com/TUfast-TUD/TUfast_TUD). + +TUfast ist eine Browser-Erweiterung, die von einigen Studierenden der TU Dresden entwickelt wurde. +Die Hauptfunktion besteht darin, Benutzernamen und Passwörter auf verschiedenen Login-Seiten der TU-Portale einzufügen. +Weitere Funktionen sind Shortcuts, Redirects von Suchmaschinen, Verbesserungen und mehr. +Es hat mehrere tausend Nutzer. + +Das Projekt ist Open Source und [auf Github gehostet](https://github.com/TUfast-TUD/TUfast_TUD). +Mittlerweile gibt es leider keinen richtigen Maintainer mehr, aber der Owner ist offen für Merge Requests und Hilfe. + +## Was habe ich gemacht? +Ich war einer der ersten und einer der Hauptprogrammierer des Projekts. +An einem Punkt haben wir beschlossen, die Codebase auf TypeScript umzustellen, [Manifest V3](https://developer.chrome.com/docs/extensions/mv3/intro/) zu unterstützen und dabei gleich mit all den bis dahin verteilten Scripten aufzuräumen. +Zu diesem Zeitpunkt war der komplette Backendcode 100% meine Arbeit. + +Später wurde noch eine 2-Faktor-Authentifizierung für viele Logins hinzugefügt. +Obwohl ich zu diesem Zeitpunkt nicht einmal mehr Student war, habe ich eine Implementierung des TOTP-Algorithmus gebaut, sodass der Auto-Login vollständig von der Extension übernommen werden konnte. +Natürlich ist das absolut nicht optimal, 2FA so einfach zu automatisieren, aber die Realität ist, dass viele Studierende diese Funktion nutzen, um sich den Login zu erleichtern. +Ich selbst würde es nicht nutzen, aber ich verstehe den Nutzen dahinter. + +## Weitere Informationen + +- [Repository](https://github.com/TUfast-TUD/TUfast_TUD) +- [Website](https://tu-fast.de/) diff --git a/src/data/socials.json b/src/data/socials.json new file mode 100644 index 0000000..aef377a --- /dev/null +++ b/src/data/socials.json @@ -0,0 +1,23 @@ +[ + { + "name": "GitHub", + "url": "https://github.com/C0ntroller", + "icon": "simple-icons:github" + }, { + "name": "LinkedIn", + "url": "https://www.linkedin.com/in/c0ntroller/", + "icon": "simple-icons:linkedin" + }, { + "name": "Instagram", + "url": "https://www.instagram.com/c0ntroller/", + "icon": "simple-icons:instagram" + }, { + "name": "Steam", + "url": "https://steamcommunity.com/id/c0ntroller/", + "icon": "simple-icons:steam" + }, { + "name": "Discord", + "url": "https://discordapp.com/users/224208617820127233", + "icon": "simple-icons:discord" + } +] \ No newline at end of file diff --git a/src/layouts/BaseLayout.astro b/src/layouts/BaseLayout.astro new file mode 100644 index 0000000..969b7ba --- /dev/null +++ b/src/layouts/BaseLayout.astro @@ -0,0 +1,72 @@ +--- +import "../styles/global.css"; +import Navigation from "../components/Navigation.astro"; +import { Font } from "astro:assets"; + +interface Props { + title: string; + theme?: "portfolio" | "blog" | "book" | "default"; + description?: string; +} + +const { + title, + theme = "default", + description = "Persönliche Webseite von Daniel Kluge", +} = Astro.props; +--- + + + + + + + + + + + + + + + + + + + + + + + + + + {title} + + + + +
+ +
+ + diff --git a/src/pages/blog/[...slug].astro b/src/pages/blog/[...slug].astro new file mode 100644 index 0000000..7826ff7 --- /dev/null +++ b/src/pages/blog/[...slug].astro @@ -0,0 +1,41 @@ +--- +import { getCollection, render } from 'astro:content'; +import BaseLayout from '../../layouts/BaseLayout.astro'; +import "katex/dist/katex.min.css"; +import "rehype-callouts/theme/obsidian" + +export async function getStaticPaths() { + const blogEntries = await getCollection('blog'); + return blogEntries.map(entry => ({ + params: { slug: entry.id }, props: { entry }, + })); +} + +const { entry } = Astro.props; +const { Content } = await render(entry); +--- + +
+

{entry.data.title}

+
+ +
+
+ +
+ +
+
+ + diff --git a/src/pages/blog/index.astro b/src/pages/blog/index.astro new file mode 100644 index 0000000..bb79b51 --- /dev/null +++ b/src/pages/blog/index.astro @@ -0,0 +1,47 @@ +--- +import { getCollection } from 'astro:content'; +import BaseLayout from '../../layouts/BaseLayout.astro'; + +const posts = await getCollection('blog'); +posts.sort((a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf()); +--- + +
+

Artikel & Gedanken

+

Gedanken über Technologie, Design und das Leben.

+
+ + +
+ diff --git a/src/pages/book/[...slug].astro b/src/pages/book/[...slug].astro new file mode 100644 index 0000000..5e98ae5 --- /dev/null +++ b/src/pages/book/[...slug].astro @@ -0,0 +1,112 @@ +--- +import { getCollection, render } from 'astro:content'; +import BaseLayout from '../../layouts/BaseLayout.astro'; +import BookNavigation from '../../components/BookNavigation.astro'; +import "katex/dist/katex.min.css"; +import "rehype-callouts/theme/obsidian"; +import "../../styles/md-custom.css"; + +export async function getStaticPaths() { + const pages = await getCollection('book'); + pages.sort((a, b) => a.id.localeCompare(b.id)); + + return pages.map((page, index) => { + return { + params: { slug: page.id }, + props: { + page, + prevPage: index > 0 ? pages[index - 1] : null, + nextPage: index < pages.length - 1 ? pages[index + 1] : null, + allPages: pages + } + }; + }); +} + +const { page, prevPage, nextPage, allPages } = Astro.props; +const { Content } = await render(page); + +const chapters = allPages.reduce((acc, p) => { + const [chapter] = p.id.split('/'); + if (!acc[chapter]) acc[chapter] = []; + acc[chapter].push(p); + return acc; +}, {} as Record); +--- + +
+

{page.data.title}

+ +
+ + +
+ {Object.entries(chapters).map(([chapter, items]) => ( +
+

{chapter.replace(/-/g, ' ').toUpperCase()}

+ +
+ ))} +
+
+
+ + diff --git a/src/pages/book/index.astro b/src/pages/book/index.astro new file mode 100644 index 0000000..a106ade --- /dev/null +++ b/src/pages/book/index.astro @@ -0,0 +1,44 @@ +--- +import { getCollection } from 'astro:content'; +import BaseLayout from '../../layouts/BaseLayout.astro'; + +const pages = await getCollection('book'); +pages.sort((a, b) => a.data.part - b.data.part); + +const chapters = pages.reduce((acc, page) => { + const chapter = page.data.chapter["id"]; + if (!acc[chapter]) acc[chapter] = []; + acc[chapter].push(page); + return acc; +}, {} as Record); +--- + +
+

Das Buch

+

Ein fortlaufendes Werk über Software, Design und Architektur.

+
+ +
+

Inhaltsverzeichnis

+ {Object.entries(chapters).map(([chapter, items]) => ( +
+

{chapter.replace(/-/g, ' ').toUpperCase()}

+ +
+ ))} +
+
+ diff --git a/src/pages/index.astro b/src/pages/index.astro new file mode 100644 index 0000000..0dab9b6 --- /dev/null +++ b/src/pages/index.astro @@ -0,0 +1,226 @@ +--- +import WelcomeTypewriter from '../components/WelcomeTypewriter.astro'; +import BaseLayout from '../layouts/BaseLayout.astro'; +import { Icon } from 'astro-icon/components'; +import socials from "../data/socials.json"; +--- + + + + + diff --git a/src/pages/portfolio/[...slug].astro b/src/pages/portfolio/[...slug].astro new file mode 100644 index 0000000..de85600 --- /dev/null +++ b/src/pages/portfolio/[...slug].astro @@ -0,0 +1,52 @@ +--- +import { getCollection, render } from 'astro:content'; +import BaseLayout from '../../layouts/BaseLayout.astro'; +import "katex/dist/katex.min.css"; +import "rehype-callouts/theme/obsidian" + +export async function getStaticPaths() { + const portfolioEntries = await getCollection('portfolio'); + return portfolioEntries.map(entry => ({ + params: { slug: entry.id }, props: { entry }, + })); +} + +const { entry } = Astro.props; +const { Content } = await render(entry); +--- + +
+

{entry.data.title}

+

{entry.data.summary}

+ {entry.data.tags && ( +
+ {entry.data.tags.map(tech => {tech})} +
+ )} +
+ +
+ +
+
+ + diff --git a/src/pages/portfolio/index.astro b/src/pages/portfolio/index.astro new file mode 100644 index 0000000..699fc05 --- /dev/null +++ b/src/pages/portfolio/index.astro @@ -0,0 +1,55 @@ +--- +import { getCollection } from 'astro:content'; +import BaseLayout from '../../layouts/BaseLayout.astro'; + +const projects = await getCollection('portfolio'); +projects.sort((a, b) => b.data.pubDate.getTime() - a.data.pubDate.getTime()); +--- + +
+

Meine Arbeiten

+

Eine Auswahl aktueller Projekte und Experimente.

+
+ + +
+ diff --git a/src/remark-modified-time.mjs b/src/remark-modified-time.mjs new file mode 100644 index 0000000..1881cca --- /dev/null +++ b/src/remark-modified-time.mjs @@ -0,0 +1,22 @@ +import { execSync } from "child_process"; +import { statSync } from "fs"; + +export function remarkModifiedTime() { + return function (tree, file) { + const filepath = file.history[0]; + try { + const result = execSync(`git log -1 --pretty="format:%cI" "${filepath}"`); + // If result is empty or undefined, fallback to fs stat + if (!result || !result.toString().trim()) { + throw new Error("No git history"); + } + file.data.astro.frontmatter.lastModified = result.toString(); + return; + } catch (e) { + // Ignore, fallback to fs stat + const result = statSync(filepath); + file.data.astro.frontmatter.lastModified = result.mtime.toISOString(); + return; + } + }; +} diff --git a/src/styles/global.css b/src/styles/global.css new file mode 100644 index 0000000..dc8839b --- /dev/null +++ b/src/styles/global.css @@ -0,0 +1,221 @@ +*, *::before, *::after { + box-sizing: border-box; +} + +@view-transition { + navigation: auto; +} + +::view-transition-group(root) { + background-color: var(--bg-color); +} + +@property --accent-base { + syntax: ''; + inherits: true; + initial-value: 214, 134, 249; +} +@property --bg-image { + syntax: ''; + inherits: true; + initial-value: url('../assets/backgrounds/bubble.svg'); +} +@property --bg-image-static { + syntax: ''; + inherits: true; + initial-value: url('../assets/backgrounds/bubble-static.svg'); +} +@property --glass-bg { + syntax: ''; + inherits: true; + initial-value: rgba(20, 25, 35, 0.4); +} +@property --glass-border { + syntax: ''; + inherits: true; + initial-value: rgba(255, 255, 255, 0.08); +} +@property --glass-blur { + syntax: ''; + inherits: true; + initial-value: 16px; +} +@property --glass-shadow { + syntax: ''; + inherits: true; + initial-value: 0 8px 32px 0 rgba(0, 0, 0, 0.37); +} +@property --text-main { + syntax: ''; + inherits: true; + initial-value: #e0e7f0; +} +@property --text-muted { + syntax: ''; + inherits: true; + initial-value: #92a1b6; +} +@property --bg-color { + syntax: ''; + inherits: true; + initial-value: #0d0117; +} +@property --accent-base--default { + syntax: ''; + inherits: true; + initial-value: 214, 134, 249; +} +@property --accent-base--portfolio { + syntax: ''; + inherits: true; + initial-value: 255, 181, 102; +} +@property --accent-base--blog { + syntax: ''; + inherits: true; + initial-value: 111, 221, 246; +} +@property --accent-base--book { + syntax: ''; + inherits: true; + initial-value: 247, 110, 138; +} + +:root { + color-scheme: dark; + --bg-color: #0d0117; + --text-main: #e0e7f0; + --text-muted: #92a1b6; + + --glass-bg: rgba(20, 25, 35, 0.4); + --glass-border: rgba(255, 255, 255, 0.08); + --glass-blur: 16px; + --glass-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.37); + + --accent-base--default: 214, 134, 249; + --accent-base--portfolio: 255, 181, 102; + --accent-base--blog: 111, 221, 246; + --accent-base--book: 247, 110, 138; + --accent-base: var(--accent-base--default); +} + +body[data-theme="default"] { + --bg-image: url('../assets/backgrounds/bubble.svg'); + --bg-image-static: url('../assets/backgrounds/bubble-static.svg'); +} + +body[data-theme="portfolio"] { + --accent-base: var(--accent-base--portfolio); + --bg-image: url('../assets/backgrounds/dual-ripples.svg'); + --bg-image-static: url('../assets/backgrounds/dual-ripples-static.svg'); +} + +body[data-theme="blog"] { + --accent-base: var(--accent-base--blog); + --bg-image: url('../assets/backgrounds/ripple-line.svg'); + --bg-image-static: url('../assets/backgrounds/ripple-line-static.svg'); +} + +body[data-theme="book"] { + --accent-base: var(--accent-base--book); + --bg-image: url('../assets/backgrounds/curve-ripple.svg'); + --bg-image-static: url('../assets/backgrounds/curve-ripple-static.svg'); +} + +html { + background-color: var(--bg-color); +} + +body { + font-family: 'Segoe UI', Arial; + background-color: var(--bg-color); + color: var(--text-main); + margin: 0; + padding: 0; + line-height: 1.6; + min-height: 100vh; + background-image: var(--bg-image), radial-gradient(circle at 50% 0%, rgba(var(--accent-base), 0.15), transparent 60%); + background-size: cover, auto; + background-position: center; + background-attachment: fixed; + transition: background-image 0.6s ease; +} + +@media (prefers-reduced-motion: reduce) { + body { + background-image: var(--bg-image-static), radial-gradient(circle at 50% 0%, rgba(var(--accent-base), 0.15), transparent 60%); + } +} + +h1, h2, h3, h4, h5, h6 { + color: rgb(var(--accent-base)); + font-weight: 600; + margin-top: 2rem; + margin-bottom: 1rem; +} + +a { + color: inherit; + text-decoration: none; + transition: all 0.2s ease-in-out; +} + +a:hover { + text-shadow: 0 0 8px rgba(var(--accent-base), 0.6); +} + +.glass-container { + background: var(--glass-bg); + backdrop-filter: blur(var(--glass-blur)); + -webkit-backdrop-filter: blur(var(--glass-blur)); + border: 1px solid var(--glass-border); + box-shadow: var(--glass-shadow); + border-radius: 1rem; + padding: 2rem; + transition: border-color 0.3s ease, box-shadow 0.3s ease; +} + +.glass-container:hover { + border-color: rgba(var(--accent-base), 0.3); + box-shadow: 0 8px 32px 0 rgba(var(--accent-base), 0.1); +} + +.container { + max-width: 800px; + margin: 0 auto; + padding: 2rem; +} + +/* Typography styles for markdown content */ +.prose code { + font-family: var(--font-cascadia-code); + background: rgba(255, 255, 255, 0.1); + padding: 0.2em 0.4em; + border-radius: 0.25rem; + font-size: 0.9em; +} + +.prose pre { + background: #111827; + padding: 1.5rem; + border-radius: 0.5rem; + overflow-x: auto; + border: 1px solid var(--glass-border); +} + +.prose pre code { + background: transparent; + padding: 0; +} + +/* Mobile Responsiveness */ +@media (max-width: 600px) { + .container { + padding: 1rem; + } + .glass-container { + padding: 1.25rem; + } + h1 { font-size: 1.75rem; } + h2 { font-size: 1.5rem; } +} diff --git a/src/styles/md-custom.css b/src/styles/md-custom.css new file mode 100644 index 0000000..03eaa11 --- /dev/null +++ b/src/styles/md-custom.css @@ -0,0 +1,49 @@ +.callout { + mix-blend-mode: unset !important; +} + +table { + border-collapse: collapse; +} + +th { + border-bottom: 1px solid var(--text-muted, #ccc); + font-weight: bold; +} +td { + border-top: 1px solid var(--text-muted, #ccc); +} + +th:not(:first-child), td:not(:first-child) { + border-left: 1px solid var(--text-muted, #ccc); +} +th:not(:last-child), td:not(:last-child) { + border-right: 1px solid var(--text-muted, #ccc); +} + +th, td { + padding: 0.5em; + text-align: right; +} + +blockquote, output { + position: relative; + border-top: 1px solid var(--text-muted, #ccc); + border-bottom: 1px solid var(--text-muted, #ccc); + margin: 0; + padding: 10px; + padding-left: 25px; + font-family: var(--font-cascadia-code); + + p { + margin: 0; + } +} + +blockquote::before { + content: ">"; + font-weight: bold; + position: absolute; + left: 0; + color: var(--text-muted, #ccc); +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..8bf91d3 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,5 @@ +{ + "extends": "astro/tsconfigs/strict", + "include": [".astro/types.d.ts", "**/*"], + "exclude": ["dist"] +}