From: Drew Edgar <2647430E@student.gla.ac.uk> Date: Tue, 24 Jun 2025 19:59:09 +0000 (+0100) Subject: Initial commit X-Git-Url: http://git.drewedgar.com/website.git/commitdiff_plain/c38e46874b2fbe844211d861471e4b8c2d624359?ds=inline Initial commit --- c38e46874b2fbe844211d861471e4b8c2d624359 diff --git a/.eleventy.js b/.eleventy.js new file mode 100644 index 0000000..255d66f --- /dev/null +++ b/.eleventy.js @@ -0,0 +1,43 @@ +import { feedPlugin } from "@11ty/eleventy-plugin-rss"; +import pluginSyntaxHighlight from "@11ty/eleventy-plugin-syntaxhighlight"; +import pluginNavigation from "@11ty/eleventy-navigation"; +import Image from "@11ty/eleventy-img"; +import { eleventyImageTransformPlugin } from "@11ty/eleventy-img"; +import markdownIt from "markdown-it"; + +import path from "path"; + +export default function(eleventyConfig) { + eleventyConfig.addGlobalData( + 'layout', + './layouts/index.liquid' // Relative to `./src/_includes` + ); + + eleventyConfig + .addPassthroughCopy("./content/feed/pretty-atom-feed.xsl") + .addPassthroughCopy({ "src/images/favicon": "/" }) + .addPassthroughCopy({ "src/videos": "/videos" }); + + + + eleventyConfig.addPlugin(pluginSyntaxHighlight, { + preAttributes: { tabindex: 0 } + }); + eleventyConfig.addPlugin(pluginNavigation); + + eleventyConfig.addPlugin(eleventyImageTransformPlugin); + + let options = { + html: true, + breaks: true, + linkify: true + }; + eleventyConfig.setLibrary("md", markdownIt(options)); + + return { + dir: { + input: "./src", + output: "./dist" + } + }; +}; \ No newline at end of file diff --git a/.github/workflows/eleventy_build.yml b/.github/workflows/eleventy_build.yml new file mode 100644 index 0000000..bc828d1 --- /dev/null +++ b/.github/workflows/eleventy_build.yml @@ -0,0 +1,27 @@ +name: Eleventy Build +on: + push: + branches: + - main + +jobs: + build_deploy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + + - name: Setup Node + uses: actions/setup-node@v1 + with: + node-version: '22.x' + + - run: npm install + - run: npm run build + + + - name: Deploy + uses: peaceiris/actions-gh-pages@v3 + with: + publish_dir: dist + publish_branch: gh-pages + github_token: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6f07c9b --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +node_modules/ +public/ +**/.DS_Store +dist/ +.vscode/ +.nova/ +.cache/ +*.swp diff --git a/README.md b/README.md new file mode 100644 index 0000000..f3e5b66 --- /dev/null +++ b/README.md @@ -0,0 +1,25 @@ +## About + +This is my personal website, hosting my CV, blog posts, and anything else I think is worth sharing. + +## How to Use + +To build and host the website: +```zsh +$ npm install +$ npm run build && npm run start +``` + + +## Style guide + +This project uses commit and issue conventions from [Conventional Commits](https://www.conventionalcommits.org). + +## Sources Used: + +- [Eleventy Base Blog](https://github.com/11ty/eleventy-base-blog) +- [Eleventy Docs](https://www.11ty.dev/docs/) +- [Michael Engen – Building My Resume in HTML using Eleventy](https://michaelengen.com/posts/my-eleventy-resume/) +- [bnijenhuis.nl – Adding a favicon in Eleventy](https://bnijenhuis.nl/notes/adding-a-favicon-in-eleventy/) +- Icons from [Feather](https://feathericons.com) +- [Marcin Wichary — Dark theme in a day](https://mwichary.medium.com/dark-theme-in-a-day-3518dde2955a) diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..ef88b74 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,2488 @@ +{ + "name": "blog", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "blog", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "@11ty/eleventy-img": "^6.0.4", + "@11ty/eleventy-navigation": "^1.0.4", + "@11ty/eleventy-plugin-rss": "^2.0.4", + "@11ty/eleventy-plugin-syntaxhighlight": "^5.0.1", + "esbuild": "^0.25.12", + "markdown-it": "^14.1.0", + "puppeteer": "^24.17.0", + "stacked-alpha-video": "^1.0.10" + } + }, + "node_modules/@11ty/eleventy-fetch": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@11ty/eleventy-fetch/-/eleventy-fetch-5.1.0.tgz", + "integrity": "sha512-gSmCA3olJxRwtTkXyS+KIanq1kEufCC+JsHyTa7ta5NqmeUQlWA8zEngtXrDl+ebrAvFz2bNaxLd+0ERpnnSPQ==", + "license": "MIT", + "dependencies": { + "@11ty/eleventy-utils": "^2.0.7", + "@rgrove/parse-xml": "^4.2.0", + "debug": "^4.4.0", + "flatted": "^3.3.3", + "p-queue": "6.6.2" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/11ty" + } + }, + "node_modules/@11ty/eleventy-img": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/@11ty/eleventy-img/-/eleventy-img-6.0.4.tgz", + "integrity": "sha512-jSy9BmubVs0mN76dcXWfSYDgRU+1+/rq/SxUR3MgIvTUAJRDop5pFW+Z1f56CDcOlEHaiPqHgnfOlqRmJvXl7g==", + "license": "MIT", + "dependencies": { + "@11ty/eleventy-fetch": "^5.1.0", + "@11ty/eleventy-utils": "^2.0.7", + "brotli-size": "^4.0.0", + "debug": "^4.4.0", + "entities": "^6.0.0", + "image-size": "^1.2.1", + "p-queue": "^6.6.2", + "sharp": "^0.33.5" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/11ty" + } + }, + "node_modules/@11ty/eleventy-navigation": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@11ty/eleventy-navigation/-/eleventy-navigation-1.0.4.tgz", + "integrity": "sha512-FgIfKiFP1e3plQdIvSRph1tFV44bbCXcYs+AWcwnsfrn5I6Gf0t3hBq9IrR4JdnZ06hlEKq8qRbnbb0nfEZg3A==", + "license": "MIT", + "dependencies": { + "dependency-graph": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/11ty" + } + }, + "node_modules/@11ty/eleventy-plugin-rss": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@11ty/eleventy-plugin-rss/-/eleventy-plugin-rss-2.0.4.tgz", + "integrity": "sha512-LF60sGVlxGTryQe3hTifuzrwF8R7XbrNsM2xfcDcNMSliLN4kmB+7zvoLRySRx0AQDjqhPTAeeeT0ra6/9zHUQ==", + "license": "MIT", + "dependencies": { + "@11ty/eleventy-utils": "^2.0.0", + "@11ty/posthtml-urls": "^1.0.1", + "debug": "^4.4.0", + "posthtml": "^0.16.6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/11ty" + } + }, + "node_modules/@11ty/eleventy-plugin-syntaxhighlight": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@11ty/eleventy-plugin-syntaxhighlight/-/eleventy-plugin-syntaxhighlight-5.0.1.tgz", + "integrity": "sha512-xDPF3Ay38XlmWZe9ER0SLtMmNah7olUBlGORhUiCUkPh3jYGVCDTDayi4tbFI9Dxha8NwKlfBZ2FXM/s3aZzAg==", + "license": "MIT", + "dependencies": { + "prismjs": "^1.30.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/11ty" + } + }, + "node_modules/@11ty/eleventy-utils": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@11ty/eleventy-utils/-/eleventy-utils-2.0.7.tgz", + "integrity": "sha512-6QE+duqSQ0GY9rENXYb4iPR4AYGdrFpqnmi59tFp9VrleOl0QSh8VlBr2yd6dlhkdtj7904poZW5PvGr9cMiJQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/11ty" + } + }, + "node_modules/@11ty/posthtml-urls": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@11ty/posthtml-urls/-/posthtml-urls-1.0.1.tgz", + "integrity": "sha512-6EFN/yYSxC/OzYXpq4gXDyDMlX/W+2MgCvvoxf11X1z76bqkqFJ8eep5RiBWfGT5j0323a1pwpelcJJdR46MCw==", + "license": "MIT", + "dependencies": { + "evaluate-value": "^2.0.0", + "http-equiv-refresh": "^2.0.1", + "list-to-array": "^1.1.0", + "parse-srcset": "^1.0.2" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.3.tgz", + "integrity": "sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", + "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", + "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", + "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", + "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", + "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", + "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", + "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", + "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", + "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", + "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", + "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", + "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", + "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", + "cpu": [ + "mips64el" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", + "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", + "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", + "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", + "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", + "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", + "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", + "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", + "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", + "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", + "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", + "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", + "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", + "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz", + "integrity": "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.0.4" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.5.tgz", + "integrity": "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.0.4" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.4.tgz", + "integrity": "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.4.tgz", + "integrity": "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.5.tgz", + "integrity": "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==", + "cpu": [ + "arm" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.4.tgz", + "integrity": "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.4.tgz", + "integrity": "sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==", + "cpu": [ + "s390x" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.4.tgz", + "integrity": "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.4.tgz", + "integrity": "sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==", + "cpu": [ + "arm64" + ], + "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.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.4.tgz", + "integrity": "sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.5.tgz", + "integrity": "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==", + "cpu": [ + "arm" + ], + "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-arm": "1.0.5" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.5.tgz", + "integrity": "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==", + "cpu": [ + "arm64" + ], + "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-arm64": "1.0.4" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.5.tgz", + "integrity": "sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==", + "cpu": [ + "s390x" + ], + "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-s390x": "1.0.4" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.5.tgz", + "integrity": "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==", + "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.0.4" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.5.tgz", + "integrity": "sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==", + "cpu": [ + "arm64" + ], + "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-arm64": "1.0.4" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.5.tgz", + "integrity": "sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==", + "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.0.4" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.33.5.tgz", + "integrity": "sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==", + "cpu": [ + "wasm32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.2.0" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.5.tgz", + "integrity": "sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==", + "cpu": [ + "ia32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.5.tgz", + "integrity": "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@puppeteer/browsers": { + "version": "2.10.7", + "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.10.7.tgz", + "integrity": "sha512-wHWLkQWBjHtajZeqCB74nsa/X70KheyOhySYBRmVQDJiNj0zjZR/naPCvdWjMhcG1LmjaMV/9WtTo5mpe8qWLw==", + "license": "Apache-2.0", + "dependencies": { + "debug": "^4.4.1", + "extract-zip": "^2.0.1", + "progress": "^2.0.3", + "proxy-agent": "^6.5.0", + "semver": "^7.7.2", + "tar-fs": "^3.1.0", + "yargs": "^17.7.2" + }, + "bin": { + "browsers": "lib/cjs/main-cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@rgrove/parse-xml": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@rgrove/parse-xml/-/parse-xml-4.2.0.tgz", + "integrity": "sha512-UuBOt7BOsKVOkFXRe4Ypd/lADuNIfqJXv8GvHqtXaTYXPPKkj2nS2zPllVsrtRjcomDhIJVBnZwfmlI222WH8g==", + "license": "ISC", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@tootallnate/quickjs-emscripten": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz", + "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==", + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "24.3.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.3.0.tgz", + "integrity": "sha512-aPTXCrfwnDLj4VvXrm+UUCQjNEvJgNA8s5F1cvwQU+3KNltTOkBm1j30uNLyqqPNe7gE3KFzImYoZEfLhp4Yow==", + "license": "MIT", + "optional": true, + "dependencies": { + "undici-types": "~7.10.0" + } + }, + "node_modules/@types/yauzl": { + "version": "2.10.3", + "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", + "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==", + "license": "MIT", + "optional": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "license": "Python-2.0" + }, + "node_modules/ast-types": { + "version": "0.13.4", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz", + "integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/b4a": { + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.7.tgz", + "integrity": "sha512-OnAYlL5b7LEkALw87fUVafQw5rVR9RjwGd4KUwNQ6DrrNmaVaUCgLipfVlzrPQ4tWOR9P0IXGNOx50jYCCdSJg==", + "license": "Apache-2.0" + }, + "node_modules/bare-events": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.6.1.tgz", + "integrity": "sha512-AuTJkq9XmE6Vk0FJVNq5QxETrSA/vKHarWVBG5l/JbdCL1prJemiyJqUS0jrlXO0MftuPq4m3YVYhoNc5+aE/g==", + "license": "Apache-2.0", + "optional": true + }, + "node_modules/bare-fs": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.2.1.tgz", + "integrity": "sha512-mELROzV0IhqilFgsl1gyp48pnZsaV9xhQapHLDsvn4d4ZTfbFhcghQezl7FTEDNBcGqLUnNI3lUlm6ecrLWdFA==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "bare-events": "^2.5.4", + "bare-path": "^3.0.0", + "bare-stream": "^2.6.4" + }, + "engines": { + "bare": ">=1.16.0" + }, + "peerDependencies": { + "bare-buffer": "*" + }, + "peerDependenciesMeta": { + "bare-buffer": { + "optional": true + } + } + }, + "node_modules/bare-os": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-3.6.2.tgz", + "integrity": "sha512-T+V1+1srU2qYNBmJCXZkUY5vQ0B4FSlL3QDROnKQYOqeiQR8UbjNHlPa+TIbM4cuidiN9GaTaOZgSEgsvPbh5A==", + "license": "Apache-2.0", + "optional": true, + "engines": { + "bare": ">=1.14.0" + } + }, + "node_modules/bare-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-3.0.0.tgz", + "integrity": "sha512-tyfW2cQcB5NN8Saijrhqn0Zh7AnFNsnczRcuWODH0eYAXBsJ5gVxAUuNr7tsHSC6IZ77cA0SitzT+s47kot8Mw==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "bare-os": "^3.0.1" + } + }, + "node_modules/bare-stream": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.7.0.tgz", + "integrity": "sha512-oyXQNicV1y8nc2aKffH+BUHFRXmx6VrPzlnaEvMhram0nPBrKcEdcyBg5r08D0i8VxngHFAiVyn1QKXpSG0B8A==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "streamx": "^2.21.0" + }, + "peerDependencies": { + "bare-buffer": "*", + "bare-events": "*" + }, + "peerDependenciesMeta": { + "bare-buffer": { + "optional": true + }, + "bare-events": { + "optional": true + } + } + }, + "node_modules/basic-ftp": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.5.tgz", + "integrity": "sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/brotli-size": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/brotli-size/-/brotli-size-4.0.0.tgz", + "integrity": "sha512-uA9fOtlTRC0iqKfzff1W34DXUA3GyVqbUaeo3Rw3d4gd1eavKVCETXrn3NzO74W+UVkG3UHu8WxUi+XvKI/huA==", + "license": "MIT", + "dependencies": { + "duplexer": "0.1.1" + }, + "engines": { + "node": ">= 10.16.0" + } + }, + "node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/chromium-bidi": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-8.0.0.tgz", + "integrity": "sha512-d1VmE0FD7lxZQHzcDUCKZSNRtRwISXDsdg4HjdTR5+Ll5nQ/vzU12JeNmupD6VWffrPSlrnGhEWlLESKH3VO+g==", + "license": "Apache-2.0", + "dependencies": { + "mitt": "^3.0.1", + "zod": "^3.24.1" + }, + "peerDependencies": { + "devtools-protocol": "*" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/color": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", + "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1", + "color-string": "^1.9.0" + }, + "engines": { + "node": ">=12.5.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "license": "MIT", + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "node_modules/cosmiconfig": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz", + "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==", + "license": "MIT", + "dependencies": { + "env-paths": "^2.2.1", + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/data-uri-to-buffer": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz", + "integrity": "sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==", + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/degenerator": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-5.0.1.tgz", + "integrity": "sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==", + "license": "MIT", + "dependencies": { + "ast-types": "^0.13.4", + "escodegen": "^2.1.0", + "esprima": "^4.0.1" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/dependency-graph": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/dependency-graph/-/dependency-graph-1.0.0.tgz", + "integrity": "sha512-cW3gggJ28HZ/LExwxP2B++aiKxhJXMSIt9K48FOXQkm+vuG5gyatXnLsONRJdzO/7VfjDIiaOOa/bs4l464Lwg==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/detect-libc": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", + "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/devtools-protocol": { + "version": "0.0.1475386", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1475386.tgz", + "integrity": "sha512-RQ809ykTfJ+dgj9bftdeL2vRVxASAuGU+I9LEx9Ij5TXU5HrgAQVmzi72VA+mkzscE12uzlRv5/tWWv9R9J1SA==", + "license": "BSD-3-Clause" + }, + "node_modules/dom-serializer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", + "license": "MIT", + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/dom-serializer/node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "license": "BSD-2-Clause", + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "BSD-2-Clause" + }, + "node_modules/domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.2.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "license": "BSD-2-Clause", + "dependencies": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/duplexer": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", + "integrity": "sha512-sxNZ+ljy+RA1maXoUReeqBBpBC6RLKmg5ewzV+x+mSETmWNoKdZN6vcQjpFROemza23hGFskJtFNoUWUaQ+R4Q==" + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/end-of-stream": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/error-ex/node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "license": "MIT" + }, + "node_modules/esbuild": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", + "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.12", + "@esbuild/android-arm": "0.25.12", + "@esbuild/android-arm64": "0.25.12", + "@esbuild/android-x64": "0.25.12", + "@esbuild/darwin-arm64": "0.25.12", + "@esbuild/darwin-x64": "0.25.12", + "@esbuild/freebsd-arm64": "0.25.12", + "@esbuild/freebsd-x64": "0.25.12", + "@esbuild/linux-arm": "0.25.12", + "@esbuild/linux-arm64": "0.25.12", + "@esbuild/linux-ia32": "0.25.12", + "@esbuild/linux-loong64": "0.25.12", + "@esbuild/linux-mips64el": "0.25.12", + "@esbuild/linux-ppc64": "0.25.12", + "@esbuild/linux-riscv64": "0.25.12", + "@esbuild/linux-s390x": "0.25.12", + "@esbuild/linux-x64": "0.25.12", + "@esbuild/netbsd-arm64": "0.25.12", + "@esbuild/netbsd-x64": "0.25.12", + "@esbuild/openbsd-arm64": "0.25.12", + "@esbuild/openbsd-x64": "0.25.12", + "@esbuild/openharmony-arm64": "0.25.12", + "@esbuild/sunos-x64": "0.25.12", + "@esbuild/win32-arm64": "0.25.12", + "@esbuild/win32-ia32": "0.25.12", + "@esbuild/win32-x64": "0.25.12" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escodegen": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", + "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", + "license": "BSD-2-Clause", + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=6.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/evaluate-value": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/evaluate-value/-/evaluate-value-2.0.0.tgz", + "integrity": "sha512-VonfiuDJc0z4sOO7W0Pd130VLsXN6vmBWZlrog1mCb/o7o/Nl5Lr25+Kj/nkCCAhG+zqeeGjxhkK9oHpkgTHhQ==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "license": "MIT" + }, + "node_modules/extract-zip": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", + "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", + "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/fast-fifo": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", + "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", + "license": "MIT" + }, + "node_modules/fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", + "license": "MIT", + "dependencies": { + "pend": "~1.2.0" + } + }, + "node_modules/flatted": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "license": "ISC" + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "license": "MIT", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-uri": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.5.tgz", + "integrity": "sha512-b1O07XYq8eRuVzBNgJLstU6FYc1tS6wnMtF1I1D9lE8LxZSOGZ7LhxN54yPP6mGw5f2CkXY2BQUL9Fx41qvcIg==", + "license": "MIT", + "dependencies": { + "basic-ftp": "^5.0.2", + "data-uri-to-buffer": "^6.0.2", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/htmlparser2": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-7.2.0.tgz", + "integrity": "sha512-H7MImA4MS6cw7nbyURtLPO1Tms7C5H602LRETv95z1MxO/7CP7rDVROehUYeYBUYEON94NXXDEPmZuq+hX4sog==", + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "MIT", + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.2", + "domutils": "^2.8.0", + "entities": "^3.0.1" + } + }, + "node_modules/htmlparser2/node_modules/entities": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz", + "integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/http-equiv-refresh": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-equiv-refresh/-/http-equiv-refresh-2.0.1.tgz", + "integrity": "sha512-XJpDL/MLkV3dKwLzHwr2dY05dYNfBNlyPu4STQ8WvKCFdc6vC5tPXuq28of663+gHVg03C+16pHHs/+FmmDjcw==", + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/image-size": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-1.2.1.tgz", + "integrity": "sha512-rH+46sQJ2dlwfjfhCyNx5thzrv+dtmBIhPHk0zgRUukHzZ/kRueTJXoYYsclBaKcSMBWuGbOFXtioLpzTb5euw==", + "license": "MIT", + "dependencies": { + "queue": "6.0.2" + }, + "bin": { + "image-size": "bin/image-size.js" + }, + "engines": { + "node": ">=16.x" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ip-address": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.0.1.tgz", + "integrity": "sha512-NWv9YLW4PoW2B7xtzaS3NCot75m6nK7Icdv0o3lfMceJVRfSoQwqD4wEH5rLwoKJwUiZ/rfpiVBhnaF0FK4HoA==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", + "license": "MIT" + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-json": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-json/-/is-json-2.0.1.tgz", + "integrity": "sha512-6BEnpVn1rcf3ngfmViLM6vjUjGErbdrL4rwlv+u1NO1XO8kqT4YGL8+19Q+Z/bas8tY90BTWMk2+fW1g6hQjbA==", + "license": "ISC" + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "license": "MIT" + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "license": "MIT" + }, + "node_modules/linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", + "license": "MIT", + "dependencies": { + "uc.micro": "^2.0.0" + } + }, + "node_modules/list-to-array": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/list-to-array/-/list-to-array-1.1.0.tgz", + "integrity": "sha512-+dAZZ2mM+/m+vY9ezfoueVvrgnHIGi5FvgSymbIgJOFwiznWyA59mav95L+Mc6xPtL3s9gm5eNTlNtxJLbNM1g==", + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/markdown-it": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz", + "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==", + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1", + "entities": "^4.4.0", + "linkify-it": "^5.0.0", + "mdurl": "^2.0.0", + "punycode.js": "^2.3.1", + "uc.micro": "^2.1.0" + }, + "bin": { + "markdown-it": "bin/markdown-it.mjs" + } + }, + "node_modules/markdown-it/node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==", + "license": "MIT" + }, + "node_modules/mitt": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", + "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", + "license": "MIT" + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/netmask": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz", + "integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==", + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/p-queue": { + "version": "6.6.2", + "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-6.6.2.tgz", + "integrity": "sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==", + "license": "MIT", + "dependencies": { + "eventemitter3": "^4.0.4", + "p-timeout": "^3.2.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-timeout": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz", + "integrity": "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==", + "license": "MIT", + "dependencies": { + "p-finally": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pac-proxy-agent": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.2.0.tgz", + "integrity": "sha512-TEB8ESquiLMc0lV8vcd5Ql/JAKAoyzHFXaStwjkzpOpC5Yv+pIzLfHvjTSdf3vpa2bMiUQrg9i6276yn8666aA==", + "license": "MIT", + "dependencies": { + "@tootallnate/quickjs-emscripten": "^0.23.0", + "agent-base": "^7.1.2", + "debug": "^4.3.4", + "get-uri": "^6.0.1", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.6", + "pac-resolver": "^7.0.1", + "socks-proxy-agent": "^8.0.5" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/pac-resolver": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-7.0.1.tgz", + "integrity": "sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==", + "license": "MIT", + "dependencies": { + "degenerator": "^5.0.0", + "netmask": "^2.0.2" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse-srcset": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/parse-srcset/-/parse-srcset-1.0.2.tgz", + "integrity": "sha512-/2qh0lav6CmI15FzA3i/2Bzk2zCgQhGMkvhOhKNcBVQ1ldgpbfiNTVslmooUmWJcADi1f1kIeynbDRVzNlfR6Q==", + "license": "MIT" + }, + "node_modules/pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/posthtml": { + "version": "0.16.6", + "resolved": "https://registry.npmjs.org/posthtml/-/posthtml-0.16.6.tgz", + "integrity": "sha512-JcEmHlyLK/o0uGAlj65vgg+7LIms0xKXe60lcDOTU7oVX/3LuEuLwrQpW3VJ7de5TaFKiW4kWkaIpJL42FEgxQ==", + "license": "MIT", + "dependencies": { + "posthtml-parser": "^0.11.0", + "posthtml-render": "^3.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/posthtml-parser": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/posthtml-parser/-/posthtml-parser-0.11.0.tgz", + "integrity": "sha512-QecJtfLekJbWVo/dMAA+OSwY79wpRmbqS5TeXvXSX+f0c6pW4/SE6inzZ2qkU7oAMCPqIDkZDvd/bQsSFUnKyw==", + "license": "MIT", + "dependencies": { + "htmlparser2": "^7.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/posthtml-render": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/posthtml-render/-/posthtml-render-3.0.0.tgz", + "integrity": "sha512-z+16RoxK3fUPgwaIgH9NGnK1HKY9XIDpydky5eQGgAFVXTCSezalv9U2jQuNV+Z9qV1fDWNzldcw4eK0SSbqKA==", + "license": "MIT", + "dependencies": { + "is-json": "^2.0.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/prismjs": { + "version": "1.30.0", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz", + "integrity": "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/proxy-agent": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.5.0.tgz", + "integrity": "sha512-TmatMXdr2KlRiA2CyDu8GqR8EjahTG3aY3nXjdzFyoZbmB8hrBsTyMezhULIXKnC0jpfjlmiZ3+EaCzoInSu/A==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "^4.3.4", + "http-proxy-agent": "^7.0.1", + "https-proxy-agent": "^7.0.6", + "lru-cache": "^7.14.1", + "pac-proxy-agent": "^7.1.0", + "proxy-from-env": "^1.1.0", + "socks-proxy-agent": "^8.0.5" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + }, + "node_modules/pump": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz", + "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==", + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/punycode.js": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", + "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/puppeteer": { + "version": "24.17.0", + "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-24.17.0.tgz", + "integrity": "sha512-CGrmJ8WgilK3nyE73k+pbxHggETPpEvL6AQ9H5JSK1RgZRGMQVJ+iO3MocGm9yBQXQJ9U5xijyLvkYXFeb0/+g==", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@puppeteer/browsers": "2.10.7", + "chromium-bidi": "8.0.0", + "cosmiconfig": "^9.0.0", + "devtools-protocol": "0.0.1475386", + "puppeteer-core": "24.17.0", + "typed-query-selector": "^2.12.0" + }, + "bin": { + "puppeteer": "lib/cjs/puppeteer/node/cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/puppeteer-core": { + "version": "24.17.0", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-24.17.0.tgz", + "integrity": "sha512-RYOBKFiF+3RdwIZTEacqNpD567gaFcBAOKTT7742FdB1icXudrPI7BlZbYTYWK2wgGQUXt9Zi1Yn+D5PmCs4CA==", + "license": "Apache-2.0", + "dependencies": { + "@puppeteer/browsers": "2.10.7", + "chromium-bidi": "8.0.0", + "debug": "^4.4.1", + "devtools-protocol": "0.0.1475386", + "typed-query-selector": "^2.12.0", + "ws": "^8.18.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/queue": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/queue/-/queue-6.0.2.tgz", + "integrity": "sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA==", + "license": "MIT", + "dependencies": { + "inherits": "~2.0.3" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/sharp": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.33.5.tgz", + "integrity": "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "color": "^4.2.3", + "detect-libc": "^2.0.3", + "semver": "^7.6.3" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.33.5", + "@img/sharp-darwin-x64": "0.33.5", + "@img/sharp-libvips-darwin-arm64": "1.0.4", + "@img/sharp-libvips-darwin-x64": "1.0.4", + "@img/sharp-libvips-linux-arm": "1.0.5", + "@img/sharp-libvips-linux-arm64": "1.0.4", + "@img/sharp-libvips-linux-s390x": "1.0.4", + "@img/sharp-libvips-linux-x64": "1.0.4", + "@img/sharp-libvips-linuxmusl-arm64": "1.0.4", + "@img/sharp-libvips-linuxmusl-x64": "1.0.4", + "@img/sharp-linux-arm": "0.33.5", + "@img/sharp-linux-arm64": "0.33.5", + "@img/sharp-linux-s390x": "0.33.5", + "@img/sharp-linux-x64": "0.33.5", + "@img/sharp-linuxmusl-arm64": "0.33.5", + "@img/sharp-linuxmusl-x64": "0.33.5", + "@img/sharp-wasm32": "0.33.5", + "@img/sharp-win32-ia32": "0.33.5", + "@img/sharp-win32-x64": "0.33.5" + } + }, + "node_modules/simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "license": "MIT", + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks": { + "version": "2.8.7", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.7.tgz", + "integrity": "sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==", + "license": "MIT", + "dependencies": { + "ip-address": "^10.0.1", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks-proxy-agent": { + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz", + "integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "^4.3.4", + "socks": "^2.8.3" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "license": "BSD-3-Clause", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stacked-alpha-video": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/stacked-alpha-video/-/stacked-alpha-video-1.0.10.tgz", + "integrity": "sha512-FdBFKJu/+kinUMTkpELYwvx2ARXDi9GOfvN7KEMajIiNmkBJzEK2h37QSTTPzee1QXBiYssoDW7NdfTxHXYygA==", + "license": "Apache-2.0" + }, + "node_modules/streamx": { + "version": "2.22.1", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.22.1.tgz", + "integrity": "sha512-znKXEBxfatz2GBNK02kRnCXjV+AA4kjZIUxeWSr3UGirZMJfTE9uiwKHobnbgxWyL/JWro8tTq+vOqAK1/qbSA==", + "license": "MIT", + "dependencies": { + "fast-fifo": "^1.3.2", + "text-decoder": "^1.1.0" + }, + "optionalDependencies": { + "bare-events": "^2.2.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tar-fs": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.1.1.tgz", + "integrity": "sha512-LZA0oaPOc2fVo82Txf3gw+AkEd38szODlptMYejQUhndHMLQ9M059uXR+AfS7DNo0NpINvSqDsvyaCrBVkptWg==", + "license": "MIT", + "dependencies": { + "pump": "^3.0.0", + "tar-stream": "^3.1.5" + }, + "optionalDependencies": { + "bare-fs": "^4.0.1", + "bare-path": "^3.0.0" + } + }, + "node_modules/tar-stream": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz", + "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==", + "license": "MIT", + "dependencies": { + "b4a": "^1.6.4", + "fast-fifo": "^1.2.0", + "streamx": "^2.15.0" + } + }, + "node_modules/text-decoder": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.3.tgz", + "integrity": "sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA==", + "license": "Apache-2.0", + "dependencies": { + "b4a": "^1.6.4" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/typed-query-selector": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/typed-query-selector/-/typed-query-selector-2.12.0.tgz", + "integrity": "sha512-SbklCd1F0EiZOyPiW192rrHZzZ5sBijB6xM+cpmrwDqObvdtunOHHIk9fCGsoK5JVIYXoyEp4iEdE3upFH3PAg==", + "license": "MIT" + }, + "node_modules/uc.micro": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", + "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", + "license": "MIT" + }, + "node_modules/undici-types": { + "version": "7.10.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.10.0.tgz", + "integrity": "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==", + "license": "MIT", + "optional": true + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, + "node_modules/ws": { + "version": "8.18.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", + "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", + "license": "MIT", + "dependencies": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + }, + "node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..4c5bfc5 --- /dev/null +++ b/package.json @@ -0,0 +1,37 @@ +{ + "name": "blog", + "version": "1.0.0", + "description": "A personal blog built with 11ty", + "main": "index.js", + "type": "module", + "scripts": { + "add-domain": "echo \"drewedgar.com\" > dist/CNAME", + "build:client": "node scripts/build-client.js", + "build": "npm run build:client && npx @11ty/eleventy && npm run add-domain", + "start": "npx @11ty/eleventy --serve", + "watch": "npx @11ty/eleventy --watch" + }, + "repository": { + "type": "git", + "url": "git://github.com/drew-edgar/drew-edgar.github.io.git" + }, + "author": { + "name": "Drew Edgar", + "email": "drewpedgar@gmail.com", + "url": "https://drewedgar.com" + }, + "license": "ISC", + "dependencies": { + "@11ty/eleventy-img": "^6.0.4", + "@11ty/eleventy-navigation": "^1.0.4", + "@11ty/eleventy-plugin-rss": "^2.0.4", + "@11ty/eleventy-plugin-syntaxhighlight": "^5.0.1", + "esbuild": "^0.25.12", + "markdown-it": "^14.1.0", + "puppeteer": "^24.17.0", + "stacked-alpha-video": "^1.0.10" + }, + "devdependencies": { + "@11ty/eleventy": "^3.1.1" + } +} diff --git a/scripts/build-client.js b/scripts/build-client.js new file mode 100644 index 0000000..ec6b433 --- /dev/null +++ b/scripts/build-client.js @@ -0,0 +1,10 @@ +import esbuild from "esbuild"; + +await esbuild.build({ + entryPoints: ["src/client/video-with-transparency/index.ts"], + bundle: true, + format: "esm", + outfile: "dist/assets/video-with-transparency.js", + sourcemap: true, + minify: true, +}); diff --git a/src/_data/cv.json b/src/_data/cv.json new file mode 100644 index 0000000..c3b53d5 --- /dev/null +++ b/src/_data/cv.json @@ -0,0 +1,232 @@ +{ + "basics": { + "name": "Drew Edgar", + "label": "AI and XR Researcher", + "image": "pfp-small.webp", + "email": "drewpedgar@gmail.com", + "url": "https://drewedgar.com", + "summary": "I'm a highly-accomplished early-career human-computer interaction (HCI) professional, who brings a perspective combining technical expertise and a deep commitment to user-centred design and societal impact. Through my background in HCI research, I have developed significant proficiency and knowledge in the fields of Extended Reality (XR) and Artificial Intelligence (AI). My passion lies in leveraging rigorous research to inform policy and design decisions and contribute to the responsible evolution and use of technology. I am currently seeking opportunities to apply my skills in a research or development role, where I can continue to grow and make a meaningful impact on the world.", + "location": { + "countryCode": "UK" + }, + "profiles": [ + { + "network": "LinkedIn", + "username": "Drew Edgar", + "url": "https://www.linkedin.com/in/drew-edgar/" + }, + { + "network": "Google Scholar", + "username": "Drew Edgar", + "url": "https://scholar.google.com/citations?hl=en&user=LjtyGmgAAAAJ" + }, + { + "network": "BlueSky", + "username": "@drewedgar.com", + "url": "https://bsky.app/profile/drewedgar.com" + } + ] + }, + "work": [ + { + "name": "University of Glasgow", + "position": "HCI Research Assistant", + "url": "https://www.gla.ac.uk", + "startDate": "2025-06", + "endDate": "2025-08", + "period": "Summer 2025", + "summary": "Description…", + "highlights": ["Worked on the DAARC project.", "Designed and created instructional materials for an undergraduate course on XR development."], + "supervisors": [ + { + "name": "Dr Thomas Goodge", + "url": "https://scholar.google.com/citations?user=mzytrugAAAAJ" + }, + { + "name": "Dr Mark McGill", + "url": "https://scholar.google.com/citations?user=N0NjK_AAAAAJ" + } + ] + }, + { + "name": "University of Glasgow", + "position": "Computer Science Lab Demonstrator", + "url": "https://www.gla.ac.uk", + "startDate": "2024", + "endDate": "2025", + "summary": "Description…", + "highlights": ["Led lab sessions on the fundamentals of Human-Computer Interaction, emphasising student participation and discussion.", + "Facilitated sessions on LLM development and design, utilising Pytorch to implement word embeddings, with the goal of enhancing students' understanding of machine learning concepts."] + }, + { + "name": "University of Glasgow", + "position": "HCI Research Assistant", + "url": "https://www.gla.ac.uk", + "startDate": "2024-06", + "endDate": "2024-08", + "period": "Summer 2024", + "summary": "Description…", + "highlights": ["Worked on the XR notification and VR exit transitions projects."], + "supervisors": [ + { + "name": "Dr Joseph O'Hagan", + "url": "https://scholar.google.com/citations?user=74ue18MAAAAJ" + } + ] + } + ], + "education": [ + { + "institution": "University of Glasgow", + "url": "https://www.gla.ac.uk", + "area": "Computer Science", + "specialisation": "Human-Computer Interaction", + "studyType": "Bachelor of Science", + "startDate": "2021", + "endDate": "2025", + "award": "First Class Honours", + "score": "4.0", + "courses": [""] + } + ], + "publications": [ + { + "name": "Evaluating Dynamic Delivery of Audio+Visual Message Notifications for Spatial User Interfaces", + "publisher": "IEEE VR 2025: IEEE Conference on Virtual Reality and 3D User Interfaces", + "publisherNote": "A* Conference", + "releaseDate": "2025-03-28", + "url": "https://doi.org/10.1109/VR59515.2025.00052", + "summary": "Description…", + "authors": [ + "Hyunsung Cho", + "Drew Edgar", + "David Lindlbauer", + "Joseph O’Hagan" + ] + }, + { + "name": "Examining the use of Augmented Reality + Aritifical Intelligence for crime through Co-Design Workshops", + "status": "In Preparation", + "summary": "Description…", + "authors": [ + "Thomas Goodge", + "Drew Edgar" + ] + }, + { + "name": "Evaluating An Environmentally Aware Adaptive Music System for Pedestrians", + "status": "In Preparation", + "summary": "Description…", + "authors": [ + "Drew Edgar", + "Jake Bhattacharyya", + "Mark McGill" + ] + } + ], + "researchProjects": [ + { + "name": "Examining the Criminal and Harmful Implications of Everyday Augmented Reality + Artifical Intelligence", + "url": "", + "description": "Description…", + "highlights": [ + "As part of the REPHRAIN DAARC (Defence Against Augmented Reality Crimes) project.", + "Contributed to a systematic literature review on criminal and harmful implications of AR.", + "Conducted focus groups and interviews to gather insights on potential AR+AI crime scenarios.", + "Developed a demo application (Unity + Meta Llama 3) to illustrate the potential for criminal misuse of everyday AR technology enabled by the capabilities of frontier multimodal LLMs." + ], + "supervisors": [ + { + "name": "Dr Thomas Goodge", + "url": "https://scholar.google.com/citations?user=mzytrugAAAAJ" + } + ] + }, + { + "name": "Designing and Evaluating An Environmentally Aware Adaptive Music System for Pedestrians", + "url": "", + "description": "Description…", + "highlights": [ + "An independent dissertation research project where I designed and evaluated an Audio Augmented Reality (AAR) system for pedestrians that altered the instrumentation of music based on various environmental changes.", + "Conducted a literature review, designed and implemented the user study procedure and companion app, and analysed the qualitative and quantitative data." + ], + "supervisors": [ + { + "name": "Dr Mark McGill", + "url": "https://scholar.google.com/citations?user=N0NjK_AAAAAJ" + } + ] + }, + { + "name": "Multimodal AI Safety Research & Evaluation", + "description": "Description…", + "highlights": [ + "Developed Python pipelines for preprocessing multimodal datasets, structued chat prompts, and performed data quality filtering.", + "Conducted a systematic safety evaluation of Meta's Llama 3 model using industry-standard benchmarks and toolkits, with a focus on hallucination, harmful output, and human interaction risks. Applied mixed-effects and other statistical modelling techniques to assess model performance.", + "Implemented interactive simulations in Python, captured model responses, computed safety metrics, and visualised the results with pandas/matplotlib, resulting in a comprehensive evaluation report summarising accuracy, safety vulnerabilities, and mitigation strategies for real-world applications.", + "Facilitated discussions on responsible design and safety methods for AI systems, particularly in regards to user experience and potential harms." + ] + }, + { + "name": "Examining Different Notification Methods in XR", + "url": "https://ieeexplore.ieee.org/document/10937443", + "description": "Description…", + "highlights": [ + "Collaborated with researchers from Carnegie Mellon University on a study exploring user attitudes towards different audio-visual notification styles in Extended Reality (XR).", + "Developed study applications in Unity for Meta Quest 3, designed survey and interview protocols, recruited participants, and conducted the user study.", + "The resulting paper was accepted at IEEE VR 2025." + ], + "supervisors": [ + { + "name": "Dr Joseph O'Hagan", + "url": "https://scholar.google.com/citations?user=74ue18MAAAAJ" + } + ] + }, + { + "name": "Exploring Exit Transitions in VR", + "description": "Description…", + "highlights": [ + "Investigated the different ways a user could be transitioned out of a VR environment, and the effects of these transitions on user experience." + ], + "supervisors": [ + { + "name": "Dr Joseph O'Hagan", + "url": "https://scholar.google.com/citations?user=74ue18MAAAAJ" + } + ] + } + ], + "skills": [ + { + "name": "Research", + "keywords": ["Science Communication", "Systematic Literature Reviews", "Qualitative Analysis Methods", "User Study Design and Implementation", "Workshops and Focus Groups", "Quantitative Methods", "Statistical and Probabilistic Modeling"] + }, + { + "name": "Artificial Intelligence (LLMs)", + "keywords": ["Large Language Models", "Multimodal LLMs", "Fine-tuning", "AI Safety", "Pytorch", "Hugging Face Transformers", "Meta Llama Models", "Multimodal Benchmark Evaluation", "Dataset Preprocessing and Curation", "OpenCV", "Python (pandas, matplotlib, NumPy)"] + }, + { + "name": "XR Development", + "keywords": ["Unity", "XR Design", "Meta Quest", "Meta SDK", "Meta Project Aria"] + }, + { + "name": "Web Development", + "keywords": ["HTML", "CSS", "JavaScript", "React", "Static Sites", "Single Page Apps", "Figma Prototyping", "UI Design"] + } + ], + "references": [ + { + "name": "Mark McGill", + "reference": "Reference…" + }, + { + "name": "Thomas Goodge", + "reference": "Reference…" + }, + { + "name": "Joseph O'Hagan", + "reference": "Reference…" + } + ] +} diff --git a/src/_includes/components/cv/address.liquid b/src/_includes/components/cv/address.liquid new file mode 100644 index 0000000..c4adb3b --- /dev/null +++ b/src/_includes/components/cv/address.liquid @@ -0,0 +1,19 @@ + \ No newline at end of file diff --git a/src/_includes/components/cv/cv-header.liquid b/src/_includes/components/cv/cv-header.liquid new file mode 100644 index 0000000..8fa0dd9 --- /dev/null +++ b/src/_includes/components/cv/cv-header.liquid @@ -0,0 +1,12 @@ +
+ {{ basics.name }} +

+ {{ basics.name }} +
+ {{ basics.label }} +

+ {% + include "components/cv/address.liquid", + basics: basics + %} +
\ No newline at end of file diff --git a/src/_includes/components/cv/education.liquid b/src/_includes/components/cv/education.liquid new file mode 100644 index 0000000..ca1741d --- /dev/null +++ b/src/_includes/components/cv/education.liquid @@ -0,0 +1,31 @@ +
+

Education

+ {%- for education in cv.education %} +
+
+

+ {{ education.studyType }} in {{ education.area }} +

+ + {{ education.institution }} + {%- if education.specialisation -%} + + Specialisation: {{ education.specialisation }} + + {%- endif %} + + + {%- if education.award -%} + + {{ education.award }} + + {%- endif %} + + + {{ education.startDate }} – {{ education.endDate }} + +
+
+ + {%- endfor %} +
diff --git a/src/_includes/components/cv/experience.liquid b/src/_includes/components/cv/experience.liquid new file mode 100644 index 0000000..79b7f5e --- /dev/null +++ b/src/_includes/components/cv/experience.liquid @@ -0,0 +1,25 @@ +
+

Research

+ {%- for project in cv.researchProjects %} +
+
+

{{ project.name }}

+ {%- if project.supervisors and project.supervisors.size > 0 -%} + + {% if project.supervisors.size > 1 %}Supervisors{% else %}Supervisor{% endif %}: + {% for sup in project.supervisors -%} + {{ sup.name }}{% unless forloop.last %}, {% endunless %} + {%- endfor -%} + + {%- endif %} +
+ +
+ {%- endfor %} +
\ No newline at end of file diff --git a/src/_includes/components/cv/publications.liquid b/src/_includes/components/cv/publications.liquid new file mode 100644 index 0000000..5bf3ae1 --- /dev/null +++ b/src/_includes/components/cv/publications.liquid @@ -0,0 +1,49 @@ +
+

Publications

+ {%- for publication in cv.publications %} +
+
+

+ {{ publication.name }} + {%- if publication.url %} • + PDF + +          {%- endif -%} +

+ + {% for author in publication.authors %} + {% if author == "Drew Edgar" %} + {{ author }}{% unless forloop.last %}, {% endunless %} + {% else %} + {{ author }}{% unless forloop.last %}, {% endunless %} + {% endif %} + {% endfor %} + + + + {%- if publication.publisher -%} + {{ publication.publisher }} + {%- endif %} + {%- if publication.publisherNote -%} + + ({{ publication.publisherNote }}) + + {%- endif %} + + {%- if publication.noteLink %} • + + Link + +          {%- endif -%} + + + + {%- if publication.status -%} + {{ publication.status }} + {%- endif %} + +
+
+ + {%- endfor %} +
diff --git a/src/_includes/components/cv/references.liquid b/src/_includes/components/cv/references.liquid new file mode 100644 index 0000000..4f51b33 --- /dev/null +++ b/src/_includes/components/cv/references.liquid @@ -0,0 +1,10 @@ +
+

References

+
+
+ + Available on request. + +
+
+
diff --git a/src/_includes/components/cv/skills.liquid b/src/_includes/components/cv/skills.liquid new file mode 100644 index 0000000..cd73f47 --- /dev/null +++ b/src/_includes/components/cv/skills.liquid @@ -0,0 +1,19 @@ +
+

Skills

+ {%- for skillset in skills %} +
+
+

+ {{ skillset.name }} +

+ + {% for skill in skillset.keywords %} + {{ skill }}{% unless forloop.last %}, {% endunless %} + {% endfor %} + + +
+
+ + {%- endfor %} +
diff --git a/src/_includes/components/cv/summary.liquid b/src/_includes/components/cv/summary.liquid new file mode 100644 index 0000000..8a47a11 --- /dev/null +++ b/src/_includes/components/cv/summary.liquid @@ -0,0 +1,4 @@ +
+

About me

+

{{ summary }}

+
\ No newline at end of file diff --git a/src/_includes/components/cv/work.liquid b/src/_includes/components/cv/work.liquid new file mode 100644 index 0000000..38eef45 --- /dev/null +++ b/src/_includes/components/cv/work.liquid @@ -0,0 +1,27 @@ +
+

Work

+ {%- for work in cv.work %} +
+
+

{{ work.position }}

+ + {{ work.name }} + + + {%- if work.period -%} + {{ work.period }} + {%- else -%} + {{ work.startDate }} – {{ work.endDate }} + {%- endif %} + +
+ +
+ {%- endfor %} +
\ No newline at end of file diff --git a/src/_includes/components/footer.liquid b/src/_includes/components/footer.liquid new file mode 100644 index 0000000..8c15f21 --- /dev/null +++ b/src/_includes/components/footer.liquid @@ -0,0 +1,16 @@ + \ No newline at end of file diff --git a/src/_includes/components/nav-header.liquid b/src/_includes/components/nav-header.liquid new file mode 100644 index 0000000..3ba3119 --- /dev/null +++ b/src/_includes/components/nav-header.liquid @@ -0,0 +1,76 @@ + +
+
+ + + +
+ + + + + + + + + +
+ + + +

Drew Edgar

+
+
+ +
+ + diff --git a/src/_includes/cv_styles.css b/src/_includes/cv_styles.css new file mode 100644 index 0000000..4df5b11 --- /dev/null +++ b/src/_includes/cv_styles.css @@ -0,0 +1,261 @@ +/* CV */ + +body .cv-header { + display: flex; + align-items: center; + padding: 0.5rem; + flex-direction: row; + flex-wrap: wrap; + gap: 1rem; +} + +body .cv-header h1 { + font-size: 2rem; + font-weight: bolder; + flex: 1 0 auto; +} + +body .cv-header h1 span { + font-size: 1.1rem; + font-weight: 300; +} + +body .cv-header address { + flex: 1 0 auto; + align-self: end; + text-align: end; + font-style: normal; + font-size: 0.9rem; + line-height: 1.6; +} + +body .cv-header address a { + display: block; + white-space: pre; +} + +body .cv-header address a::after { + margin-inline-start: 0.5rem; + display: inline-block; + position: relative; + right: 0.5pt; + background-size: 1rem 1rem; + width: 1rem; + height: 1rem; + bottom: -2pt; + right: 2pt; +} + +body .cv-header address a[href*="drewedgar.com"]::after { + content: '🌐'; +} + +body .cv-header address a[href^="mailto"]::after { + content: '📧'; +} + +body .cv-header address a[href^="tel"]::after { + content: '📞'; +} + +body .cv-header address a[href^="https://www.linkedin.com"]::after { + content: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 72 72'%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Cpath d='M8,72 L64,72 C68.418278,72 72,68.418278 72,64 L72,8 C72,3.581722 68.418278,-8.11624501e-16 64,0 L8,0 C3.581722,8.11624501e-16 -5.41083001e-16,3.581722 0,8 L0,64 C5.41083001e-16,68.418278 3.581722,72 8,72 Z' fill='%23007EBB'/%3E%3Cpath d='M62,62 L51.315625,62 L51.315625,43.8021149 C51.315625,38.8127542 49.4197917,36.0245323 45.4707031,36.0245323 C41.1746094,36.0245323 38.9300781,38.9261103 38.9300781,43.8021149 L38.9300781,62 L28.6333333,62 L28.6333333,27.3333333 L38.9300781,27.3333333 L38.9300781,32.0029283 C38.9300781,32.0029283 42.0260417,26.2742151 49.3825521,26.2742151 C56.7356771,26.2742151 62,30.7644705 62,40.051212 L62,62 Z M16.349349,22.7940133 C12.8420573,22.7940133 10,19.9296567 10,16.3970067 C10,12.8643566 12.8420573,10 16.349349,10 C19.8566406,10 22.6970052,12.8643566 22.6970052,16.3970067 C22.6970052,19.9296567 19.8566406,22.7940133 16.349349,22.7940133 Z M11.0325521,62 L21.769401,62 L21.769401,27.3333333 L11.0325521,27.3333333 L11.0325521,62 Z' fill='%23FFF'/%3E%3C/g%3E%3C/svg%3E"); +} + +body .cv-header address a[href^="https://bsky.app/"]::after { + content: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 360 320'%3E%3Cpath fill='%2318a2f7' d='M180 142c-16.3-31.7-60.7-90.8-102-120C38.5-5.9 23.4-1 13.5 3.4 2.1 8.6 0 26.2 0 36.5c0 10.4 5.7 84.8 9.4 97.2 12.2 41 55.7 55 95.7 50.5-58.7 8.6-110.8 30-42.4 106.1 75.1 77.9 103-16.7 117.3-64.6 14.3 48 30.8 139 116 64.6 64-64.6 17.6-97.5-41.1-106.1 40 4.4 83.5-9.5 95.7-50.5 3.7-12.4 9.4-86.8 9.4-97.2 0-10.3-2-27.9-13.5-33C336.5-1 321.5-6 282 22c-41.3 29.2-85.7 88.3-102 120Z'/%3E%3C/svg%3E"); +} + +body .cv-header address a[href^="https://scholar.google.com"]::after { + content: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Ctitle%3EGoogle Scholar icon%3C/title%3E%3Cpath d='M12 24a7 7 0 1 1 0-14 7 7 0 0 1 0 14zm0-24L0 9.5l4.838 3.94A8 8 0 0 1 12 9a8 8 0 0 1 7.162 4.44L24 9.5z' fill='%23007BFF'/%3E%3C/svg%3E"); +} + +/* CV content */ +body .cv-content { + display: grid; + grid-template-columns: max-content 1fr; + row-gap: 0.75rem; +} + +body .cv-content > section { + grid-column: 1 / 3; + width: 100%; + display: grid; + grid-template-columns: subgrid; + justify-items: stretch; + align-items: start; + row-gap: 0.75rem; + column-gap: 1rem; + border-block-start: 2px solid var(--accent-color-normal); + padding-block-start: 0.75rem; + padding-inline: 0.5rem; + margin-inline: 0.25rem; + line-height: 1.4; +} + +body .cv-content > section > h2 { + grid-column: 1 / span 1; + margin-block-start: 0.2rem; + line-height: 1; + font-size: 1.1rem; + font-weight: 200; + text-transform: uppercase; +} + +body .cv-content > section > p, +body .cv-content > section > section { + grid-column: 2 / 3; +} + +body .cv-content > section > section { + display: grid; + gap: 0.25rem; +} + +body .cv-content > section > section > header { + display: grid; + grid-template-columns: 1fr max-content; + column-gap: 0.25rem; +} + +body .cv-content > section > section > header > h3 { + grid-column: 1 / span 1; + font-size: 1rem; + font-weight: bolder; + white-space: wrap; + overflow: hidden; +} + +body .cv-content > section > section > header > span { + grid-column: 1; + font-weight: 200; +} + +body .cv-content > section > section > header > span.date { + grid-column: 2; + grid-row: 1; + justify-self: end; +} + +body .cv-content > section > section > ul { + padding-inline-start: 1rem; +} + +@media screen and (max-width: 50rem) { + body { + padding-inline: 1rem; + + } + body .cv-content > section { + margin-bottom: 1rem; + row-gap: 1.5rem; + } + body .cv-content > section > section { + margin-bottom: 2rem; + row-gap: 1rem; + } + body .cv-content > section > section, + body .cv-content > section > p { + grid-column-start: 1; + } + body .cv-content > section > section > header > h3 { + grid-column-end: span 2; + } + body .cv-content > section > section > header > span.date { + grid-row: 2; + } + body .cv-content > section > h2 { + grid-column-end: span 2; + justify-self: center; + font-size: 1.25rem; + } +} + +.specialisation, +.publisherNote, +.noteLink { + font-style: italic; +} + +.author { + text-decoration-line: underline; + text-decoration-style: dotted; +} + +#profilePic { + object-fit: cover; + height: 4em; + width: 4em; + box-shadow: 0.25em 0.25em var(--accent-color-normal); +} + +/* supervisor: inline on wide, block (no pipe) on narrow */ +.supervisor { + display: inline; +} + +@media screen and (max-width: 50rem) { + .supervisor { + display: block; + margin-top: 0.25rem; + } + .supervisor::before { + content: ""; + } +} + +/* specialisation: inline on wide, block (no pipe) on narrow */ +.specialisation { + display: inline; +} +.specialisation::before { + content: "| "; +} + +@media screen and (max-width: 50rem) { + .specialisation { + display: block; + margin-top: 0.25rem; + } + .specialisation::before { + content: ""; + } +} + +/* skills */ +.skills span { + font-size: 0.8rem; +} + +/* repeated information */ +.repeated { + font-style: italic; + font-weight: 300; +} + +/* CV print button */ +.printButton { + position: fixed; + text-decoration: none; + border: max(0.15vw, 1.5px) solid var(--border-color-normal); + padding: max(0.4vw, 4px) max(0.8vw, 8px); + border-right: none; + border-top-left-radius: max(0.5vw, 5px); + border-bottom-left-radius: max(0.5vw, 5px); + right: 0%; + bottom: 10%; + box-shadow: -0.2vw 0.2vw 0.5vw rgba(0,0,0,0.15); + background-color: var(--background); + z-index: 1; +} + +.printButton svg { + width: max(1.5vw, 1.5vh, 25px); + height: max(2vw, 2vh, 40px); + fill: var(--background); + stroke: var(--text-color-normal); +} + +.printButton:hover { + background-color: var(--active-color-normal); +} diff --git a/src/_includes/layouts/base.liquid b/src/_includes/layouts/base.liquid new file mode 100644 index 0000000..d0a61d2 --- /dev/null +++ b/src/_includes/layouts/base.liquid @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + {% if scripts %} + {% for s in scripts %} + + {% endfor %} + {% endif %} + + {{ title }} + + + + {% include "components/nav-header.liquid" %} + + + +
+ {{ content }} +
+ {% include "components/footer.liquid" %} + + \ No newline at end of file diff --git a/src/_includes/layouts/home.liquid b/src/_includes/layouts/home.liquid new file mode 100644 index 0000000..4a964f7 --- /dev/null +++ b/src/_includes/layouts/home.liquid @@ -0,0 +1,13 @@ +--- +layout: "layouts/base.liquid" +--- + +
+ {% if featuredImage %} + {{ + {% endif %} + +
+ {{ content }} +
+
\ No newline at end of file diff --git a/src/_includes/layouts/index.liquid b/src/_includes/layouts/index.liquid new file mode 100644 index 0000000..884826a --- /dev/null +++ b/src/_includes/layouts/index.liquid @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + {% if scripts %} + {% for s in scripts %} + + {% endfor %} + {% endif %} + + {{ title }} + + + + + + +
+ {{ content }} +
+ + \ No newline at end of file diff --git a/src/_includes/layouts/post.liquid b/src/_includes/layouts/post.liquid new file mode 100644 index 0000000..64e1f2d --- /dev/null +++ b/src/_includes/layouts/post.liquid @@ -0,0 +1,32 @@ +--- +layout: "layouts/base.liquid" +--- + +
+
+

{{ title }}

+
+ {% if featuredImage %} + {{ + {% endif %} + +
+ {{ content }} +
+ +
+

Published on: {{ date | date: "%B %d, %Y" }}

+ + {% assign filteredTags = tags | where_exp: "tag", "tag != 'posts'" %} + {% if filteredTags.size > 0 %} +
+

Tags:

+
    + {% for tag in filteredTags %} +
  • {{ tag }}
  • + {% endfor %} +
+
+ {% endif %} +
+
\ No newline at end of file diff --git a/src/_includes/print.css b/src/_includes/print.css new file mode 100644 index 0000000..5029b08 --- /dev/null +++ b/src/_includes/print.css @@ -0,0 +1,182 @@ +@media print { + /* Page setup */ + @page { + size: A4 portrait; + margin: 0.75in; + } + + /* Root styles (print overrides) */ + html { + font-size: 9pt; + scrollbar-gutter: auto; + } + + html[data-theme="dark"] { + --hue: 60; + --accent-hue: 20; + --invert-percent: 0; + + --background: hsl(var(--hue), 17%, 98%); + --active-color-normal: hsl(var(--hue), 2.94%, 93.67%); + --border-color-normal: hsl(var(--accent-hue), 6.88%, 37.06%); + + --text-color-normal: hsl(var(--hue), 0%, 0%); + --secondary-color-normal: hsl(var(--hue), 0%, 10%); + --accent-color-normal: hsl(21, 78.65%, 34.9%); + } + + /* Base element styles */ + body { + max-width: 880px; + margin-block: 6pt 24pt; + background-color: #ffffff; + } + + p, + h2, + h3, + blockquote, + pre, + span, + section > section { + break-inside: avoid; + } + + ul { + padding-inline-start: 1.25rem !important; + } + + ul li { + padding-inline-start: 0.25rem !important; + } + + a { + text-decoration: none; + } + + /* Hide UI-only elements */ + .header, + .printButton, + .footer { + display: none; + } + + /* Links */ + a.expandLink[href]:before { + content: "(" attr(href) ") "; + font-size: 5pt; + } + + a.noteLink span, + a.expandLink span { + display: none; + } + + a.noteLink[href]::after { + content: "(" attr(href) ") "; + } + + .addressLinks a { + font-size: 6pt; + } + + /* --- CV Layout --- */ + + /* CV header */ + body .cv-header { + padding: 6pt; + gap: 12pt; + } + + #profilePic { + object-fit: cover; + height: 48pt; + width: 48pt; + box-shadow: 3pt 3pt var(--accent-color-normal); + } + + body .cv-header h1 { + font-size: 18pt; + } + + body .cv-header h1 span { + font-size: 12pt; + } + + body .cv-header address { + line-height: 1.6; + } + + body .cv-header address a::after { + margin-inline-start: 0.5rem; + right: 0.5pt; + bottom: -2pt; + width: 9pt; + height: 9pt; + font-size: 9pt; + background-size: 1rem 1rem; + } + + /* CV content */ + body .cv-content { + display: grid; + grid-template-columns: max-content 1fr; + row-gap: 9pt; + } + + body .cv-content > section { + display: block; + border-block-start: 2px solid var(--accent-color-normal); + column-gap: 12pt; + row-gap: 12pt; + padding-block-start: 9pt; + padding-inline: 6pt; + margin-inline: 3pt; + line-height: 1.4; + } + + body .cv-content > section > h2 { + margin-block-start: 0.2rem; + margin-bottom: 6pt; + font-size: 10pt; + line-height: 1; + } + + body .cv-content > section > p, + body .cv-content > section > section { + margin-block: 6pt; + } + + body .cv-content > section > section { + display: grid; + gap: 0.25rem; + } + + body .cv-content > section > section > header { + display: grid; + grid-template-columns: 1fr max-content; + column-gap: 0.25rem; + } + + body .cv-content > section > section > header > h3 { + grid-column: 1; + font-size: 9pt; + } + + body .cv-content > section > section > header > span { + grid-column: 1; + } + + body .cv-content > section > section > header > span.date { + grid-column: 2; + grid-row: 1; + } + + body .cv-content > section > section > ul { + padding-inline-start: 1rem; + } + + body .cv-content .skills span { + font-size: 8pt; + } +} diff --git a/src/_includes/styles.css b/src/_includes/styles.css new file mode 100644 index 0000000..0daefea --- /dev/null +++ b/src/_includes/styles.css @@ -0,0 +1,418 @@ +html { + font-size: 1.2rem; + box-sizing: border-box; + scrollbar-gutter: stable both-edges; + + --hue: 60; + --accent-hue: 20; + --invert-percent: 0; + + --background: hsl(var(--hue), 17%, 98%); + --active-color-normal: hsl(var(--accent-hue), 10.94%, 95.67%); + --border-color-normal: hsl(var(--accent-hue), 6.88%, 37.06%); + + --text-color-normal: hsl(var(--hue), 0%, 0%); + --secondary-color-normal: hsl(var(--hue), 0%, 10%); + + --accent-color-normal: hsl(21, 78.65%, 34.9%); + + color: var(--text-color-normal); +} + +html[data-theme='dark'] { + --hue: 0; + --accent-hue: 20; + --invert-percent: 100; + + --background: hsl(var(--accent-hue), 27%, 10%); + --active-color-normal: hsl(var(--accent-hue), 15.46%, 19.02%); + --border-color-normal: hsl(var(--accent-hue), 6.88%, 37.06%); + + --text-color-normal: hsl(var(--hue), 100%, 100%); + --secondary-color-normal: hsl(var(--hue), 0%, 90%); + --accent-color-normal: hsl(var(--accent-hue), 78.65%, 34.9%); +} + +html.color-theme-in-transition, +html.color-theme-in-transition *, +html.color-theme-in-transition *:before, +html.color-theme-in-transition *:after { + transition: all 750ms !important; + transition-delay: 0 !important; +} + +*, +*::before, +*::after { + box-sizing: inherit; +} + +* { + margin: 0; + padding: 0; + border: 0; + font-family: Helvetica, Arial, sans-serif; +} + +a { + color: var(--text-color-normal); +} + +body { + max-width: 55rem; + margin-inline: auto; + margin-block: 0.5rem 2rem; + padding-inline: 1rem; + background-color: var(--background); +} + + + +/* Post List */ +.post-list { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(20rem, 1fr)); + gap: 2rem; + margin-block: 2rem; + width: 100%; +} + +.post-list article { + background-color: var(--active-color-normal); + border-bottom: 2px solid var(--accent-color-normal); + border-radius: 0.5rem; + padding: 1rem; + display: flex; + flex-direction: column; + height: 100%; + overflow-wrap: break-word; + word-break: break-all; + min-width: 0; + overflow: hidden; +} + +.post-list article img { + width: 100%; + height: 12rem; + object-fit: cover; + border-radius: 0.25rem; + margin-bottom: 0.75rem; +} + +.post-list article h2 { + font-size: 1.25rem; + margin-bottom: 0.5rem; + overflow-wrap: break-word; + word-break: normal; +} + +.post-list .date-and-tags { + display: flex; +} + +.post-list article p { + overflow-wrap: break-word; + word-break: normal; +} + +.post-list article p.date, .post-list article .tags { + font-size: 0.9rem; + color: var(--secondary-color-normal); + margin-bottom: 0.5rem; +} + +.post-list article .tags { + font-style: italic; + margin-left: 0.5rem; +} + +/* prevent .post-list from overflowing on very narrow viewports */ +@media screen and (max-width: 25rem) { + .post-list { + grid-template-columns: 1fr; + } +} + +/* Images */ +img { + max-width: 100%; +} +img[width][height] { + height: auto; +} +img[src$=".svg"] { + width: 100%; + height: auto; + max-width: none; +} + +/* Article */ + +@media screen and (max-width: 50rem) { + article.post { + padding-inline: 1rem; + } +} + +article.post { + margin-block: 2rem; +} + +.frontmatter { + display: flex; + flex-direction: column; + gap: 0.5rem +} + +.frontmatter p { + font-style: italic; +} + +.tags { + display: flex; + align-items: center; + gap: 0.5rem; /* space between label and list */ +} + +.tags ul { + display: flex; + gap: 0.5rem; + list-style: none; + padding: 0; + margin: 0; +} + + +.post-content { + line-height: 1.6; + margin-block: 1rem; +} + +.post-content p, +.post-content h1, +.post-content h2, +.post-content h3, +.post-content h4, +.post-content h5, +.post-content h6 { + margin-block: 0.5rem; +} + +.post-content h1, +.post-content h2, +.post-content h3, +.post-content h4, +.post-content h5, +.post-content h6 { + margin-top: 2rem; +} + +.post-content img { + display: block; + max-width: 100%; + height: auto; + margin-block: 1rem; +} + +.post-content img.float-left { + float: right; + max-width: 40%; + margin: 0 1rem 1rem 0; +} + +.post-content img.float-right { + float: right; + max-width: 40%; + margin: 0 0 1rem 1rem; +} + +.post-content .emphasised { + font-size: 1.5rem; + font-weight: bold; +} + +.post-content h1, .frontmatter h1 { + font-size: 2rem; +} + +.post-content a { + color: var(--text-color-normal); +} + +/* clearfix */ +.post-content::after { + content: ""; + display: table; + clear: both; +} + +/* disable floats on narrow screens */ +@media screen and (max-width: 50rem) { + .post-content img.float-left, + .post-content img.float-right { + float: none; + width: 100%; + margin: 1rem 0; + } +} + +/* Nav header */ +.header { + margin-top: 2rem; + display: grid; + grid-template-columns: 1fr 1fr 1fr; + grid-column-gap: 15px; + grid-row-gap: 10px; + grid-column: 2/2; + margin-bottom: 1rem; +} + +.header_content { + display: flex; + align-items: center; + gap: 0rem; +} + +.header_name { + float: left; + padding: 0.6rem; + border-bottom: 3px solid var(--accent-color-normal); + margin-right: 0rem; + text-decoration: none; +} + +.header_name h1 { + font-size: 1rem; + text-transform: uppercase; + letter-spacing: 0.1rem; + font-weight: bold; + margin:0; +} + +/* stacked-alpha-video { + display: inline-block; +} */ + +stacked-alpha-video video { + display: none; +} + +.is-hidden { opacity:0 !important; pointer-events:none; } + +.fox-player { position: relative; width: var(--size, 80px); } +.ratio-1x1::before { content: ""; display: block; } /* square */ + +.fox-player > * { position: absolute; inset: 0; width: 100%; height: 100%; } + +.fox-thumb { all:unset; cursor:pointer; display:block; z-index:2; } +.fox-thumb img { width:100%; height:100%; display:block; object-fit:cover; } + + + +.nav { + padding-top: 1rem; + grid-column: 3/3; + display: flex; + justify-content: flex-end; + align-items: center; +} + +.nav ul { + list-style: none; + display: flex; + gap: 1rem; + margin: 0; + padding: 0; +} + +.nav ul li a { + text-decoration: none; + -webkit-text-decoration: none; + padding: 0.5rem 1rem; + transition: color 0.1s ease-in, background-color 0.1s ease-in; + text-transform: uppercase; +} + +.nav ul li.current a { + font-weight: bold; +} + +.nav ul li a:hover { + text-decoration: underline; +} + +.nav ul li.current a:hover { + text-decoration: none; +} + +.nav button { + background-color: var(--background); + color: var(--text-color-normal); +} + +@media screen and (max-width: 50rem) { + .header { + display: grid; + grid-template-columns: 1fr; + grid-template-rows: auto auto; + margin-top: 1rem; + } + .header_content { + grid-column: 1; + grid-row: 1; + } + .nav { + grid-column: 1; + grid-row: 2; + justify-content: flex-start; + padding-inline: 1rem; + } + .nav ul li a { + text-decoration: underline; + padding: 0; + + } +} + +/* Footer */ +.footer { + margin-top: 1rem; + margin-bottom: 2rem; + border-top: 2pt solid var(--accent-color-normal); +} + +.footer_content { + padding: 0.5em 0; + display: flex; + justify-content: space-between; + align-items: center; +} + +.footer_content p { + margin: 0; + font-size: 0.8rem; + flex: 1; + line-height: 1; +} + +p.footer_center { + text-align: center; +} +p.footer_left { + text-align: left; +} +p.footer_right { + text-align: right; +} + +.footer_center svg { + vertical-align: middle; + width: 1em; + height: 1em; +} + +.footer_right .footer_logo { + width: 1.2em; + vertical-align: middle; +} + diff --git a/src/blankpage.html b/src/blankpage.html new file mode 100644 index 0000000..2d1aeb9 --- /dev/null +++ b/src/blankpage.html @@ -0,0 +1,17 @@ + + + + + +This page intentionally left blank + + + + + + +

This page intentionally left blank

+ + + + \ No newline at end of file diff --git a/src/client/video-with-transparency/index.ts b/src/client/video-with-transparency/index.ts new file mode 100644 index 0000000..b68f695 --- /dev/null +++ b/src/client/video-with-transparency/index.ts @@ -0,0 +1 @@ +import 'stacked-alpha-video'; \ No newline at end of file diff --git a/src/cv.liquid b/src/cv.liquid new file mode 100644 index 0000000..17789a6 --- /dev/null +++ b/src/cv.liquid @@ -0,0 +1,53 @@ +--- +layout: layouts/base.liquid +title: "Drew Edgar's CV" +permalink: "/cv/" +eleventyNavigation: + key: CV +--- + + +
+ +
+
+ {% + include "components/cv/cv-header.liquid", + basics: cv.basics + %} +
+ {% + include "components/cv/summary.liquid", + summary: cv.basics.summary + %} + {% + include "components/cv/education.liquid", + education: cv.education + %} + {% + include "components/cv/publications.liquid", + publications: cv.publications + %} + {% + include "components/cv/experience.liquid", + researchProjects: cv.researchProjects + %} + {% + include "components/cv/work.liquid", + work: cv.work + %} + {% + include "components/cv/skills.liquid", + skills: cv.skills + %} + {% + include "components/cv/references.liquid", + references: cv.references + %} +
+
+ + + diff --git a/src/images/favicon/apple-touch-icon.png b/src/images/favicon/apple-touch-icon.png new file mode 100644 index 0000000..d84e61a Binary files /dev/null and b/src/images/favicon/apple-touch-icon.png differ diff --git a/src/images/favicon/favicon.ico b/src/images/favicon/favicon.ico new file mode 100644 index 0000000..14d2f99 Binary files /dev/null and b/src/images/favicon/favicon.ico differ diff --git a/src/images/fox-first-frame.png b/src/images/fox-first-frame.png new file mode 100644 index 0000000..68b15f6 Binary files /dev/null and b/src/images/fox-first-frame.png differ diff --git a/src/images/fox-first-frame.webp b/src/images/fox-first-frame.webp new file mode 100644 index 0000000..9f73a04 Binary files /dev/null and b/src/images/fox-first-frame.webp differ diff --git a/src/images/fox.svg b/src/images/fox.svg new file mode 100644 index 0000000..e480dd1 --- /dev/null +++ b/src/images/fox.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/images/pfp-large.jpg b/src/images/pfp-large.jpg new file mode 100644 index 0000000..ab8375f Binary files /dev/null and b/src/images/pfp-large.jpg differ diff --git a/src/images/pfp-large.webp b/src/images/pfp-large.webp new file mode 100644 index 0000000..4b3920b Binary files /dev/null and b/src/images/pfp-large.webp differ diff --git a/src/images/pfp-small.jpg b/src/images/pfp-small.jpg new file mode 100644 index 0000000..eacec37 Binary files /dev/null and b/src/images/pfp-small.jpg differ diff --git a/src/images/pfp-small.webp b/src/images/pfp-small.webp new file mode 100644 index 0000000..c794910 Binary files /dev/null and b/src/images/pfp-small.webp differ diff --git a/src/images/pfp.jpg b/src/images/pfp.jpg new file mode 100644 index 0000000..d92b0f0 Binary files /dev/null and b/src/images/pfp.jpg differ diff --git a/src/images/pfp.webp b/src/images/pfp.webp new file mode 100644 index 0000000..e347e9a Binary files /dev/null and b/src/images/pfp.webp differ diff --git a/src/index.md b/src/index.md new file mode 100644 index 0000000..d544ba1 --- /dev/null +++ b/src/index.md @@ -0,0 +1,15 @@ +--- +layout: layouts/home.liquid +title: "Drew Edgar" +permalink: "/" +--- + +A portrait photo of Drew Edgar + +👋 Hello, I'm Drew! + +I'm currently working as a Research Assistant at the University of Glasgow, where I recently earned my Bachelor's degree in Computer Science with a focus on Human-Computer Interaction. + +My research explores the intersection of Extended Reality (XR) and Artificial Intelligence (AI), with a focus on understanding potential harms and misuse of these technologies to inform policy and ethical development guidelines. My goal is to contribute to the responsible evolution and use of technology, doing my part to ensure that these new advancements can benefit us all. + +I am currently seeking opportunities to apply my skills in a research or development role, where I can continue to grow and make a meaningful impact on the world. Feel free to check out my CV here to learn more about my work! \ No newline at end of file diff --git a/src/posts.liquid b/src/posts.liquid new file mode 100644 index 0000000..b6ebead --- /dev/null +++ b/src/posts.liquid @@ -0,0 +1,46 @@ +--- +layout: "layouts/base.liquid" +title: "Posts" +eleventyNavigation: + key: Posts +--- + +
+

Posts

+
+ {% if collections.posts and collections.posts.size > 0 %} + {% for post in collections.posts reversed %} +
+

{{ post.data.title }}

+ + {% if post.data.featuredImage %} + {{ + {% endif %} + +
+ {% if post.data.date %} +

{{ post.date | date: "%B %d, %Y" }}

+ {% endif %} + + + {% assign filteredTags = post.data.tags | where_exp: "tag", "tag != 'posts'" %} + {% if filteredTags.size > 0 %} +
+ — +
    + {% for tag in filteredTags %} +
  • {{ tag }}
  • + {% endfor %} +
+
+ {% endif %} +
+ +

{{ post.data.description | default: "" }}

+
+ {% endfor %} + {% else %} +

No posts yet...

+ {% endif %} +
+
\ No newline at end of file diff --git a/src/posts/writing/adaptive_audio.md b/src/posts/writing/adaptive_audio.md new file mode 100644 index 0000000..4bbc95d --- /dev/null +++ b/src/posts/writing/adaptive_audio.md @@ -0,0 +1,61 @@ +--- +layout: layouts/post.liquid +title: An Adaptive Music System For Pedestrians +description: How I designed, prototyped, and studied a music system that makes everyday walking feel like an immersive video game. +date: 2025-08-26 +tags: ["posts", "research projects"] +--- + +For my final year dissertation (which I’m excited to say is now **in the process of being published as a paper**), I wanted to see if I could bring something I love about video games into real life: **adaptive soundtracks**. The kind of music that changes and evolves with what you’re doing on screen. + +Some of my favourite examples of this in gaming are: +- In The Legend of Zelda: Tears of the Kingdom, the soundtrack [gradually layers more instruments](https://youtu.be/grimWQeYhwA) as you approach a final boss fight. +- Persona 5 shifts its background music depending on the mood or weather, offering [different versions](https://youtu.be/woz5qvDdMRM?list=RDU2Qd9TRY1Ro) for [tone](https://youtu.be/w7Cm74nDhac?list=RDw7Cm74nDhac), [rainy days](https://youtu.be/U2Qd9TRY1Ro?list=RDU2Qd9TRY1Ro), and more. +- In Super Mario Odyssey, stepping into an 8-bit zone transforms the music into a [retro chiptune version](https://youtu.be/e1sLlITB02A). + +These adaptive scores aren’t just background — they guide your attention, shape the mood, and make you feel part of the world. I wanted to try translating those ideas to real-world walking. + +## Requirements gathering + +Before building anything, I needed to know which aspects of the environment actually matter to walkers, and how music could adapt in response to variations in those different aspects. + +I ran a focus group where participants watched a first-person walking video and annotated a map with points where they thought a piece of music should change in response to changes in the environment. +We also brainstormed possible musical responses — from changing tempo to adding or removing instruments. + +When I combined those ideas with what the literature already says about pedestrian experience, I landed on four key features to focus on: +- **Greenery** (trees, plants, parks) +- **Traffic / Busyness** (cars, people, general busyness) +- **Pavement width** (tight vs. spacious) +- **Street Aesthetics** (how nice a street looks) + +On the music side, I decided to use instrument layering — splitting a track into stems (bass, drums, vocals, etc.) and adjusting the volume of each layer based on the environment. Thanks to AI stem-splitting tools (of which there are plenty available), this approach can be applied to almost any song, meaning this system can feasibly be applied to a user's entire music playlist. + +## Feasibility Study + +Next, I wanted to know: can we actually detect these features in real time? +I hacked together a prototype using [Meta’s Project Aria glasses](https://www.projectaria.com/glasses/), which captured live video while walking. That video was fed into an object detection model (YOLO), which counted cars and people to estimate traffic density. At the same time, I used AI-based music stem separation to split a song into layers. +As the traffic “score” increased, more layers were added, making the music richer and more intense. Previous research also showed that similar computer vision techniques could be used to capture the other three affordances: estimating greenery levels in an area, measuring path width, and, with machine learning, detecting aesthetic qualities of architecture. It was a proof-of-concept that yes, this could work with the right technology in the future. + +## Wizard of Oz User Study + +For the main evaluation, I ran a Wizard-of-Oz style user study. Instead of relying on unreliable sensig and computer vision, I built a custom native iOS Swift app that let me manually control the music in real time while walking alongside participants. This meant they got to experience a smooth “ideal case” version of the system — essentially, what it should feel like when fully automated. +The study took place along a 0.5 km university campus route that I divided into 14 segments, each chosen because something about the environment shifted noticeably — for example, the amount of greenery, how wide the pavement was, or the general aesthetic of the street. For each segment, I assigned affordance scores (0–1 scale) based on: +- Greenery – estimating the proportion of visible trees, grass, or plants +- Pavement width – measured directly on-site +- Aesthetics – rated by independent scorers who watched video clips of each segment +- Traffic density – treated as a dynamic factor, measured live by observation + +In the app, static affordances (like greenery or pavement width) were tied to these pre-set scores. To create smooth transitions between segments, the app gave me a slider to interpolate between values — so as participants walked, the music could gradually shift. For traffic, I adjusted the score live using another slider, based on the density of people and cars in the immediate area. + +Each participant walked the route five times: once for each single-affordance condition (greenery, traffic, pavement width, aesthetics), plus a Curated condition, where they got to choose which affordances controlled which instrument layers (e.g. vocals → traffic, bass → pavement width). +The app supported all of this by letting me: +- progress through the route segment by segment, updating scores, +- smoothly interpolate affordance values with sliders, +- and in the Curated condition, set up participants’ own mappings between environment and music. + +Screenshots of different screens of the study app. + + +## Wrapping Up + +I won’t spoil the results here (you'll have to wait for the full paper for that), but running this project let me see how much potential there is for bringing adaptive music into everyday life. Just like in games, responsive soundtracks can change how you move, notice things, and even feel in familiar spaces. \ No newline at end of file diff --git a/src/posts/writing/ai_safety.md b/src/posts/writing/ai_safety.md new file mode 100644 index 0000000..2025f73 --- /dev/null +++ b/src/posts/writing/ai_safety.md @@ -0,0 +1,29 @@ +--- +layout: layouts/post.liquid +title: AI Safety +description: A look at my research into AI safety, from co-design workshops on AR risks to technical evaluation of models. +date: 2025-09-01 +tags: ["posts", "research projects"] +--- + +Over the past couple of years, I’ve been involved in several projects at the intersection of AI safety and human–AI interaction research. These projects all share a common theme: understanding how AI systems behave in the real world, identifying potential risks, and exploring ways to make them safer and more aligned with human needs. + +## Human Risks of Augmented Reality and AI +One strand of my work has focused on the REPHRAIN DAARC (Defence Against Augmented Reality Crimes) project, which looks at the social and criminal risks of the combination of immersive technologies and artificial intelligence capabilities. + +My first contribution here was helping with a systematic literature review of the potential harms and criminal uses of AR — everything from invasive data collection to novel types of fraud or harassment. That work gave us a clearer map of the risks already being discussed in research and policy circles. + +But we also wanted to explore how ordinary people imagined future threats. To do that, I co-ran focus groups and workshops where participants co-designed possible AR+AI crime scenarios. The process was deliberately interactive: participants sketched out ideas, swapped and remixed each other’s scenarios, and then worked together to assess their likelihood and severity on a risk matrix. Finally, the groups brainstormed possible countermeasures or guidelines that could help mitigate these threats. + +Later, we applied pair qualitative coding to all of these responses, looking for recurring themes in how people thought about risk, probability, and safety in an emerging technology space. + +**This research is currently in the process of being published.** + +## Multimodal AI Safety Evaluation + +In parallel, I also pursued an independent project to better understand how AI safety research is actually done — the methods, frameworks, and tools that underpin it. Coming from an HCI background, I wanted to explore how human-centred perspectives could connect with technical evaluation. +Using Meta’s Llama 3 model, I developed Python pipelines that handled everything from cleaning multimodal datasets to structuring prompts for consistent testing. Rather than only looking at accuracy, I focused on where things could go wrong: when the model fabricated information (hallucination), when it produced unsafe or biased text (harmful output), and how its responses might mislead or manipulate users (interaction risks). + +To investigate these, I used industry-standard safety benchmarks and toolkits, applying mixed-effects models and other statistical methods to analyse performance. I also implemented interactive Python simulations to capture responses, compute safety metrics, and visualise the data with pandas and matplotlib. + +The outcome was a comprehensive report that summarised both accuracy and safety vulnerabilities, and outlined mitigation strategies for real-world applications. Just as importantly, the project gave me hands-on experience with the evaluation frameworks used in AI safety research and highlighted how they can (and should) connect to user experience considerations in HCI. \ No newline at end of file diff --git a/src/posts/writing/colophon.md b/src/posts/writing/colophon.md new file mode 100644 index 0000000..69ebecd --- /dev/null +++ b/src/posts/writing/colophon.md @@ -0,0 +1,52 @@ +--- +layout: layouts/post.liquid +title: Colophon +description: A peek at how I organise my readings, stay off social media, and keep this site running. +date: 2025-08-20 +tags: ["posts", "documentation"] +--- +
+A lot of blogs that I follow have a colophon page describing their writing process, and they always provide me with a new way to reflect on how I work, so I’ve decided to join the club! + +## RSS + +I am trying to cut down on the amount of content I consume that comes from social media algorithms. Even if you disregard how widely they are used for advertising, misinformation, and to manipulate you into just feeling bad, I find that when I am more intentional with what I choose to engage with, my whole day improves as a result. + +What this means in practice is only using RSS feeds as much as possible. +- I’ve converted my YouTube subscriptions into feeds so that I don’t have to engage with the actual YouTube site. (Which you can do just by appending `/rss` to the end of the channel link) +- I subscribe to a few news sites (mostly tech focused) +- I converted any email newsletters into feeds using https://kill-the-newsletter.com +- Everything else comes from blogs I have discovered over the past few months + +My RSS Reader of choice is NetNewsWire, which is pretty basic but has everything I really need. + + +## Obsidian + +Anything that I engage with that I think is of value to me goes into a note in [Obsidian](https://obsidian.md). Each note links to at least one tag which then makes it easier to both find the note later on and find links between ideas/notes. I don’t limit myself too much by what should constitute a note, it can be a quote from an article, a summary of an essay, a full guide to a subject, or simply a link to the resource with a small explainer for why I found it valuable. + +Here is a little example for the nerds like me that are interested in this kind of thing: + +A screenshot of my Obsidian setup. + +Obsidian is also where I do all of my own writing and journalling, which means I can easily link to other notes that I take ideas from. I avoid using AI for any writing except straightforward tasks where nothing is lost through automation, I’m a big believer in [just reading the prompt instead](https://claytonwramsey.com/blog/prompt/). + +There are a few plugins that I can’t live without +- [Local Backup](https://obsidian.md/plugins?id=local-backup) — Very occasionally I’ll have an Issue with iCloud where a note I make suddenly disappears, and while it’s only happened a couple of times, this plugin means I don’t worry about it all that much +- [Smart Typography](https://obsidian.md/plugins?id=obsidian-smart-typography) — I just love all my little symbols → ← ≤ +- [Zotero Integration](https://obsidian.md/plugins?id=obsidian-zotero-desktop-connector) — A huge timesaver when doing a literature review +- [Style Settings](https://obsidian.md/plugins?id=obsidian-style-settings) + [AnuPpuccin](https://github.com/AnubisNekhet/anuppuccin) — My theme of choice + +## Other Apps + +[LookAway](https://lookaway.com) — This app reminds me to sit up straight, blink normally, and actually blocks me from using my Mac on a [Pomodoro](https://en.wikipedia.org/wiki/Pomodoro_Technique) schedule so that I actually have to get up and move around once in a while. Life-saving. + +[FocusDock](https://focusdock.eu) — Removes all the fluff from my Mac dock when I’m working so I’m not tempted to open Discord every 5 seconds. + +## Publishing + +This site was originally intended to just host a html version of my CV, which I did so using [this guide by Michael Engen](https://michaelengen.com/posts/my-eleventy-resume/), but I enjoyed the simplicity of Eleventy so much that I decided to develop it into a full blog. I expect that the design of this site will change a lot over time, but I’m fairly happy with it for now. + +I have tried to remove as much unecessary bloat as possible adn am trying to stay more vigilant about keeping my code small and tidy, as well as optimising any images that are used. + +This website runs on green hosting - verified by thegreenwebfoundation.org diff --git a/src/posts/writing/images/obsidian_screenshot.png b/src/posts/writing/images/obsidian_screenshot.png new file mode 100644 index 0000000..c7887e4 Binary files /dev/null and b/src/posts/writing/images/obsidian_screenshot.png differ diff --git a/src/posts/writing/images/obsidian_screenshot.webp b/src/posts/writing/images/obsidian_screenshot.webp new file mode 100644 index 0000000..e7ca388 Binary files /dev/null and b/src/posts/writing/images/obsidian_screenshot.webp differ diff --git a/src/posts/writing/images/study_interface.png b/src/posts/writing/images/study_interface.png new file mode 100644 index 0000000..96bd33d Binary files /dev/null and b/src/posts/writing/images/study_interface.png differ diff --git a/src/posts/writing/images/study_interface.webp b/src/posts/writing/images/study_interface.webp new file mode 100644 index 0000000..3a58129 Binary files /dev/null and b/src/posts/writing/images/study_interface.webp differ diff --git a/src/posts/writing/xr_notifications.md b/src/posts/writing/xr_notifications.md new file mode 100644 index 0000000..94aeef7 --- /dev/null +++ b/src/posts/writing/xr_notifications.md @@ -0,0 +1,28 @@ +--- +layout: layouts/post.liquid +title: Notifications in Extended Reality +description: My first research paper, where I helped build an AR study, ran participants, and learned what teamwork in HCI looks like. +date: 2025-08-21 +tags: ["posts", "research projects"] +--- + +Last year, I had the chance to join a research project that was a collaboration between the University of Glasgow (my university) and Carnegie Mellon University. The work ended up being published at IEEE VR — my first ever published paper, which is a big point of pride for me. It was also my first real experience working as part of a research team, and it gave me a front-row view of how studies in HCI are designed, built, and actually run with participants. +The project looked at different ways of delivering audio-visual notifications in augmented reality (AR) — from simple icons and tones to full text-to-speech messages — and asked how these designs affected people’s attention and usability. + +You can read the published paper [here](https://doi.org/10.1109/VR59515.2025.00052). + +## My Role + +I was responsible for three main areas: +- **Helping to design the study** — I worked with the team to figure out what participants would actually do, what kinds of notifications they’d encounter, and how we’d measure their responses. It was eye-opening to see how much thought goes into balancing realism with experimental control. +- **Developing the Unity application** – I built the app that ran the study, delivering notifications, logging data, and handling interactions in XR. This developed my Unity knowledge further than ever before — I had to make something robust enough to behave consistently across dozens of participant sessions. +- **Running the study itself** – I helped guide participants through the tasks, answered their questions, and made sure everything ran smoothly. This was where I learned the most about working directly with people: giving clear instructions, knowing when to step in and offer help, and adapting on the fly when the tech (inevitably) misbehaved. + +## What I Learned +This project wasn’t just about coding or collecting data — it taught me a lot about the process of doing research: +- Planning around schedules – I quickly learned the importance of structuring study sessions, managing participant time, and keeping everything on track without making the study feel rushed. +- Interacting with participants – running a study isn’t just technical; it’s about people skills. Clear communication and a calm presence go a long way to making participants feel comfortable. +- Collaboration in practice – Working with researchers across institutions taught me how differently people contribute to a project. Seeing how these pieces fit together showed me that research isn’t about doing everything yourself — it’s about trusting your collaborators' expertise, coordinating across time zones and schedules, and making sure your part supports the bigger picture. + +Being part of this project was hugely rewarding. It gave me practical experience in everything from Unity development to participant management, and it showed me how much thought and care goes into making a study run smoothly. +Most importantly, it marked my first step into the world of research — and having that work recognised at IEEE VR was very motivating. I left the project not only with new technical skills, but also with a stronger sense of how to collaborate, communicate, and contribute meaningfully to a shared research goal. \ No newline at end of file diff --git a/src/videos/av1.mp4 b/src/videos/av1.mp4 new file mode 100644 index 0000000..6cf22f0 Binary files /dev/null and b/src/videos/av1.mp4 differ diff --git a/src/videos/hevc.mp4 b/src/videos/hevc.mp4 new file mode 100644 index 0000000..fb61033 Binary files /dev/null and b/src/videos/hevc.mp4 differ