[Archived] Archived WIP of vielle.dev

Compare changes

Choose any two refs to compare.

+1
.gitignore
··· 22 22 23 23 # jetbrains setting folder 24 24 .idea/ 25 + .vscode/*
+3
.simple-git-hooks.json
··· 1 + { 2 + "pre-commit": "pnpm pretty-quick --staged" 3 + }
-3
.vscode/settings.json
··· 1 - { 2 - "cSpell.words": ["titlebar"] 3 - }
+15 -3
astro.config.mjs
··· 1 1 // @ts-check 2 2 import { defineConfig } from "astro/config"; 3 - 4 3 import mdx from "@astrojs/mdx"; 4 + import sitemap from "@astrojs/sitemap"; 5 5 6 - import sitemap from "@astrojs/sitemap"; 6 + import { rehypeAccessibleEmojis } from "rehype-accessible-emojis"; 7 + import rehypeCustomHtml from "./rehype-custom-html"; 7 8 8 9 // https://astro.build/config 9 10 export default defineConfig({ 10 11 integrations: [mdx(), sitemap()], 11 12 12 13 output: "server", 13 - 14 14 site: "https://vielle.dev", 15 + 16 + markdown: { 17 + // @ts-expect-error idk why this gets flagged as wrong 18 + rehypePlugins: [rehypeAccessibleEmojis, rehypeCustomHtml], 19 + shikiConfig: { 20 + themes: { 21 + dark: "catppuccin-frappe", 22 + light: "github-light-high-contrast", 23 + }, 24 + defaultColor: false, 25 + }, 26 + }, 15 27 });
+12 -6
package.json
··· 12 12 }, 13 13 "dependencies": { 14 14 "@astrojs/check": "^0.9.4", 15 - "@astrojs/mdx": "^4.2.6", 16 - "@astrojs/rss": "^4.0.11", 17 - "@astrojs/sitemap": "^3.4.0", 18 - "astro": "5.7.13", 15 + "@astrojs/mdx": "^4.3.0", 16 + "@astrojs/rss": "^4.0.12", 17 + "@astrojs/sitemap": "^3.4.1", 18 + "astro": "5.11.0", 19 19 "lunarphase-js": "^2.0.3", 20 20 "markdown-it": "^14.1.0", 21 - "typescript": "^5.8.3" 21 + "rehype-accessible-emojis": "^0.3.2", 22 + "remark-toc": "^9.0.0", 23 + "typescript": "^5.8.3", 24 + "unified": "^11.0.5" 22 25 }, 23 26 "devDependencies": { 27 + "@types/hast": "^3.0.4", 24 28 "prettier": "3.5.3", 25 - "prettier-plugin-astro": "0.14.1" 29 + "prettier-plugin-astro": "0.14.1", 30 + "pretty-quick": "^4.1.1", 31 + "simple-git-hooks": "^2.13.0" 26 32 } 27 33 }
+258 -48
pnpm-lock.yaml
··· 11 11 specifier: ^0.9.4 12 12 version: 0.9.4(prettier-plugin-astro@0.14.1)(prettier@3.5.3)(typescript@5.8.3) 13 13 "@astrojs/mdx": 14 - specifier: ^4.2.6 15 - version: 4.2.6(astro@5.7.13(@types/node@22.15.20)(rollup@4.41.0)(typescript@5.8.3)(yaml@2.8.0)) 14 + specifier: ^4.3.0 15 + version: 4.3.0(astro@5.11.0(@types/node@22.15.20)(rollup@4.41.0)(typescript@5.8.3)(yaml@2.8.0)) 16 16 "@astrojs/rss": 17 - specifier: ^4.0.11 18 - version: 4.0.11 17 + specifier: ^4.0.12 18 + version: 4.0.12 19 19 "@astrojs/sitemap": 20 - specifier: ^3.4.0 21 - version: 3.4.0 20 + specifier: ^3.4.1 21 + version: 3.4.1 22 22 astro: 23 - specifier: 5.7.13 24 - version: 5.7.13(@types/node@22.15.20)(rollup@4.41.0)(typescript@5.8.3)(yaml@2.8.0) 23 + specifier: 5.11.0 24 + version: 5.11.0(@types/node@22.15.20)(rollup@4.41.0)(typescript@5.8.3)(yaml@2.8.0) 25 25 lunarphase-js: 26 26 specifier: ^2.0.3 27 27 version: 2.0.3 28 28 markdown-it: 29 29 specifier: ^14.1.0 30 30 version: 14.1.0 31 + rehype-accessible-emojis: 32 + specifier: ^0.3.2 33 + version: 0.3.2 34 + remark-toc: 35 + specifier: ^9.0.0 36 + version: 9.0.0 31 37 typescript: 32 38 specifier: ^5.8.3 33 39 version: 5.8.3 40 + unified: 41 + specifier: ^11.0.5 42 + version: 11.0.5 34 43 devDependencies: 44 + "@types/hast": 45 + specifier: ^3.0.4 46 + version: 3.0.4 35 47 prettier: 36 48 specifier: 3.5.3 37 49 version: 3.5.3 38 50 prettier-plugin-astro: 39 51 specifier: 0.14.1 40 52 version: 0.14.1 53 + pretty-quick: 54 + specifier: ^4.1.1 55 + version: 4.1.1(prettier@3.5.3) 56 + simple-git-hooks: 57 + specifier: ^2.13.0 58 + version: 2.13.0 41 59 42 60 packages: 43 61 "@astrojs/check@0.9.4": ··· 55 73 integrity: sha512-7bCjW6tVDpUurQLeKBUN9tZ5kSv5qYrGmcn0sG0IwacL7isR2ZbyyA3AdZ4uxsuUFOS2SlgReTH7wkxO6zpqWA==, 56 74 } 57 75 76 + "@astrojs/compiler@2.12.2": 77 + resolution: 78 + { 79 + integrity: sha512-w2zfvhjNCkNMmMMOn5b0J8+OmUaBL1o40ipMvqcG6NRpdC+lKxmTi48DT8Xw0SzJ3AfmeFLB45zXZXtmbsjcgw==, 80 + } 81 + 58 82 "@astrojs/internal-helpers@0.6.1": 59 83 resolution: 60 84 { ··· 76 100 prettier-plugin-astro: 77 101 optional: true 78 102 79 - "@astrojs/markdown-remark@6.3.1": 103 + "@astrojs/markdown-remark@6.3.2": 80 104 resolution: 81 105 { 82 - integrity: sha512-c5F5gGrkczUaTVgmMW9g1YMJGzOtRvjjhw6IfGuxarM6ct09MpwysP10US729dy07gg8y+ofVifezvP3BNsWZg==, 106 + integrity: sha512-bO35JbWpVvyKRl7cmSJD822e8YA8ThR/YbUsciWNA7yTcqpIAL2hJDToWP5KcZBWxGT6IOdOkHSXARSNZc4l/Q==, 83 107 } 84 108 85 - "@astrojs/mdx@4.2.6": 109 + "@astrojs/mdx@4.3.0": 86 110 resolution: 87 111 { 88 - integrity: sha512-0i/GmOm6d0qq1/SCfcUgY/IjDc/bS0i42u7h85TkPFBmlFOcBZfkYhR5iyz6hZLwidvJOEq5yGfzt9B1Azku4w==, 112 + integrity: sha512-OGX2KvPeBzjSSKhkCqrUoDMyzFcjKt5nTE5SFw3RdoLf0nrhyCXBQcCyclzWy1+P+XpOamn+p+hm1EhpCRyPxw==, 89 113 } 90 - engines: { node: ^18.17.1 || ^20.3.0 || >=22.0.0 } 114 + engines: { node: 18.20.8 || ^20.3.0 || >=22.0.0 } 91 115 peerDependencies: 92 116 astro: ^5.0.0 93 117 94 - "@astrojs/prism@3.2.0": 118 + "@astrojs/prism@3.3.0": 95 119 resolution: 96 120 { 97 - integrity: sha512-GilTHKGCW6HMq7y3BUv9Ac7GMe/MO9gi9GW62GzKtth0SwukCu/qp2wLiGpEujhY+VVhaG9v7kv/5vFzvf4NYw==, 121 + integrity: sha512-q8VwfU/fDZNoDOf+r7jUnMC2//H2l0TuQ6FkGJL8vD8nw/q5KiL3DS1KKBI3QhI9UQhpJ5dc7AtqfbXWuOgLCQ==, 98 122 } 99 - engines: { node: ^18.17.1 || ^20.3.0 || >=22.0.0 } 123 + engines: { node: 18.20.8 || ^20.3.0 || >=22.0.0 } 100 124 101 - "@astrojs/rss@4.0.11": 125 + "@astrojs/rss@4.0.12": 102 126 resolution: 103 127 { 104 - integrity: sha512-3e3H8i6kc97KGnn9iaZBJpIkdoQi8MmR5zH5R+dWsfCM44lLTszOqy1OBfGGxDt56mpQkYVtZJWoxMyWuUZBfw==, 128 + integrity: sha512-O5yyxHuDVb6DQ6VLOrbUVFSm+NpObulPxjs6XT9q3tC+RoKbN4HXMZLpv0LvXd1qdAjzVgJ1NFD+zKHJNDXikw==, 105 129 } 106 130 107 - "@astrojs/sitemap@3.4.0": 131 + "@astrojs/sitemap@3.4.1": 108 132 resolution: 109 133 { 110 - integrity: sha512-C5m/xsKvRSILKM3hy47n5wKtTQtJXn8epoYuUmCCstaE9XBt20yInym3Bz2uNbEiNfv11bokoW0MqeXPIvjFIQ==, 134 + integrity: sha512-VjZvr1e4FH6NHyyHXOiQgLiw94LnCVY4v06wN/D0gZKchTMkg71GrAHJz81/huafcmavtLkIv26HnpfDq6/h/Q==, 111 135 } 112 136 113 - "@astrojs/telemetry@3.2.1": 137 + "@astrojs/telemetry@3.3.0": 114 138 resolution: 115 139 { 116 - integrity: sha512-SSVM820Jqc6wjsn7qYfV9qfeQvePtVc1nSofhyap7l0/iakUKywj3hfy3UJAOV4sGV4Q/u450RD4AaCaFvNPlg==, 140 + integrity: sha512-UFBgfeldP06qu6khs/yY+q1cDAaArM2/7AEIqQ9Cuvf7B1hNLq0xDrZkct+QoIGyjq56y8IaE2I3CTvG99mlhQ==, 117 141 } 118 - engines: { node: ^18.17.1 || ^20.3.0 || >=22.0.0 } 142 + engines: { node: 18.20.8 || ^20.3.0 || >=22.0.0 } 119 143 120 144 "@astrojs/yaml2ts@0.2.2": 121 145 resolution: ··· 924 948 integrity: sha512-rO73L89PJxeYM3s3pPPjiPgVVcymqU490g0YO5n5By0k2Erzj6tay/4lr1CHAAU4JyOWd1rpQ8bCf6cZfHU96A==, 925 949 } 926 950 951 + "@types/ungap__structured-clone@1.2.0": 952 + resolution: 953 + { 954 + integrity: sha512-ZoaihZNLeZSxESbk9PUAPZOlSpcKx81I1+4emtULDVmBLkYutTcMlCj2K9VNlf9EWODxdO6gkAqEaLorXwZQVA==, 955 + } 956 + 927 957 "@types/unist@2.0.11": 928 958 resolution: 929 959 { ··· 1087 1117 } 1088 1118 hasBin: true 1089 1119 1090 - astro@5.7.13: 1120 + astro@5.11.0: 1091 1121 resolution: 1092 1122 { 1093 - integrity: sha512-cRGq2llKOhV3XMcYwQpfBIUcssN6HEK5CRbcMxAfd9OcFhvWE7KUy50zLioAZVVl3AqgUTJoNTlmZfD2eG0G1w==, 1123 + integrity: sha512-MEICntERthUxJPSSDsDiZuwiCMrsaYy3fnDhp4c6ScUfldCB8RBnB/myYdpTFXpwYBy6SgVsHQ1H4MuuA7ro/Q==, 1094 1124 } 1095 1125 engines: 1096 - { node: ^18.17.1 || ^20.3.0 || >=22.0.0, npm: ">=9.6.5", pnpm: ">=7.1.0" } 1126 + { node: 18.20.8 || ^20.3.0 || >=22.0.0, npm: ">=9.6.5", pnpm: ">=7.1.0" } 1097 1127 hasBin: true 1098 1128 1099 1129 axobject-query@4.1.0: ··· 1557 1587 integrity: sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==, 1558 1588 } 1559 1589 1560 - fast-xml-parser@4.5.3: 1590 + fast-xml-parser@5.2.3: 1561 1591 resolution: 1562 1592 { 1563 - integrity: sha512-RKihhV+SHsIUGXObeVy9AXiBbFwkVk7Syp8XgwN5U3JV416+Gwp/GO9i0JYKmikykgz/UHRrrV4ROuZEo/T0ig==, 1593 + integrity: sha512-OdCYfRqfpuLUFonTNjvd30rCBZUneHpSQkCqfaeWQ9qrKcl6XlWeDBNVwGb+INAIxRshuN2jF+BE0L6gbBO2mw==, 1564 1594 } 1565 1595 hasBin: true 1566 1596 ··· 1588 1618 } 1589 1619 engines: { node: ">=8" } 1590 1620 1621 + find-up@5.0.0: 1622 + resolution: 1623 + { 1624 + integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==, 1625 + } 1626 + engines: { node: ">=10" } 1627 + 1591 1628 flattie@1.1.1: 1592 1629 resolution: 1593 1630 { ··· 1614 1651 } 1615 1652 engines: { node: ^8.16.0 || ^10.6.0 || >=11.0.0 } 1616 1653 os: [darwin] 1654 + 1655 + gemoji@4.2.1: 1656 + resolution: 1657 + { 1658 + integrity: sha512-V9lUpRSn+KQGavZx8Pk+6mxG3kaz21ae2kTCXuT36KaRPNgYU8eHtj/RcUCNFVvmwppsYYz3nnNS9lmcP5kTsg==, 1659 + } 1617 1660 1618 1661 get-caller-file@2.0.5: 1619 1662 resolution: ··· 1660 1703 integrity: sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg==, 1661 1704 } 1662 1705 1706 + hast-util-is-element@1.1.0: 1707 + resolution: 1708 + { 1709 + integrity: sha512-oUmNua0bFbdrD/ELDSSEadRVtWZOf3iF6Lbv81naqsIV99RnSCieTbWuWCY8BAeEfKJTKl0gRdokv+dELutHGQ==, 1710 + } 1711 + 1663 1712 hast-util-is-element@3.0.0: 1664 1713 resolution: 1665 1714 { ··· 1737 1786 { 1738 1787 integrity: sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==, 1739 1788 } 1789 + 1790 + ignore@7.0.5: 1791 + resolution: 1792 + { 1793 + integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==, 1794 + } 1795 + engines: { node: ">= 4" } 1740 1796 1741 1797 import-meta-resolve@4.1.0: 1742 1798 resolution: ··· 1889 1945 integrity: sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==, 1890 1946 } 1891 1947 1948 + locate-path@6.0.0: 1949 + resolution: 1950 + { 1951 + integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==, 1952 + } 1953 + engines: { node: ">=10" } 1954 + 1892 1955 lodash@4.17.21: 1893 1956 resolution: 1894 1957 { ··· 2045 2108 resolution: 2046 2109 { 2047 2110 integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==, 2111 + } 2112 + 2113 + mdast-util-toc@7.1.0: 2114 + resolution: 2115 + { 2116 + integrity: sha512-2TVKotOQzqdY7THOdn2gGzS9d1Sdd66bvxUyw3aNpWfcPXCLYSJCCgfPy30sEtuzkDraJgqF35dzgmz6xlvH/w==, 2048 2117 } 2049 2118 2050 2119 mdn-data@2.12.2: ··· 2283 2352 } 2284 2353 engines: { node: ">=8.6" } 2285 2354 2355 + mri@1.2.0: 2356 + resolution: 2357 + { 2358 + integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==, 2359 + } 2360 + engines: { node: ">=4" } 2361 + 2286 2362 mrmime@2.0.1: 2287 2363 resolution: 2288 2364 { ··· 2378 2454 integrity: sha512-rPiZhzC3wXwE59YQMRDodUwwT9FZ9nNBwQQfsd1wfdtlKEyCdRV0avrTcSZ5xlIvGRVPd/cx6ZN45ECmS39xvg==, 2379 2455 } 2380 2456 2457 + p-limit@3.1.0: 2458 + resolution: 2459 + { 2460 + integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==, 2461 + } 2462 + engines: { node: ">=10" } 2463 + 2381 2464 p-limit@6.2.0: 2382 2465 resolution: 2383 2466 { 2384 2467 integrity: sha512-kuUqqHNUqoIWp/c467RI4X6mmyuojY5jGutNU0wVTmEOOfcuwLqyMVoAi9MKi2Ak+5i9+nhmrK4ufZE8069kHA==, 2385 2468 } 2386 2469 engines: { node: ">=18" } 2470 + 2471 + p-locate@5.0.0: 2472 + resolution: 2473 + { 2474 + integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==, 2475 + } 2476 + engines: { node: ">=10" } 2387 2477 2388 2478 p-queue@8.1.0: 2389 2479 resolution: ··· 2434 2524 { 2435 2525 integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==, 2436 2526 } 2527 + 2528 + path-exists@4.0.0: 2529 + resolution: 2530 + { 2531 + integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==, 2532 + } 2533 + engines: { node: ">=8" } 2437 2534 2438 2535 picocolors@1.1.1: 2439 2536 resolution: ··· 2485 2582 engines: { node: ">=14" } 2486 2583 hasBin: true 2487 2584 2585 + pretty-quick@4.1.1: 2586 + resolution: 2587 + { 2588 + integrity: sha512-9Ud0l/CspNTmyIdYac9X7Inb3o8fuUsw+1zJFvCGn+at0t1UwUcUdo2RSZ41gcmfLv1fxgWQxWEfItR7CBwugg==, 2589 + } 2590 + engines: { node: ">=14" } 2591 + hasBin: true 2592 + peerDependencies: 2593 + prettier: ^3.0.0 2594 + 2488 2595 prismjs@1.30.0: 2489 2596 resolution: 2490 2597 { ··· 2579 2686 integrity: sha512-uorlqlzAKjKQZ5P+kTJr3eeJGSVroLKoHmquUj4zHWuR+hEyNqlXsSKlYYF5F4NI6nl7tWCs0apKJ0lmfsXAPA==, 2580 2687 } 2581 2688 2689 + rehype-accessible-emojis@0.3.2: 2690 + resolution: 2691 + { 2692 + integrity: sha512-kChZo+EZsuFQYHUPu6kOZFjDrG7UtQdGxkrCvHBVo9ariKPL6S68QdPVxLxwcAtZSRZIXZhDuTJHgIM8KW24Qw==, 2693 + } 2694 + 2582 2695 rehype-parse@9.0.1: 2583 2696 resolution: 2584 2697 { ··· 2646 2759 integrity: sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==, 2647 2760 } 2648 2761 2762 + remark-toc@9.0.0: 2763 + resolution: 2764 + { 2765 + integrity: sha512-KJ9txbo33GjDAV1baHFze7ij4G8c7SGYoY8Kzsm2gzFpbhL/bSoVpMMzGa3vrNDSWASNd/3ppAqL7cP2zD6JIA==, 2766 + } 2767 + 2649 2768 request-light@0.5.8: 2650 2769 resolution: 2651 2770 { ··· 2762 2881 integrity: sha512-wuxzZzQG8kvZndD7nustrNFIKYJ1jJoWIPaBpVe2+KHSvtzMi4SBjOxrigs8qeqce/l3U0cwiC+VAkLKSunHQQ==, 2763 2882 } 2764 2883 2884 + simple-git-hooks@2.13.0: 2885 + resolution: 2886 + { 2887 + integrity: sha512-N+goiLxlkHJlyaYEglFypzVNMaNplPAk5syu0+OPp/Bk6dwVoXF6FfOw2vO0Dp+JHsBaI+w6cm8TnFl2Hw6tDA==, 2888 + } 2889 + hasBin: true 2890 + 2765 2891 simple-swizzle@0.2.2: 2766 2892 resolution: 2767 2893 { ··· 2849 2975 } 2850 2976 engines: { node: ">=12" } 2851 2977 2852 - strnum@1.1.2: 2978 + strnum@2.1.1: 2853 2979 resolution: 2854 2980 { 2855 - integrity: sha512-vrN+B7DBIoTTZjnPNewwhx6cBA/H+IS7rfW68n7XxC1y7uoiGQBxaKzqucGUgavX15dJgiGztLJ8vxuEzwqBdA==, 2981 + integrity: sha512-7ZvoFTiCnGxBtDqJ//Cu6fWtZtc7Y3x+QOirG15wztbdngGSkht27o2pyGWrVy0b4WAy3jbKmnoK6g5VlVNUUw==, 2856 2982 } 2857 2983 2858 2984 style-to-js@1.1.16: ··· 3021 3147 resolution: 3022 3148 { 3023 3149 integrity: sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ==, 3150 + } 3151 + 3152 + unist-util-flatmap@1.0.0: 3153 + resolution: 3154 + { 3155 + integrity: sha512-IG32jcKJlhARCYT2LsYPJWdoXYkzz3ESAdl1aa2hn9Auh+cgUmU6wgkII4yCc/1GgeWibRdELdCZh/p3QKQ1dQ==, 3024 3156 } 3025 3157 3026 3158 unist-util-is@6.0.0: ··· 3475 3607 } 3476 3608 engines: { node: ">=12" } 3477 3609 3610 + yocto-queue@0.1.0: 3611 + resolution: 3612 + { 3613 + integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==, 3614 + } 3615 + engines: { node: ">=10" } 3616 + 3478 3617 yocto-queue@1.2.1: 3479 3618 resolution: 3480 3619 { ··· 3539 3678 3540 3679 "@astrojs/compiler@2.12.0": {} 3541 3680 3681 + "@astrojs/compiler@2.12.2": {} 3682 + 3542 3683 "@astrojs/internal-helpers@0.6.1": {} 3543 3684 3544 3685 "@astrojs/language-server@2.15.4(prettier-plugin-astro@0.14.1)(prettier@3.5.3)(typescript@5.8.3)": ··· 3567 3708 transitivePeerDependencies: 3568 3709 - typescript 3569 3710 3570 - "@astrojs/markdown-remark@6.3.1": 3711 + "@astrojs/markdown-remark@6.3.2": 3571 3712 dependencies: 3572 3713 "@astrojs/internal-helpers": 0.6.1 3573 - "@astrojs/prism": 3.2.0 3714 + "@astrojs/prism": 3.3.0 3574 3715 github-slugger: 2.0.0 3575 3716 hast-util-from-html: 2.0.3 3576 3717 hast-util-to-text: 4.0.2 ··· 3593 3734 transitivePeerDependencies: 3594 3735 - supports-color 3595 3736 3596 - "@astrojs/mdx@4.2.6(astro@5.7.13(@types/node@22.15.20)(rollup@4.41.0)(typescript@5.8.3)(yaml@2.8.0))": 3737 + "@astrojs/mdx@4.3.0(astro@5.11.0(@types/node@22.15.20)(rollup@4.41.0)(typescript@5.8.3)(yaml@2.8.0))": 3597 3738 dependencies: 3598 - "@astrojs/markdown-remark": 6.3.1 3739 + "@astrojs/markdown-remark": 6.3.2 3599 3740 "@mdx-js/mdx": 3.1.0(acorn@8.14.1) 3600 3741 acorn: 8.14.1 3601 - astro: 5.7.13(@types/node@22.15.20)(rollup@4.41.0)(typescript@5.8.3)(yaml@2.8.0) 3742 + astro: 5.11.0(@types/node@22.15.20)(rollup@4.41.0)(typescript@5.8.3)(yaml@2.8.0) 3602 3743 es-module-lexer: 1.7.0 3603 3744 estree-util-visit: 2.0.0 3604 3745 hast-util-to-html: 9.0.5 ··· 3612 3753 transitivePeerDependencies: 3613 3754 - supports-color 3614 3755 3615 - "@astrojs/prism@3.2.0": 3756 + "@astrojs/prism@3.3.0": 3616 3757 dependencies: 3617 3758 prismjs: 1.30.0 3618 3759 3619 - "@astrojs/rss@4.0.11": 3760 + "@astrojs/rss@4.0.12": 3620 3761 dependencies: 3621 - fast-xml-parser: 4.5.3 3762 + fast-xml-parser: 5.2.3 3622 3763 kleur: 4.1.5 3623 3764 3624 - "@astrojs/sitemap@3.4.0": 3765 + "@astrojs/sitemap@3.4.1": 3625 3766 dependencies: 3626 3767 sitemap: 8.0.0 3627 3768 stream-replace-string: 2.0.0 3628 3769 zod: 3.25.7 3629 3770 3630 - "@astrojs/telemetry@3.2.1": 3771 + "@astrojs/telemetry@3.3.0": 3631 3772 dependencies: 3632 3773 ci-info: 4.2.0 3633 3774 debug: 4.4.1 ··· 4031 4172 4032 4173 "@types/sax@1.2.7": 4033 4174 dependencies: 4034 - "@types/node": 17.0.45 4175 + "@types/node": 22.15.20 4176 + 4177 + "@types/ungap__structured-clone@1.2.0": {} 4035 4178 4036 4179 "@types/unist@2.0.11": {} 4037 4180 ··· 4131 4274 4132 4275 astring@1.9.0: {} 4133 4276 4134 - astro@5.7.13(@types/node@22.15.20)(rollup@4.41.0)(typescript@5.8.3)(yaml@2.8.0): 4277 + astro@5.11.0(@types/node@22.15.20)(rollup@4.41.0)(typescript@5.8.3)(yaml@2.8.0): 4135 4278 dependencies: 4136 - "@astrojs/compiler": 2.12.0 4279 + "@astrojs/compiler": 2.12.2 4137 4280 "@astrojs/internal-helpers": 0.6.1 4138 - "@astrojs/markdown-remark": 6.3.1 4139 - "@astrojs/telemetry": 3.2.1 4281 + "@astrojs/markdown-remark": 6.3.2 4282 + "@astrojs/telemetry": 3.3.0 4140 4283 "@capsizecss/unpack": 2.4.0 4141 4284 "@oslojs/encoding": 1.1.0 4142 4285 "@rollup/pluginutils": 5.1.4(rollup@4.41.0) ··· 4163 4306 github-slugger: 2.0.0 4164 4307 html-escaper: 3.0.3 4165 4308 http-cache-semantics: 4.2.0 4309 + import-meta-resolve: 4.1.0 4166 4310 js-yaml: 4.1.0 4167 4311 kleur: 4.1.5 4168 4312 magic-string: 0.30.17 ··· 4483 4627 4484 4628 fast-uri@3.0.6: {} 4485 4629 4486 - fast-xml-parser@4.5.3: 4630 + fast-xml-parser@5.2.3: 4487 4631 dependencies: 4488 - strnum: 1.1.2 4632 + strnum: 2.1.1 4489 4633 4490 4634 fastq@1.19.1: 4491 4635 dependencies: ··· 4499 4643 dependencies: 4500 4644 to-regex-range: 5.0.1 4501 4645 4646 + find-up@5.0.0: 4647 + dependencies: 4648 + locate-path: 6.0.0 4649 + path-exists: 4.0.0 4650 + 4502 4651 flattie@1.1.1: {} 4503 4652 4504 4653 fontace@0.3.0: ··· 4520 4669 4521 4670 fsevents@2.3.3: 4522 4671 optional: true 4672 + 4673 + gemoji@4.2.1: {} 4523 4674 4524 4675 get-caller-file@2.0.5: {} 4525 4676 ··· 4562 4713 vfile: 6.0.3 4563 4714 vfile-location: 5.0.3 4564 4715 web-namespaces: 2.0.1 4716 + 4717 + hast-util-is-element@1.1.0: {} 4565 4718 4566 4719 hast-util-is-element@3.0.0: 4567 4720 dependencies: ··· 4676 4829 html-void-elements@3.0.0: {} 4677 4830 4678 4831 http-cache-semantics@4.2.0: {} 4832 + 4833 + ignore@7.0.5: {} 4679 4834 4680 4835 import-meta-resolve@4.1.0: {} 4681 4836 ··· 4737 4892 dependencies: 4738 4893 uc.micro: 2.1.0 4739 4894 4895 + locate-path@6.0.0: 4896 + dependencies: 4897 + p-locate: 5.0.0 4898 + 4740 4899 lodash@4.17.21: {} 4741 4900 4742 4901 longest-streak@3.1.0: {} ··· 4936 5095 mdast-util-to-string@4.0.0: 4937 5096 dependencies: 4938 5097 "@types/mdast": 4.0.4 5098 + 5099 + mdast-util-toc@7.1.0: 5100 + dependencies: 5101 + "@types/mdast": 4.0.4 5102 + "@types/ungap__structured-clone": 1.2.0 5103 + "@ungap/structured-clone": 1.3.0 5104 + github-slugger: 2.0.0 5105 + mdast-util-to-string: 4.0.0 5106 + unist-util-is: 6.0.0 5107 + unist-util-visit: 5.0.0 4939 5108 4940 5109 mdn-data@2.12.2: {} 4941 5110 ··· 5212 5381 braces: 3.0.3 5213 5382 picomatch: 2.3.1 5214 5383 5384 + mri@1.2.0: {} 5385 + 5215 5386 mrmime@2.0.1: {} 5216 5387 5217 5388 ms@2.1.3: {} ··· 5252 5423 regex: 6.0.1 5253 5424 regex-recursion: 6.0.2 5254 5425 5426 + p-limit@3.1.0: 5427 + dependencies: 5428 + yocto-queue: 0.1.0 5429 + 5255 5430 p-limit@6.2.0: 5256 5431 dependencies: 5257 5432 yocto-queue: 1.2.1 5433 + 5434 + p-locate@5.0.0: 5435 + dependencies: 5436 + p-limit: 3.1.0 5258 5437 5259 5438 p-queue@8.1.0: 5260 5439 dependencies: ··· 5292 5471 5293 5472 path-browserify@1.0.1: {} 5294 5473 5474 + path-exists@4.0.0: {} 5475 + 5295 5476 picocolors@1.1.1: {} 5296 5477 5297 5478 picomatch@2.3.1: {} ··· 5315 5496 5316 5497 prettier@3.5.3: {} 5317 5498 5499 + pretty-quick@4.1.1(prettier@3.5.3): 5500 + dependencies: 5501 + find-up: 5.0.0 5502 + ignore: 7.0.5 5503 + mri: 1.2.0 5504 + picocolors: 1.1.1 5505 + picomatch: 4.0.2 5506 + prettier: 3.5.3 5507 + tinyexec: 0.3.2 5508 + tslib: 2.8.1 5509 + 5318 5510 prismjs@1.30.0: {} 5319 5511 5320 5512 prompts@2.4.2: ··· 5374 5566 dependencies: 5375 5567 regex-utilities: 2.3.0 5376 5568 5569 + rehype-accessible-emojis@0.3.2: 5570 + dependencies: 5571 + emoji-regex: 8.0.0 5572 + gemoji: 4.2.1 5573 + hast-util-is-element: 1.1.0 5574 + unist-util-flatmap: 1.0.0 5575 + 5377 5576 rehype-parse@9.0.1: 5378 5577 dependencies: 5379 5578 "@types/hast": 3.0.4 ··· 5455 5654 mdast-util-to-markdown: 2.1.2 5456 5655 unified: 11.0.5 5457 5656 5657 + remark-toc@9.0.0: 5658 + dependencies: 5659 + "@types/mdast": 4.0.4 5660 + mdast-util-toc: 7.1.0 5661 + 5458 5662 request-light@0.5.8: {} 5459 5663 5460 5664 request-light@0.7.0: {} ··· 5570 5774 "@shikijs/vscode-textmate": 10.0.2 5571 5775 "@types/hast": 3.0.4 5572 5776 5777 + simple-git-hooks@2.13.0: {} 5778 + 5573 5779 simple-swizzle@0.2.2: 5574 5780 dependencies: 5575 5781 is-arrayish: 0.3.2 ··· 5619 5825 dependencies: 5620 5826 ansi-regex: 6.1.0 5621 5827 5622 - strnum@1.1.2: {} 5828 + strnum@2.1.1: {} 5623 5829 5624 5830 style-to-js@1.1.16: 5625 5831 dependencies: ··· 5708 5914 "@types/unist": 3.0.3 5709 5915 unist-util-is: 6.0.0 5710 5916 5917 + unist-util-flatmap@1.0.0: {} 5918 + 5711 5919 unist-util-is@6.0.0: 5712 5920 dependencies: 5713 5921 "@types/unist": 3.0.3 ··· 5963 6171 string-width: 4.2.3 5964 6172 y18n: 5.0.8 5965 6173 yargs-parser: 21.1.1 6174 + 6175 + yocto-queue@0.1.0: {} 5966 6176 5967 6177 yocto-queue@1.2.1: {} 5968 6178
+140
posts/full-test.md
··· 1 + --- 2 + title: Full Test 3 + date: 3000-12-31 4 + colour: "#008282" 5 + image: "mc_map_art.png" 6 + --- 7 + 8 + ## this is 9 + 10 + ### simply 11 + 12 + #### a 13 + 14 + ##### test 15 + 16 + ###### i wouldnt use absurd headings in the wild, just h2 MAYBE h3 17 + 18 + look we r testinng!! 19 + 20 + ## normie syntax 21 + 22 + ### Paragraphs 23 + 24 + here is a paragraph 25 + 26 + and here is another 27 + 28 + Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos. 29 + 30 + ::breakout::test:: Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos. 31 + 32 + ::full-width::bg-red::@div::@nest:: Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos. 33 + 34 + ::full-width::bg-orange::@div::@nest:: Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos. 35 + 36 + ::full-width::bg-yellow::@div::@nest:: Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos. 37 + 38 + ::full-width::bg-green::@div::@nest:: Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos. 39 + 40 + ::full-width::bg-blue::@div::@nest:: Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos. 41 + 42 + ::full-width::bg-purple::@div::@nest:: Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos. 43 + 44 + ::breakout:: ![small left | ](./assets/minecraft.png) Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos. 45 + 46 + Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos. 47 + 48 + paragraph with **_strong emph_**, **\*strong** in emph\*, **_emph_ in strong**, **in strong _emph_**, and \*in emph **strong\*** but not ~~this text~~ bc i said so 49 + 50 + now heres a paragraph with a [link](/blog "Goes to blog") which goes to blog, [a link](https://vielle.dev/) which points to my site, [an unvisited link (DONT CLICK)](https://thissitedoesnotexist.com/fake_page), and a link to <https://deer.social> 51 + 52 + ### Code 53 + 54 + ```html 55 + <h1>Code</h1> 56 + <p>Theres more code</p> 57 + <!-- and then some more --> 58 + wow it just keeps going 59 + <style> 60 + this { 61 + size: long; 62 + } 63 + </style> 64 + ``` 65 + 66 + ``` 67 + This is code with a really really really really really really really really really really really really really really really really really really really really really really really really really really really really really really really long line 68 + ``` 69 + 70 + ``` 71 + tinycode 72 + ``` 73 + 74 + heres sopme `inline` code 75 + 76 + ### Tables 77 + 78 + | this | is | 79 + | ---- | :---: | 80 + | a | table | 81 + | a | table | 82 + | a | table | 83 + | a | table | 84 + 85 + ### Blockquotes 86 + 87 + > and this, a block quote 88 + > which is multiline 89 + 90 + > and heres another 91 + 92 + ### Lists 93 + 94 + #### Unordered 95 + 96 + - an unordered list 97 + - with nesting 98 + - and more 99 + - going back 100 + - again 101 + 102 + #### Ordered 103 + 104 + 1. an unordered list 105 + 2. which has 106 + 1. nesting 107 + 2. high numbers 108 + 109 + #### Check 110 + 111 + - [ ] make this look good 112 + - [ ] make `/blog` look good 113 + - [x] oh yeah that was an inline code ๐Ÿ’ช 114 + 115 + ## fancy syntax 116 + 117 + ### blockquote flags 118 + 119 + > $NOTE 120 + > This one is a note 121 + 122 + > $ALERT 123 + > This one is another keyword 124 + 125 + > $NOTE 126 + > this can be achived by adding `$FLAG` to the first line of a blockquote (make sure to add the double space) 127 + 128 + ### Image 129 + 130 + ![An unimportiant image](./assets/minecraft.png) 131 + 132 + ![small left | An unimportiant image](./assets/minecraft.png) heres an image which should be on the right while this text wraps around it? i hope? make sure the image goes first tho !!!! 133 + 134 + > $INFO 135 + > image flags go in alt text 136 + > if alt text has a pipe (|), there are flags 137 + > all words up to the first pipe are treated as flags in a space seperated list 138 + > they are added to the data tag as `[data-img-flag--<name>="true"]` 139 + 140 + ![small centre | An unimportiant image](./assets/minecraft.png)
+1 -7
posts/test.mdx
··· 2 2 title: Test Post 3 3 --- 4 4 5 - import Balloon from "@components/blog/balloon.svelte"; 6 - 7 5 whats this about dawg 8 6 9 - <div style="padding: 15rem;"></div> 10 - 11 - <div style="position: absolute"> 12 - <Balloon client:load id={0} boundingWidth={300} boundingHeight={0} /> 13 - </div> 7 + (Vi a vis .MDX)
+175
rehype-custom-html.ts
··· 1 + import type { Plugin } from "unified"; 2 + import type { 3 + Root, 4 + Element, 5 + Node, 6 + ElementContent, 7 + RootContent, 8 + Text, 9 + } from "hast"; 10 + type Options = {}; 11 + 12 + /* 13 + blockquote flags go in the first line 14 + they are formatted as: 15 + `$FLAG `, where `FLAG` is the flag name 16 + there can only be one flag per blockquote 17 + */ 18 + 19 + function blockquote(node: Element) { 20 + for (const child of node.children) { 21 + if (child.type === "element" && child.children[0].type === "text") { 22 + const flag = child.children[0].value.match(/(?<=^\$).*/gm); 23 + if (flag?.length !== 1) continue; 24 + child.children.shift(); 25 + // finiky to get types working bc shift mutation etc 26 + if ( 27 + (child.children[0] as Node).type === "element" && 28 + (child.children[0] as Node as Element).tagName === "br" 29 + ) 30 + child.children.shift(); 31 + 32 + node.properties[`data-bq-flag--${flag[0].toLowerCase()}`] = true; 33 + } 34 + } 35 + } 36 + 37 + /* 38 + image flags go in alt text 39 + if alt text has a pipe (|), there are flags 40 + all words up to the first pipe are treated as flags in a space seperated list 41 + they are added to the data tag as [data-img-flag--<name>="true"] 42 + */ 43 + 44 + function image(node: Element) { 45 + // get alt; throw error if missing; convert to string 46 + const alt = ( 47 + node.properties.alt ?? 48 + (() => { 49 + throw new Error("NO ALT TEXT!!!"); 50 + })() 51 + ).toString(); 52 + 53 + // match section before | 54 + const prefixes = alt.match(/.*?(?= \|.*)/gm); 55 + if (!prefixes) return; 56 + 57 + node.properties.alt = alt.match(/(?<= \| ).*/gm); 58 + const flags = prefixes[0].split(" "); 59 + for (const flag of flags) { 60 + node.properties[`data-img-flag--${flag}`] = true; 61 + } 62 + } 63 + 64 + /* 65 + paragraph flags go at the start 66 + to use paragraph flags, include the following syntax at the start of a paragraph: 67 + `::FLAG[::FLAG]*::`, where FLAG is a flag which is included as [data-para-flag--FLAG] 68 + if FLAG starts with an `@`, it will be treated as a directive 69 + current directives: 70 + - @nest: replaces children with a p tag and moves children into it 71 + - @div: replaces self with div 72 + */ 73 + 74 + function para(value: Text, parent: Element) { 75 + const flags = value.value.match(/(?<=^::).*(?=::)/gm); 76 + 77 + if (!flags) return; 78 + 79 + const txt = value.value.match(/(?<=^::.*:: ).*/gm); 80 + value.value = 81 + !txt || txt.length !== 1 ? "Err: Parser Error (custom HTML)" : txt[0]; 82 + 83 + for (const flag of flags[0].split("::")) { 84 + if (flag[0] === "@") { 85 + switch (flag.slice(1)) { 86 + case "nest": { 87 + const prevChildren = parent.children; 88 + parent.children = [ 89 + { 90 + type: "element", 91 + tagName: "p", 92 + properties: {}, 93 + children: prevChildren, 94 + } satisfies Element, 95 + ]; 96 + break; 97 + } 98 + case "div": { 99 + parent.tagName = "div"; 100 + break; 101 + } 102 + default: { 103 + console.warn("Unknown paragraph directive:", flag); 104 + } 105 + } 106 + } else parent.properties[`data-para-flag--${flag}`] = true; 107 + } 108 + } 109 + 110 + const forChild = (children: ElementContent[] | RootContent[]) => { 111 + for (const node of children) { 112 + if (node.type !== "element") continue; 113 + 114 + switch (node.tagName) { 115 + case "blockquote": { 116 + blockquote(node); 117 + break; 118 + } 119 + 120 + case "img": { 121 + image(node); 122 + break; 123 + } 124 + 125 + case "p": { 126 + if (node.children[0].type === "text") para(node.children[0], node); 127 + forChild(node.children); 128 + break; 129 + } 130 + 131 + case "pre": { 132 + if (node.properties["tabindex"]) node.properties["tabindex"] = "-1"; 133 + break; 134 + } 135 + 136 + case "ul": 137 + { 138 + node.children.forEach((x) => { 139 + if (x.type === "element" && x.tagName === "li") { 140 + const contents = x.children.map((y) => { 141 + if ( 142 + y.type === "element" && 143 + y.tagName === "input" && 144 + y.properties.type === "checkbox" 145 + ) 146 + y.properties["aria-label"] = y.properties.checked 147 + ? "Checked checkbox" 148 + : "Unchecked checkbox"; 149 + return y; 150 + }); 151 + 152 + x.children = [ 153 + { 154 + type: "element", 155 + tagName: "label", 156 + properties: {}, 157 + children: contents, 158 + }, 159 + ]; 160 + } 161 + }); 162 + } 163 + 164 + break; 165 + } 166 + } 167 + }; 168 + 169 + const plugin: Plugin<[Options], Root> = function (options) { 170 + return function (root, _) { 171 + forChild(root.children); 172 + }; 173 + }; 174 + 175 + export default plugin;
+4 -8
src/Base.astro
··· 1 1 --- 2 2 interface Props { 3 - title: string; 3 + title?: string; 4 4 dataset?: Record<string, any>; 5 5 [key: string]: any; 6 6 } ··· 27 27 <meta name="viewport" content="width=device-width" /> 28 28 <meta name="generator" content={Astro.generator} /> 29 29 <link rel="sitemap" href="/sitemap-index.xml" /> 30 - <title>{title} | vielle.dev</title> 30 + <title>{title ? `${title} | vielle.dev` : "vielle.dev"}</title> 31 31 <script> 32 32 // sets the timezone offset 33 - document.cookie = `timezone=${new Date().toString()}`; 33 + document.cookie = `timezone=${new Date().toString()};path=/`; 34 34 </script> 35 - <!-- default styles (rem, *) --> 35 + <!-- default styles --> 36 36 <style is:global> 37 37 @layer reset { 38 - :root { 39 - font-size: 62.5%; 40 - } 41 38 body { 42 - font-size: 1.6rem; 43 39 line-height: 1.5; 44 40 -webkit-font-smoothing: antialiased; 45 41 font-family: sans-serif;
+17
src/assets/arrow-down.svg
··· 1 + <?xml version="1.0" encoding="UTF-8" standalone="no"?> 2 + <svg 3 + width="10" 4 + height="10" 5 + viewBox="0 0 10 10" 6 + version="1.1" 7 + id="svg1" 8 + xmlns="http://www.w3.org/2000/svg" 9 + xmlns:svg="http://www.w3.org/2000/svg" 10 + stroke="black" 11 + stroke-width="2px" 12 + fill="none" 13 + > 14 + <path 15 + d="m 7.8145673,2.2064799 c 0,0 -2.0655083,4.998245 -2.5000003,4.998245 -0.434492,0 -2.4999997,-4.998245 -2.4999997,-4.998245" 16 + /> 17 + </svg>
+17
src/assets/arrow-right.svg
··· 1 + <?xml version="1.0" encoding="UTF-8" standalone="no"?> 2 + <svg 3 + width="10" 4 + height="10" 5 + viewBox="0 0 10 10" 6 + version="1.1" 7 + id="svg1" 8 + xmlns="http://www.w3.org/2000/svg" 9 + xmlns:svg="http://www.w3.org/2000/svg" 10 + stroke="black" 11 + stroke-width="2px" 12 + fill="none" 13 + > 14 + <path 15 + d="m 2.8919737,2.1290735 c 0,0 4.998245,2.0655083 4.998245,2.5000003 0,0.434492 -4.998245,2.4999997 -4.998245,2.4999997" 16 + /> 17 + </svg>
-73
src/assets/arrow.svg
··· 1 - <svg 2 - width="100" 3 - height="20" 4 - viewBox="0 0 26.458333 5.2916666" 5 - class="{className}" 6 - style="{style}" 7 - > 8 - <defs id="arrow"> 9 - <marker 10 - id="DartArrow" 11 - refX="0" 12 - refY="0" 13 - orient="auto-start-reverse" 14 - markerWidth="0.75" 15 - markerHeight="0.75" 16 - viewBox="0 0 1 1" 17 - preserveAspectRatio="xMidYMid" 18 - markerUnits="strokeWidth" 19 - > 20 - <path 21 - d="M 0,0 5,-5 -12.5,0 5,5 Z" 22 - transform="scale(-0.5)" 23 - id="path6" 24 - ></path> 25 - </marker> 26 - </defs> 27 - <g 28 - id="layer1" 29 - transform="matrix(0.8611475,0.06332518,-0.05355696,1.0182114,1.9248292,-1.3174538)" 30 - > 31 - <path 32 - d="m 0.24644479,2.3103471 c 0,0 4.07342911,2.4441516 11.16257621,2.4441516 7.089149,0 11.162682,-2.4441342 11.162682,-2.4441342" 33 - id="path1" 34 - ></path> 35 - </g> 36 - </svg> 37 - 38 - <style> 39 - .line { 40 - position: absolute; 41 - display: block; 42 - 43 - width: var(--width); 44 - height: calc(var(--width) / 5); 45 - 46 - transform: rotate(var(--angle)) scaleY(var(--scaleY, 1)); 47 - transform-origin: top left; 48 - 49 - top: var(--y); 50 - left: var(--x); 51 - 52 - stroke-width: 0.957978; 53 - stroke-dasharray: none; 54 - marker-end: url(#DartArrow); 55 - 56 - stroke: var(--colour); 57 - filter: drop-shadow(0 0 0.25rem black); 58 - fill: none; 59 - 60 - .location:hover & { 61 - filter: drop-shadow(0 0 0.5rem var(--colour)); 62 - } 63 - } 64 - 65 - #arrow marker { 66 - overflow: visible; 67 - } 68 - #arrow path { 69 - fill: context-stroke; 70 - fill-rule: evenodd; 71 - stroke: none; 72 - } 73 - </style>
-13
src/assets/balloon-glint.svg
··· 1 - <svg 2 - xmlns="http://www.w3.org/2000/svg" 3 - width="210mm" 4 - height="297mm" 5 - viewBox="0 0 210 297" 6 - > 7 - <g> 8 - <path 9 - style="fill: #ffffff" 10 - d="M 9.2929728,1.5456421 C 5.8272482,2.0424561 2.9819023,4.5085073 1.7466634,7.8119263 l 2.361613,1.0051066 C 5.0089649,6.2523257 7.1627996,4.4036314 9.7544434,4.0757202 9.7129319,3.7094167 9.64465,3.2547364 9.5239665,2.6814901 9.4170573,2.1736713 9.3470099,1.8242883 9.2929728,1.5456421 Z" 11 - /> 12 - </g> 13 - </svg>
+16
src/assets/check.svg
··· 1 + <svg 2 + xmlns="http://www.w3.org/2000/svg" 3 + width="24" 4 + height="24" 5 + viewBox="0 0 24 24" 6 + fill="none" 7 + stroke="white" 8 + stroke-width="2" 9 + stroke-linecap="round" 10 + stroke-linejoin="round" 11 + class="lucide lucide-check-icon lucide-check" 12 + > 13 + <desc><!-- no alt text as descriptive --></desc> 14 + 15 + <path d="M20 6 9 17l-5-5" /> 16 + </svg>
+18
src/assets/copy.svg
··· 1 + <svg 2 + xmlns="http://www.w3.org/2000/svg" 3 + width="18" 4 + height="18" 5 + viewBox="0 0 18 18" 6 + fill="none" 7 + stroke="currentColor" 8 + stroke-width="2" 9 + stroke-linecap="round" 10 + stroke-linejoin="round" 11 + class="lucide lucide-copy-icon lucide-copy" 12 + > 13 + <title>Copy text</title> 14 + <rect width="10" height="10" x="6" y="6" rx="2" ry="2" /> 15 + <path 16 + d="m 3,12 c -1,0 -1.5,-0.5 -1.5,-1.5 V 3 c 0,-1 0.5,-1.5 1.5,-1.5 h 7 c 0.8,0 1.5,0.5 1.5,1.5" 17 + /> 18 + </svg>
+1 -2
src/assets/hamburger.svg
··· 9 9 stroke-linecap="round" 10 10 stroke-linejoin="round" 11 11 class="lucide lucide-menu-icon lucide-menu" 12 - title="" 13 12 > 14 - <title><!-- no alt text as used for buttons --></title> 13 + <title>Menu</title> 15 14 <path d="M4 12h16" /> 16 15 <path d="M4 18h16" /> 17 16 <path d="M4 6h16" />
src/assets/mc_blue_banner.png

This is a binary file and will not be displayed.

src/assets/mc_cyan_banner.png

This is a binary file and will not be displayed.

src/assets/mc_magenta_banner.png

This is a binary file and will not be displayed.

src/assets/mc_map.png

This is a binary file and will not be displayed.

src/assets/mc_map_contents.png

This is a binary file and will not be displayed.

src/assets/mc_red_banner.png

This is a binary file and will not be displayed.

+15
src/assets/moon.svg
··· 1 + <svg 2 + xmlns="http://www.w3.org/2000/svg" 3 + width="24" 4 + height="24" 5 + viewBox="0 0 24 24" 6 + fill="none" 7 + stroke="currentColor" 8 + stroke-width="2" 9 + stroke-linecap="round" 10 + stroke-linejoin="round" 11 + class="lucide lucide-moon-icon lucide-moon" 12 + > 13 + <desc><!-- no alt text as descriptive --></desc> 14 + <path d="M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z" /> 15 + </svg>
+1
src/assets/rss.svg
··· 4 4 viewBox="0 0 256 256" 5 5 xmlns="http://www.w3.org/2000/svg" 6 6 > 7 + <desc><!-- no alt text as descriptive --></desc> 7 8 <circle 8 9 cx="35.10857" 9 10 cy="220.89143"
+23
src/assets/sun.svg
··· 1 + <svg 2 + xmlns="http://www.w3.org/2000/svg" 3 + width="24" 4 + height="24" 5 + viewBox="0 0 24 24" 6 + fill="none" 7 + stroke="currentColor" 8 + stroke-width="2" 9 + stroke-linecap="round" 10 + stroke-linejoin="round" 11 + class="lucide lucide-sun-icon lucide-sun" 12 + > 13 + <desc><!-- no alt text as descriptive --></desc> 14 + <circle cx="12" cy="12" r="4" /> 15 + <path d="M12 2v2" /> 16 + <path d="M12 20v2" /> 17 + <path d="m4.93 4.93 1.41 1.41" /> 18 + <path d="m17.66 17.66 1.41 1.41" /> 19 + <path d="M2 12h2" /> 20 + <path d="M20 12h2" /> 21 + <path d="m6.34 17.66-1.41 1.41" /> 22 + <path d="m19.07 4.93-1.41 1.41" /> 23 + </svg>
-17
src/assets/x.svg
··· 1 - <svg 2 - xmlns="http://www.w3.org/2000/svg" 3 - width="24" 4 - height="24" 5 - viewBox="0 0 24 24" 6 - fill="none" 7 - stroke="currentColor" 8 - stroke-width="2" 9 - stroke-linecap="round" 10 - stroke-linejoin="round" 11 - class="lucide lucide-x-icon lucide-x" 12 - title="" 13 - > 14 - <title><!-- no alt text as used for buttons --></title> 15 - <path d="M18 6 6 18" /> 16 - <path d="m6 6 12 12" /> 17 - </svg>
+7 -7
src/components/blog/Background.astro
··· 12 12 Astro.cookies.get("timezone")?.value ?? 13 13 Astro.request.headers.get("Date") ?? 14 14 Astro.request.headers.get("date") ?? // idk if it cares about capitals so Both 15 - Date.now() 15 + Date.now(), 16 16 ); 17 17 18 18 const accurateHours = ··· 65 65 <div 66 66 id="background" 67 67 data-time={daytime ? "day" : "night"} 68 - style={`--palette-sky-day: ${blog.palette.sky.day}; 69 - --palette-sky-night: ${blog.palette.sky.night}; 70 - --palette-sun: ${blog.palette.sun}; 71 - --palette-moon: ${blog.palette.moon}; 72 - --palette-cloud: ${blog.palette.clouds}; 73 - --palette-star: ${blog.palette.stars}`} 68 + style={`--palette-sky-day: ${blog.palette.environment.sky.day}; 69 + --palette-sky-night: ${blog.palette.environment.sky.night}; 70 + --palette-sun: ${blog.palette.environment.sun}; 71 + --palette-moon: ${blog.palette.environment.moon}; 72 + --palette-cloud: ${blog.palette.environment.clouds}; 73 + --palette-star: ${blog.palette.environment.stars}`} 74 74 aria-hidden="true" 75 75 > 76 76 {
+21 -24
src/components/blog/Balloon.astro
··· 17 17 18 18 <div 19 19 class="cable" 20 - style={`--length: ${length}rem; 20 + style={`--length: ${length}px; 21 21 --id: ${id}; 22 22 --of: ${of}; 23 - --offset: ${offset}rem; 23 + --offset: ${offset}px; 24 24 ${rotation.map((x, i) => `--rot-${i}: ${x}deg;`).join(" ")} 25 25 --timing: ${utils.getRandom(blog.balloons.timing)}s; 26 26 `} 27 27 > 28 28 <div 29 29 class="balloon" 30 - style={`--width: ${utils.getRandom(blog.balloons.size[0])}rem; 31 - --height: ${utils.getRandom(blog.balloons.size[1])}rem;`} 30 + style={`--width: ${utils.getRandom(blog.balloons.size[0])}px; 31 + --height: ${utils.getRandom(blog.balloons.size[1])}px;`} 32 32 tabindex="-1" 33 33 data-min-time={blog.balloons.time[0]} 34 34 data-max-time={blog.balloons.time[1]} ··· 41 41 42 42 <script> 43 43 const balloons = document.querySelectorAll(".balloon"); 44 - console.log(balloons); 45 44 balloons.forEach((el) => { 46 45 if (!(el instanceof HTMLElement)) return; 47 46 ··· 53 52 const postParent = el.parentElement?.parentElement; 54 53 if (!cableParent) throw new Error("No parent 1 level up!!!"); 55 54 if (!postParent) throw new Error("No parent 2 levels up!!!"); 56 - console.log("clicked! popping", el, "with post", postParent); 57 55 58 56 el.blur(); 59 57 ··· 63 61 { 64 62 duration: 100, 65 63 fill: "forwards", 66 - } 64 + }, 67 65 ).finished, 68 66 69 67 cableParent.animate( ··· 77 75 { 78 76 duration: 500, 79 77 fill: "forwards", 80 - } 78 + }, 81 79 ), 82 80 83 81 postParent.animate( 84 82 [ 85 83 {}, 86 84 { 87 - top: "calc(var(--x-offset-0) + 50rem)", 85 + top: "calc(var(--x-offset-0) + 500px)", 88 86 }, 89 87 ], 90 88 { 91 89 duration: 1000, 92 90 easing: "ease-in-out", 93 - } 91 + }, 94 92 ).finished, 95 93 ]).then(() => { 96 94 const duration = (mintime + Math.random() * (maxtime - mintime)) * 1000; ··· 105 103 duration, 106 104 fill: "forwards", 107 105 // easing: "ease-in", 108 - } 106 + }, 109 107 ); 110 108 111 109 cableParent.animate( ··· 122 120 { 123 121 duration, 124 122 fill: "forwards", 125 - } 123 + }, 126 124 ); 127 125 128 126 postParent.animate( 129 127 [ 130 128 { 131 - top: "calc(var(--x-offset-0) + 50rem)", 129 + top: "calc(var(--x-offset-0) + 500px)", 132 130 }, 133 131 {}, 134 132 ], ··· 136 134 duration, 137 135 fill: "forwards", 138 136 easing: "ease-in", 139 - } 137 + }, 140 138 ); 141 139 }); 142 140 }); ··· 233 231 .cable { 234 232 position: absolute; 235 233 236 - width: 0.5rem; 234 + width: 5px; 237 235 height: var(--length); 238 - border-radius: 0.25rem; 236 + border-radius: 2.5px; 239 237 background: black; 240 238 241 239 [data-time="night"] + * & { 242 240 background: #404040; 243 241 } 244 242 245 - /* .5rem accounts for border (z-index doesn't work) */ 246 243 z-index: -99; 247 244 top: calc(-1 * var(--length)); 248 245 left: calc( ··· 277 274 } 278 275 279 276 .cable-tie { 280 - width: 1.75rem; 281 - height: 0.5rem; 277 + width: 17.5px; 278 + height: 5px; 282 279 283 280 position: absolute; 284 - bottom: -0.25rem; 281 + bottom: -2.5px; 285 282 left: 50%; 286 283 translate: -50%; 287 284 z-index: 1; 288 285 289 - border-radius: 0.25rem; 286 + border-radius: 2.5px; 290 287 background-color: black; 291 288 292 289 [data-time="night"] + * & { ··· 295 292 } 296 293 297 294 .tie { 298 - width: 2rem; 299 - height: 2rem; 295 + width: 20px; 296 + height: 20px; 300 297 background-color: var(--colour); 301 298 clip-path: polygon(50% 0, 0 100%, 100% 100%); 302 299 position: absolute; 303 - bottom: -1rem; 300 + bottom: -10px; 304 301 left: 50%; 305 302 translate: -50%; 306 303 }
+148
src/components/blog/CodeHeading.astro
··· 1 + --- 2 + import Copy from "@/assets/copy.svg"; 3 + 4 + interface Props { 5 + colours: { text: string; border: string }; 6 + } 7 + 8 + const { colours } = Astro.props; 9 + --- 10 + 11 + <template id="code-heading"> 12 + <span class="lang"><slot is:inline>.txt</slot></span> 13 + <span id="copied" style="visibility:hidden" role="alert">Copied!</span> 14 + <button id="copy"><Copy /></button> 15 + 16 + <!-- define:vars didnt work :( --> 17 + <style 18 + set:html={` 19 + :host { 20 + --text: ${colours.text}; 21 + --border: ${colours.border}; 22 + } 23 + `} 24 + ></style> 25 + 26 + <style> 27 + @keyframes teeter { 28 + from, 29 + to { 30 + rotate: 0deg; 31 + } 32 + 25% { 33 + rotate: 15deg; 34 + } 35 + 75% { 36 + rotate: -15deg; 37 + } 38 + } 39 + 40 + .lang { 41 + color: var(--text); 42 + } 43 + 44 + button { 45 + border: none; 46 + background: none; 47 + aspect-ratio: 1; 48 + border-radius: 100%; 49 + 50 + display: flex; 51 + align-items: center; 52 + justify-content: center; 53 + 54 + &:hover, 55 + &:focus { 56 + scale: 1.2; 57 + outline: none; 58 + background-color: #ffffff20; 59 + } 60 + 61 + &:active { 62 + scale: 1.4; 63 + animation: teeter 0.2s; 64 + } 65 + 66 + & svg { 67 + stroke: var(--text); 68 + margin: 2px; 69 + } 70 + } 71 + 72 + :host { 73 + display: flex block; 74 + justify-content: space-between; 75 + align-items: center; 76 + /* gets overridden by * because why not ig */ 77 + padding: 5px 15px !important; 78 + position: sticky; 79 + top: 0; 80 + left: 0; 81 + border-bottom: 4px solid var(--border); 82 + user-select: none; 83 + } 84 + </style> 85 + </template> 86 + 87 + <script> 88 + class CodeHeading extends HTMLElement { 89 + contents = ""; 90 + static observedAttributes = ["contents"]; 91 + 92 + template: HTMLTemplateElement; 93 + content: DocumentFragment; 94 + shadowRoot: ShadowRoot; 95 + 96 + constructor() { 97 + super(); 98 + const template = document.getElementById("code-heading"); 99 + if (!template || !(template instanceof HTMLTemplateElement)) 100 + throw new Error("Could not get #code-heading"); 101 + this.template = template; 102 + this.content = template.content; 103 + 104 + this.shadowRoot = this.attachShadow({ mode: "open" }); 105 + this.shadowRoot.appendChild(this.content.cloneNode(true)); 106 + 107 + const copy = this.shadowRoot.getElementById("copy"); 108 + if (!copy) throw new Error("No #copy in #code-heading"); 109 + 110 + const copied = this.shadowRoot.getElementById("copied"); 111 + if (!copied) throw new Error("No #copied in #code-heading"); 112 + 113 + const copied_animation = { 114 + opacity: [0, 1], 115 + visibility: ["hidden", "visible"], 116 + }; 117 + 118 + copy.addEventListener("click", () => { 119 + navigator.clipboard 120 + .writeText(this.contents) 121 + .catch((e) => { 122 + console.error("Encountered error copying to clipboard;", e); 123 + }) 124 + .then(async () => { 125 + await copied.animate(copied_animation, { 126 + duration: 200, 127 + fill: "forwards", 128 + }).finished; 129 + 130 + copied.animate(copied_animation, { 131 + duration: 200, 132 + delay: 2000, 133 + fill: "forwards", 134 + direction: "reverse", 135 + }); 136 + }); 137 + }); 138 + } 139 + 140 + attributeChangedCallback(name: string, _: any, newV?: string) { 141 + if (name == "contents") { 142 + this.contents = newV ?? ""; 143 + } 144 + } 145 + } 146 + 147 + customElements.define("code-heading", CodeHeading); 148 + </script>
+17 -17
src/components/blog/Post.astro
··· 17 17 if (!data.image) return; 18 18 const img = data.image.match(/.*(?=\.png)/gm); 19 19 if (img === null) return; 20 - return await import(`../../posts/assets/${img[0]}.png`).then( 21 - (x) => x.default 20 + return await import(`../../content/posts/assets/${img[0]}.png`).then( 21 + (x) => x.default, 22 22 ); 23 23 })(); 24 24 ··· 61 61 ${offsets 62 62 .map( 63 63 (x, i) => 64 - `--x-offset-${i}: calc((100svw - ${blog.post.width + 2 * blog.post.xPadding}rem) * ${positions[0] + x[0]} + ${blog.post.xPadding}rem); 65 - --y-offset-${i}: ${blog.post.yLeeway * 2 * (positions[1] + x[1]) - blog.post.yLeeway}rem;` 64 + `--x-offset-${i}: calc((100svw - ${blog.post.width + 2 * blog.post.xPadding}px) * ${positions[0] + x[0]} + ${blog.post.xPadding}px); 65 + --y-offset-${i}: ${blog.post.yLeeway * 2 * (positions[1] + x[1]) - blog.post.yLeeway}px;`, 66 66 ) 67 67 .join("\n")} 68 68 ··· 73 73 74 74 /* config */ 75 75 76 - --width: ${blog.post.width}rem; 77 - --y-gap: ${blog.post.yGap}rem; 76 + --width: ${blog.post.width}px; 77 + --y-gap: ${blog.post.yGap}px; 78 78 `} 79 79 > 80 80 { ··· 172 172 173 173 section { 174 174 width: var(--width); 175 - padding: 1rem; 175 + padding: 10px; 176 176 margin-bottom: var(--y-gap); 177 177 178 178 position: relative; ··· 185 185 content: "" / ""; 186 186 display: block; 187 187 position: absolute; 188 - top: -0.5rem; 189 - left: -0.5rem; 188 + top: -5px; 189 + left: -5px; 190 190 z-index: -2; 191 191 192 - width: calc(var(--width) + 1rem); 193 - height: calc(100% + 1rem); 192 + width: calc(var(--width) + 10px); 193 + height: calc(100% + 10px); 194 194 195 195 background-color: white; 196 - border: 0.5rem solid var(--colour, dodgerblue); 197 - border-radius: 2.5rem; 196 + border: 5px solid var(--colour, dodgerblue); 197 + border-radius: 25px; 198 198 199 - box-shadow: 0 0 7.5rem var(--box-shadow-colour, #00000080); 199 + box-shadow: 0 0 75px var(--box-shadow-colour, #00000080); 200 200 } 201 201 202 202 /* default, overridden by reduced motion */ ··· 217 217 } 218 218 219 219 & > img { 220 - border-radius: 1.5rem; 220 + border-radius: 15px; 221 221 222 - width: 30rem; 223 - height: 20rem; 222 + width: 300px; 223 + height: 200px; 224 224 object-fit: cover; 225 225 } 226 226 }
+11 -7
src/components/blog/background/Cloud.astro
··· 36 36 37 37 const vectorOffset = ( 38 38 v: [number, number], 39 - o: [number, number] 39 + o: [number, number], 40 40 ): [number, number] => { 41 41 return [o[0] - v[0], o[1] - v[1]]; 42 42 }; ··· 89 89 ]; 90 90 const newDistance = Math.sqrt( 91 91 (newCenter[0] - p.origin[0]) ** 2 + 92 - (newCenter[1] - p.origin[1]) ** 2 92 + (newCenter[1] - p.origin[1]) ** 2, 93 93 ); 94 94 return { 95 95 origin: vectorOffset(vector(ang, c), p.origin), ··· 120 120 prev: 0, 121 121 output: [] as any[], 122 122 complete: false, 123 - } 123 + }, 124 124 ).output 125 125 } 126 126 <circle cx={r} cy={r} r={r}></circle> ··· 133 133 y1="0" 134 134 y2="1" 135 135 > 136 - <stop offset="0%" stop-color={blog.palette.clouds}></stop> 136 + <stop offset="0%" stop-color={blog.palette.environment.clouds}></stop> 137 137 <stop 138 138 offset={`${blog.background.clouds.gradientStops[0]}%`} 139 - stop-color={blog.palette.clouds}></stop> 139 + stop-color={blog.palette.environment.clouds}></stop> 140 140 <stop 141 141 offset={`${blog.background.clouds.gradientStops[1]}%`} 142 - stop-color={`rgb(from ${blog.palette.clouds} r g b / 0)`}></stop> 143 - <stop offset="100%" stop-color={`rgb(from ${blog.palette.clouds} r g b / 0)`}></stop> 142 + stop-color={`rgb(from ${blog.palette.environment.clouds} r g b / 0)`} 143 + ></stop> 144 + <stop 145 + offset="100%" 146 + stop-color={`rgb(from ${blog.palette.environment.clouds} r g b / 0)`} 147 + ></stop> 144 148 </linearGradient> 145 149 </defs> 146 150
+3 -3
src/components/blog/background/Clouds.astro
··· 30 30 ...prev.output, 31 31 <Cloud 32 32 style={`--parallax-speed: ${blog.background.parallax.clouds}; 33 - width: ${width}rem; 34 - height: ${height}rem; 35 - top: ${y}rem; 33 + width: ${width}px; 34 + height: ${height}px; 35 + top: ${y}px; 36 36 left: calc(${x} * 200lvw - 100lvw); 37 37 position: absolute;`} 38 38 id={"cloud-" + i}
+10 -9
src/components/blog/background/Moon.astro
··· 8 8 9 9 <style> 10 10 svg { 11 - width: 20rem; 12 - height: 20rem; 13 - font-size: 20rem; 11 + width: 200px; 12 + height: 200px; 13 + font-size: 200px; 14 14 15 15 position: absolute; 16 - top: 30rem; 17 - right: 1rem; 16 + top: 300px; 17 + right: 10px; 18 18 } 19 19 </style> 20 20 ··· 32 32 </defs> 33 33 34 34 <!-- base --> 35 - <circle fill={blog.palette.moon} cx="100" cy="100" r="100"></circle> 35 + <circle fill={blog.palette.environment.moon} cx="100" cy="100" r="100" 36 + ></circle> 36 37 <!-- half shadow --> 37 38 <circle 38 - fill={blog.palette.sky.night} 39 + fill={blog.palette.environment.sky.night} 39 40 cx="100" 40 41 cy="100" 41 42 r="100" ··· 43 44 <!-- rotation shadow bulge thing --> 44 45 <ellipse 45 46 fill={phase < 0.25 || phase > 0.75 46 - ? blog.palette.moon 47 - : blog.palette.sky.night} 47 + ? blog.palette.environment.moon 48 + : blog.palette.environment.sky.night} 48 49 cx="100" 49 50 cy="100" 50 51 rx={50 * Math.cos(4 * Math.PI * phase) + 50}
+3 -3
src/components/blog/background/Stars.astro
··· 39 39 const prongs = Math.round( 40 40 blog.background.stars.prongs[0] + 41 41 Math.random() * 42 - (blog.background.stars.prongs[1] - blog.background.stars.prongs[0]) 42 + (blog.background.stars.prongs[1] - blog.background.stars.prongs[0]), 43 43 ); 44 44 45 45 return ( 46 46 <svg 47 47 style={`--parallax-speed: ${utils.getRandom(blog.background.parallax.star, sizeSeed)}; 48 - --size: ${blog.background.stars.size[0] + sizeSeed * (blog.background.stars.size[1] - blog.background.stars.size[0])}rem; 48 + --size: ${blog.background.stars.size[0] + sizeSeed * (blog.background.stars.size[1] - blog.background.stars.size[0])}px; 49 49 --x: ${Math.random()}; 50 50 --y: ${Math.random()}; 51 51 --rotate-speed: ${blog.background.stars.rotateSpeed[0] + Math.random() * (blog.background.stars.rotateSpeed[1] - blog.background.stars.rotateSpeed[0])}s; ··· 57 57 > 58 58 {new Array(prongs).fill(0).map((_, i) => ( 59 59 <polygon 60 - fill={blog.palette.stars} 60 + fill={blog.palette.environment.stars} 61 61 points="50 0, 75 50, 25 50" 62 62 transform={`rotate(${(i / prongs) * 360})`} 63 63 transform-origin="center"
+4 -4
src/components/blog/background/Sun.astro
··· 13 13 svg { 14 14 position: absolute; 15 15 border-radius: 100%; 16 - top: 15rem; 17 - left: calc(100lvw * var(--sun-progress-percent) - 7.5rem); 16 + top: 150px; 17 + left: calc(100lvw * var(--sun-progress-percent) - 75px); 18 18 z-index: -1; 19 19 20 20 animation: ··· 33 33 --sun-progress-percent: ${percent}`} 34 34 data-parallax 35 35 > 36 - <circle fill={blog.palette.sun} cx="50" cy="50" r="35"></circle> 36 + <circle fill={blog.palette.environment.sun} cx="50" cy="50" r="35"></circle> 37 37 38 38 { 39 39 new Array(prongs) 40 40 .fill(0) 41 41 .map((_, i) => ( 42 42 <polygon 43 - fill={blog.palette.sun} 43 + fill={blog.palette.environment.sun} 44 44 points="50 0, 55 10, 45 10" 45 45 transform={`rotate(${(i / prongs) * 360})`} 46 46 transform-origin="center"
+259
src/components/blog/post.css
··· 1 + .content { 2 + /* Custom Flags */ 3 + 4 + & blockquote { 5 + &[data-bq-flag--note], 6 + &[data-bq-flag--alert], 7 + &[data-bq-flag--info] { 8 + color: black; 9 + } 10 + 11 + &[data-bq-flag--note] { 12 + --accent: var(--rainbow-4); 13 + --icon: "๐Ÿ“"; 14 + --name: "Note"; 15 + } 16 + 17 + &[data-bq-flag--alert] { 18 + --accent: var(--rainbow-0); 19 + --icon: "๐Ÿšจ"; 20 + --name: "Alert"; 21 + } 22 + 23 + &[data-bq-flag--info] { 24 + --accent: var(--rainbow-5); 25 + --icon: "๐Ÿ“š"; 26 + --name: "Info"; 27 + } 28 + } 29 + 30 + & p, 31 + & div:has(> p) { 32 + &[data-para-flag--bg-red], 33 + &[data-para-flag--bg-orange], 34 + &[data-para-flag--bg-yellow], 35 + &[data-para-flag--bg-green], 36 + &[data-para-flag--bg-blue], 37 + &[data-para-flag--bg-purple] { 38 + /* contrasting colour */ 39 + color: black; 40 + } 41 + 42 + &[data-para-flag--bg-red] { 43 + background-color: var(--rainbow-0); 44 + } 45 + 46 + &[data-para-flag--bg-orange] { 47 + background-color: var(--rainbow-1); 48 + } 49 + 50 + &[data-para-flag--bg-yellow] { 51 + background-color: var(--rainbow-2); 52 + } 53 + 54 + &[data-para-flag--bg-green] { 55 + background-color: var(--rainbow-3); 56 + } 57 + 58 + &[data-para-flag--bg-blue] { 59 + background-color: var(--rainbow-4); 60 + } 61 + 62 + &[data-para-flag--bg-purple] { 63 + background-color: var(--rainbow-5); 64 + } 65 + } 66 + 67 + & img { 68 + &[data-img-flag--small="true"] { 69 + width: 50%; 70 + } 71 + 72 + &[data-img-flag--left="true"] { 73 + float: left; 74 + } 75 + 76 + &[data-img-flag--right="true"] { 77 + float: right; 78 + } 79 + 80 + &[data-img-flag--centre="true"] { 81 + margin-inline: auto; 82 + } 83 + } 84 + 85 + /* Headings */ 86 + 87 + & h2, 88 + & h3, 89 + & h4 { 90 + margin-block-start: 20px; 91 + margin-block-end: 20px; 92 + 93 + color: var(--typo-subheading); 94 + 95 + & + & { 96 + margin-block-start: 0; 97 + } 98 + 99 + :has(+ &) { 100 + margin-block-end: 0; 101 + } 102 + } 103 + 104 + & h2 { 105 + font-size: 2.2rem; 106 + } 107 + 108 + & h3 { 109 + font-size: 1.8rem; 110 + } 111 + 112 + & h4 { 113 + font-size: 1.5rem; 114 + } 115 + 116 + /* Paragraphs */ 117 + & p, 118 + & blockquote { 119 + clear: both; 120 + margin-block: 20px; 121 + } 122 + 123 + & div:has(> p) { 124 + margin-block: 10px; 125 + } 126 + 127 + /* Images */ 128 + & img { 129 + height: auto; /* fix height issues ?? */ 130 + margin: 10px; 131 + } 132 + 133 + & a { 134 + text-decoration: 2px underline; 135 + 136 + &:link { 137 + color: var(--typo-url); 138 + } 139 + &:visited { 140 + color: var(--typo-visited); 141 + } 142 + &:hover { 143 + text-decoration: 1px wavy underline; 144 + } 145 + &:active { 146 + color: var(--rainbow-3); 147 + } 148 + } 149 + 150 + /* Standard Lists */ 151 + & ul, 152 + & ol { 153 + margin-inline-start: 40px; 154 + & & { 155 + margin-inline-start: 20px; 156 + } 157 + } 158 + 159 + /* Blockquotes */ 160 + & blockquote { 161 + --accent: var(--bg-secondary); 162 + border-left: 2px solid hsl(from var(--accent) h s calc(l * 0.9)); 163 + padding: 10px 40px 10px 10px; 164 + margin: 10px; 165 + border-radius: 5px; 166 + background-color: var(--accent); 167 + width: fit-content; 168 + min-width: 200px; 169 + 170 + &::before { 171 + content: var(--icon) " " var(--name) / var(--name); 172 + } 173 + } 174 + 175 + /* Inline code */ 176 + :not(pre) > code { 177 + color: var(--typo-code); 178 + background-color: var(--bg-code); 179 + padding: 2px; 180 + border-radius: 5px; 181 + 182 + /* make blockquote code use a lighter version of the accent with a darker background */ 183 + :is(blockquote) & { 184 + color: hsl(from var(--accent) h calc(s * 0.8) calc(l * 1.4)); 185 + background-color: #00000080; 186 + } 187 + } 188 + 189 + /* Outline Code */ 190 + & pre:has(> code) { 191 + padding: 5px; 192 + border-radius: 10px; 193 + } 194 + 195 + .astro-code { 196 + background-color: var(--bg-code) !important; 197 + margin-block: 10px; 198 + padding: 0; 199 + position: relative; 200 + 201 + & code { 202 + display: block; 203 + padding: 10px; 204 + 205 + & span { 206 + color: light-dark(var(--shiki-light), var(--shiki-dark)) !important; 207 + } 208 + } 209 + } 210 + 211 + /* Check lists */ 212 + .task-list-item { 213 + list-style: none; 214 + } 215 + 216 + .task-list-item label { 217 + display: flex; 218 + margin-block-start: 5px; 219 + margin-block-end: 7.5px; 220 + 221 + gap: 5px; 222 + 223 + & input[type="checkbox"] { 224 + width: 25px; 225 + height: 25px; 226 + margin-inline-end: 5px; 227 + 228 + background: light-dark(rgb(0, 0, 0, 0.2), rgb(255, 255, 255, 0.4)); 229 + border-radius: 5px; 230 + border: 1px solid var(--typo-body); 231 + 232 + &:checked { 233 + --checkmark: url("../../assets/check.svg"); 234 + background: var(--checkmark) center/20px padding-box no-repeat 235 + var(--rainbow-2); 236 + } 237 + } 238 + } 239 + 240 + /* Table */ 241 + & table { 242 + border-collapse: collapse; 243 + border-spacing: 0; 244 + 245 + & th, 246 + & td { 247 + border: 1px solid white; 248 + padding: 5px; 249 + } 250 + 251 + & thead th { 252 + background-color: var(--bg-secondary); 253 + } 254 + 255 + & tbody tr:nth-child(2n) { 256 + background-color: #ffffff10; 257 + } 258 + } 259 + }
+90
src/components/generic/LightDarkToggle.astro
··· 1 + --- 2 + import Sun from "@/assets/sun.svg"; 3 + import Moon from "@/assets/moon.svg"; 4 + 5 + interface Props { 6 + colours: { 7 + bg: string; 8 + fg: string; 9 + }; 10 + } 11 + 12 + const { colours } = Astro.props; 13 + --- 14 + 15 + <button 16 + aria-label="toggle colour scheme" 17 + id="colour-toggle" 18 + style="visibility: hidden" 19 + > 20 + <Sun data-mode-light /> 21 + <Moon data-mode-dark /> 22 + </button> 23 + 24 + <style define:vars={colours}> 25 + #colour-toggle { 26 + background-color: var(--bg); 27 + 28 + border: none; 29 + border-radius: 50%; 30 + padding: 5px; 31 + 32 + width: 34px; 33 + height: 34px; 34 + 35 + & svg { 36 + fill: var(--fg); 37 + stroke: var(--fg); 38 + } 39 + } 40 + </style> 41 + 42 + <script> 43 + const root = document.querySelector(":root"); 44 + if (!(root instanceof HTMLElement)) throw new Error(":root is not html"); 45 + 46 + const button = document.getElementById("colour-toggle"); 47 + if (!button) throw new Error("No #colour-toggle element"); 48 + 49 + const modeToggled = { 50 + light: document.querySelectorAll("[data-mode-light]"), 51 + dark: document.querySelectorAll("[data-mode-dark]"), 52 + }; 53 + 54 + const cookies = document.cookie 55 + .split("; ") 56 + .reduce< 57 + Record<string, string> 58 + >((prev, cur) => ({ ...prev, [cur.split("=")[0]]: cur.split("=")[1] }), {}); 59 + 60 + const mediaMode = matchMedia("(prefers-color-scheme: light)").matches; 61 + const cookieMode = 62 + cookies["colour-mode"] === "light" 63 + ? true 64 + : cookies["colour-mode"] === "dark" 65 + ? false 66 + : undefined; 67 + let lightMode = cookieMode ?? mediaMode; 68 + 69 + const updateColourScheme = () => { 70 + root.style.colorScheme = lightMode ? "light" : "dark"; 71 + document.cookie = `colour-mode=${root.style.colorScheme};path=/`; 72 + 73 + modeToggled[lightMode ? "light" : "dark"].forEach((el) => { 74 + if (!(el instanceof SVGElement || el instanceof HTMLElement)) return; 75 + el.style.display = "block"; 76 + }); 77 + 78 + modeToggled[!lightMode ? "light" : "dark"].forEach((el) => { 79 + if (!(el instanceof SVGElement || el instanceof HTMLElement)) return; 80 + el.style.display = "none"; 81 + }); 82 + }; 83 + 84 + updateColourScheme(); 85 + button.addEventListener("click", () => { 86 + lightMode = !lightMode; 87 + updateColourScheme(); 88 + }); 89 + button.style.visibility = "visible"; 90 + </script>
-92
src/components/generic/Nav.astro
··· 1 - --- 2 - import type { nav } from "@/content.config"; 3 - import NavEntry from "./NavEntry.astro"; 4 - import Hamburger from "@/assets/hamburger.svg"; 5 - import X from "@/assets/x.svg"; 6 - 7 - interface Props { 8 - startOpen?: boolean; 9 - current: string; 10 - data: nav[]; 11 - } 12 - 13 - const { data, startOpen = false, current } = Astro.props; 14 - --- 15 - 16 - <button 17 - popovertarget="nav" 18 - popovertargetaction="show" 19 - aria-label="Nav Menu" 20 - id="nav-menu" 21 - > 22 - <Hamburger /> 23 - </button> 24 - <dialog closedby="any" open={startOpen} id="nav" popover> 25 - <div class="top"> 26 - <h1>{current}</h1> 27 - <button popovertarget="nav" popovertargetaction="hide" aria-label="close"> 28 - <X width={32} height={32} /> 29 - </button> 30 - </div> 31 - <NavEntry {data} /> 32 - </dialog> 33 - 34 - <style> 35 - button { 36 - background-color: transparent; 37 - border: none; 38 - & svg { 39 - stroke: white; 40 - } 41 - } 42 - 43 - #nav-menu { 44 - background-color: black; 45 - padding: 1rem; 46 - border-radius: 0 0 50% 0; 47 - } 48 - 49 - .top { 50 - display: flex; 51 - flex-direction: row; 52 - justify-content: space-between; 53 - } 54 - 55 - dialog { 56 - color: white; 57 - 58 - width: min(50vw, 20rem); 59 - height: 100vh; 60 - padding: 1rem; 61 - 62 - background: black; 63 - border: none; 64 - 65 - position: fixed; 66 - top: 0; 67 - transition: 68 - left 0.2s, 69 - display 0.2s allow-discrete; 70 - 71 - &:popover-open { 72 - /* Post-Entry (Normal) State */ 73 - left: 0; 74 - 75 - /* Pre-Entry State */ 76 - @starting-style { 77 - left: -100%; 78 - } 79 - } 80 - 81 - /* Exiting State */ 82 - &:not(:popover-open) { 83 - left: -100%; 84 - } 85 - } 86 - 87 - /* STUPID ISSUE (astro tries to add a tag to the backdrop) */ 88 - :global(::backdrop) { 89 - background: #00000080; 90 - backdrop-filter: blur(0.5rem); 91 - } 92 - </style>
-63
src/components/generic/NavEntry.astro
··· 1 - --- 2 - import type { nav } from "@/content.config"; 3 - 4 - interface Props { 5 - data: nav[]; 6 - root?: string; 7 - } 8 - 9 - const { data, root = "" } = Astro.props; 10 - --- 11 - 12 - <ul> 13 - { 14 - data.map((x) => ( 15 - <li> 16 - <a href={`${root}${x.slug}`}>{x.name}</a> 17 - {x.children && x.children.length > 0 ? ( 18 - <Astro.self root={`${root}${x.slug}`} data={x.children} /> 19 - ) : null} 20 - </li> 21 - )) 22 - } 23 - </ul> 24 - 25 - <style> 26 - li { 27 - list-style-type: "โ•บ "; 28 - margin-inline-start: 4rem; 29 - } 30 - 31 - a:link { 32 - color: #62A0EA; 33 - } 34 - 35 - a:visited { 36 - color: #DC8ADD; 37 - } 38 - 39 - a:focus, 40 - a:hover { 41 - text-decoration: none; 42 - color: #4040ff 43 - 44 - &:visited { 45 - color: #ff40ff; 46 - } 47 - } 48 - 49 - a:focus { 50 - outline: 0.2rem solid #62A0EA; 51 - outline-offset: 0; 52 - border-radius: 0.4rem; 53 - 54 - &:visited { 55 - outline-color: #DC8ADD; 56 - } 57 - } 58 - 59 - a:active { 60 - text-decoration: none; 61 - scale: 1.05; 62 - } 63 - </style>
-210
src/components/index/Map.astro
··· 1 - --- 2 - import mc_map_contents from "@/assets/mc_map_contents.png"; 3 - import mc_red from "@/assets/mc_red_banner.png"; 4 - import mc_cyan from "@/assets/mc_cyan_banner.png"; 5 - import mc_magenta from "@/assets/mc_magenta_banner.png"; 6 - import mc_blue from "@/assets/mc_blue_banner.png"; 7 - import Arrow from "@/assets/arrow.svg"; 8 - 9 - type colour = "red" | "cyan" | "magenta" | "blue"; 10 - type position = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7; 11 - 12 - interface Location { 13 - colour: colour; 14 - name: string; 15 - href?: string; 16 - x: position; 17 - y: position; 18 - } 19 - 20 - interface Props { 21 - locations: Location[]; 22 - } 23 - 24 - const { locations } = Astro.props; 25 - 26 - const getSrc = (colour: colour) => { 27 - switch (colour) { 28 - case "red": 29 - return mc_red.src; 30 - case "cyan": 31 - return mc_cyan.src; 32 - case "magenta": 33 - return mc_magenta.src; 34 - case "blue": 35 - return mc_blue.src; 36 - } 37 - }; 38 - 39 - const getCssColour = (colour: colour) => { 40 - switch (colour) { 41 - case "red": 42 - return "#af2d25"; 43 - case "cyan": 44 - return "#159b9b"; 45 - case "magenta": 46 - return "#c64ebc"; 47 - case "blue": 48 - return "#3c43a9"; 49 - } 50 - }; 51 - 52 - const scale = 4; 53 - --- 54 - 55 - <div style=`--scale: ${scale}px;` class="map"> 56 - <img 57 - src={mc_map_contents.src} 58 - alt="" 59 - width={58 * scale} 60 - height={58 * scale} 61 - class="map-img" 62 - /> 63 - 64 - { 65 - locations.map((l) => { 66 - // calculate size of map region 67 - const mapSize = 64 * scale; 68 - 69 - interface XY { 70 - y: number; 71 - x: number; 72 - } 73 - 74 - // calculate position of banner 75 - const bannerPos: XY = { 76 - x: l.x * 8 * scale, 77 - y: l.y * 8 * scale, 78 - }; 79 - 80 - // calculate position of text 81 - // coords are inverted so that it doesnt overlap the map 82 - // and so that the line is based properly 83 - // const textPos: XY = { 84 - // x: mapSize + 16 + Math.floor(Math.random() * 30), 85 - // y: mapSize - 32 + Math.floor(Math.random() * 30) - 15, 86 - // }; 87 - 88 - const textPos: XY = { 89 - ...(l.x < 4 90 - ? { x: -132 - Math.floor(Math.random() * 50) } 91 - : { x: mapSize + 32 + Math.floor(Math.random() * 50) }), 92 - ...(l.y < 4 93 - ? { y: 16 + Math.floor(Math.random() * 50) - 25 } 94 - : { y: (mapSize / 4) * 3 + Math.floor(Math.random() * 50) - 25 }), 95 - }; 96 - 97 - // calculate coordinates of corners 98 - const bannerCorner: XY = { 99 - x: bannerPos.x < 128 ? bannerPos.x : bannerPos.x + 8 * scale, 100 - y: bannerPos.y < 128 ? bannerPos.y : bannerPos.y + 8 * scale, 101 - }; 102 - 103 - const textCorner: XY = { 104 - ...textPos, 105 - ...(l.x < 4 ? { x: textPos.x + 100 } : {}), 106 - ...{ y: textPos.y + 10 }, 107 - }; 108 - 109 - // calculate length and angle of line 110 - 111 - const lineLength: number = Math.sqrt( 112 - (bannerCorner.x - textCorner.x) ** 2 + 113 - (bannerCorner.y - textCorner.y) ** 2, 114 - ); 115 - 116 - const lineAngle: number = Math.atan2( 117 - bannerCorner.y - textCorner.y, 118 - bannerCorner.x - textCorner.x, 119 - ); 120 - 121 - return ( 122 - <a class="location" href={l.href ?? ""}> 123 - <img 124 - src={getSrc(l.colour)} 125 - alt="" 126 - width={8 * scale} 127 - height={8 * scale} 128 - class="marker" 129 - style={`--y: ${bannerPos.y}px; 130 - --x: ${bannerPos.x}px; 131 - --colour: ${getCssColour(l.colour)};`} 132 - /> 133 - <div 134 - style={`--y: ${textPos.y}px; 135 - --x: ${textPos.x}px; 136 - --colour: ${getCssColour(l.colour)}; 137 - text-align: ${l.x < 4 ? "right" : "left"};`} 138 - class="text" 139 - > 140 - {l.name} 141 - </div> 142 - <Arrow 143 - class="line" 144 - style={`--width: ${lineLength}px; 145 - --y: ${textCorner.y}px; 146 - --x: ${textCorner.x}px; 147 - --angle: ${lineAngle}rad; 148 - --colour: ${getCssColour(l.colour)}; 149 - --scaleY: ${!(l.x < 4 != l.y < 4) ? -1 : 1};`} 150 - /> 151 - </a> 152 - ); 153 - }) 154 - } 155 - </div> 156 - 157 - <style> 158 - img { 159 - display: block; 160 - position: absolute; 161 - &.map-img { 162 - top: calc(3 * var(--scale)); 163 - left: calc(3 * var(--scale)); 164 - } 165 - &.marker { 166 - top: var(--y); 167 - left: var(--x); 168 - .location:hover & { 169 - filter: drop-shadow(0 0 0.5rem var(--colour)); 170 - scale: 1.1; 171 - } 172 - } 173 - } 174 - 175 - .text { 176 - position: absolute; 177 - 178 - width: 10rem; 179 - height: 2rem; 180 - 181 - top: var(--y); 182 - left: var(--x); 183 - 184 - color: var(--colour); 185 - .location:hover & { 186 - filter: drop-shadow(0 0 0.5rem var(--colour)); 187 - } 188 - } 189 - 190 - .location { 191 - display: block; 192 - --speed: 100ms; 193 - transition: 194 - scale var(--speed), 195 - filter var(--speed); 196 - & * { 197 - transition: inherit; 198 - } 199 - } 200 - 201 - .map { 202 - box-sizing: border-box; 203 - width: calc(64 * var(--scale)); 204 - height: calc(64 * var(--scale)); 205 - background-image: url("../../assets/mc_map.png"); 206 - background-size: 100%; 207 - image-rendering: pixelated; 208 - position: relative; 209 - } 210 - </style>
+186
src/components/navigation/Nav.astro
··· 1 + --- 2 + import type { nav } from "@/content/config"; 3 + import { getEntry } from "astro:content"; 4 + 5 + import Sidebar from "./Sidebar.astro"; 6 + import Hamburger from "@/assets/hamburger.svg"; 7 + 8 + const data = (await getEntry("nav", "urls")?.then((x) => x.data)) ?? []; 9 + --- 10 + 11 + <nav> 12 + <h1>๐Ÿชค | vielle.dev</h1> 13 + <ul class="desktop"> 14 + { 15 + (() => { 16 + const a = (name: string, href: string) => <a {href}>{name}</a>; 17 + const render = ( 18 + name: string, 19 + children: nav[], 20 + popout: boolean = true, 21 + ) => { 22 + const list = ( 23 + <ul> 24 + {children.map((x) => ( 25 + <li> 26 + {x.children 27 + ? render(x.name, x.children, false) 28 + : a(x.name, x.url || "")} 29 + </li> 30 + ))} 31 + </ul> 32 + ); 33 + return popout ? ( 34 + <details name="nav"> 35 + <summary> 36 + <span>{name}</span> 37 + </summary> 38 + {list} 39 + </details> 40 + ) : ( 41 + <> 42 + <span>{name}</span> 43 + {list} 44 + </> 45 + ); 46 + }; 47 + return data.map((entry) => ( 48 + <li> 49 + {entry.children 50 + ? render(entry.name, entry.children) 51 + : a(entry.name, entry.url || "")} 52 + </li> 53 + )); 54 + })() 55 + } 56 + </ul> 57 + 58 + <button class="mobile" popovertarget="sidebar"><Hamburger /></button> 59 + 60 + <Sidebar /> 61 + </nav> 62 + 63 + <style> 64 + nav { 65 + background-color: white; 66 + color: black; 67 + border: 5px solid black; 68 + border-radius: 0 0 20px 20px; 69 + box-shadow: 0 10px; 70 + display: flex; 71 + flex-direction: row; 72 + align-items: center; 73 + justify-content: center; 74 + padding: 10px 40px; 75 + } 76 + 77 + nav > ul { 78 + display: flex; 79 + flex-direction: row; 80 + align-items: center; 81 + justify-content: center; 82 + gap: 10px; 83 + 84 + & > li { 85 + list-style-type: none; 86 + display: flex; 87 + flex-direction: row; 88 + align-items: center; 89 + justify-content: center; 90 + gap: 10px; 91 + &:not(:first-child)::before { 92 + content: ""; 93 + display: block; 94 + width: 5px; 95 + height: 5px; 96 + border-radius: 2.5px; 97 + background-color: currentColor; 98 + } 99 + } 100 + } 101 + 102 + details { 103 + position: relative; 104 + 105 + summary::marker { 106 + content: url(../../assets/arrow-right.svg); 107 + + { 108 + margin-left: 5px; 109 + } 110 + } 111 + &[open] > summary::marker { 112 + content: url(../../assets/arrow-down.svg); 113 + } 114 + } 115 + 116 + details > ul { 117 + background-color: white; 118 + border: 5px solid black; 119 + border-radius: 20px; 120 + box-shadow: 0 10px; 121 + 122 + padding: 10px 20px; 123 + width: max-content; 124 + 125 + ul { 126 + margin-left: 1em; 127 + } 128 + } 129 + 130 + /* positioning */ 131 + details[open] { 132 + anchor-name: --detail-anchor; 133 + > ul { 134 + position: absolute; 135 + 136 + /* fallback for no anchor support */ 137 + right: -20px; 138 + 139 + /* remove fallback when supported */ 140 + @supports (anchor-name: --supports-anchor) { 141 + position: fixed; 142 + right: unset; 143 + } 144 + 145 + /* anchor positioning. all properties should progressive enhance */ 146 + position-anchor: --detail-anchor; 147 + position-area: bottom span-right; 148 + position-try-fallbacks: flip-inline; 149 + position-try: flip-inline; 150 + } 151 + } 152 + 153 + h1 { 154 + margin-right: auto; 155 + font-size: 1.5rem; 156 + white-space: nowrap; 157 + } 158 + 159 + button:has(svg) { 160 + padding: 0; 161 + border: none; 162 + background-color: transparent; 163 + 164 + width: 2em; 165 + height: 2em; 166 + 167 + & svg { 168 + width: 100%; 169 + height: 100%; 170 + } 171 + } 172 + 173 + .mobile { 174 + display: none; 175 + } 176 + 177 + @media screen and (max-width: 650px) { 178 + .mobile { 179 + display: var(--display, block) !important; 180 + } 181 + 182 + .desktop { 183 + display: none !important; 184 + } 185 + } 186 + </style>
+100
src/components/navigation/Sidebar.astro
··· 1 + --- 2 + import type { nav } from "@/content/config"; 3 + import { getEntry } from "astro:content"; 4 + 5 + const data = (await getEntry("nav", "urls")?.then((x) => x.data)) ?? []; 6 + --- 7 + 8 + <aside id="sidebar" popover> 9 + <h2>๐Ÿชค | vielle.dev</h2> 10 + 11 + <nav> 12 + { 13 + (() => { 14 + const step = (entry: nav[]) => ( 15 + <ul> 16 + {entry.map((x) => ( 17 + <li> 18 + {x.url ? <a href={x.url}>{x.name}</a> : <span>{x.name}</span>} 19 + {x.children && step(x.children)} 20 + </li> 21 + ))} 22 + </ul> 23 + ); 24 + return step(data); 25 + })() 26 + } 27 + </nav> 28 + </aside> 29 + 30 + <style> 31 + aside { 32 + inset: auto; 33 + top: 0; 34 + right: 0; 35 + height: 100%; 36 + 37 + background-color: white; 38 + color: black; 39 + border: 5px solid black; 40 + border-radius: 20px 0 0 20px; 41 + box-shadow: 0 10px; 42 + padding: 10px 20px; 43 + 44 + & ul { 45 + padding-inline: 1em; 46 + } 47 + 48 + &::backdrop { 49 + background-color: #00000080; 50 + backdrop-filter: blur(15px); 51 + } 52 + 53 + /* animations */ 54 + --popover-animation-timing: 0.2s; 55 + @media (prefers-reduced-motion: reduce) { 56 + --popover-animation-timing: 0; 57 + } 58 + --popover-animation-easing: ease-in-out; 59 + 60 + transition: 61 + translate var(--popover-animation-timing, 0.2s) 62 + var(--popover-animation-easing, linear) allow-discrete, 63 + overlay var(--popover-animation-timing, 0.2s) 64 + var(--popover-animation-easing, linear) allow-discrete, 65 + display var(--popover-animation-timing, 0.2s) 66 + var(--popover-animation-easing, linear) allow-discrete; 67 + 68 + /* exit */ 69 + translate: 100% 0; 70 + /* base */ 71 + &:popover-open { 72 + translate: 0 0; 73 + /* enter */ 74 + @starting-style { 75 + translate: 100% 0; 76 + } 77 + } 78 + 79 + &::backdrop { 80 + transition: 81 + display var(--popover-animation-timing) 82 + var(--popover-animation-easing, linear) allow-discrete, 83 + overlay var(--popover-animation-timing, 0.2s) 84 + var(--popover-animation-easing, linear) allow-discrete, 85 + opacity var(--popover-animation-timing) 86 + var(--popover-animation-easing, linear) allow-discrete; 87 + 88 + /* exit */ 89 + opacity: 0; 90 + } 91 + /* base */ 92 + &:popover-open::backdrop { 93 + opacity: 1; 94 + /* enter */ 95 + @starting-style { 96 + opacity: 0; 97 + } 98 + } 99 + } 100 + </style>
+70 -25
src/config.ts
··· 1 1 export const blog = { 2 2 // overrideHour: 0, 3 3 post: { 4 - width: 30, 5 - xPadding: 2, 6 - yLeeway: 5, 7 - yGap: 20, 8 - topYGap: 35, 4 + width: 300, 5 + xPadding: 20, 6 + yLeeway: 50, 7 + yGap: 200, 8 + topYGap: 350, 9 9 drift: [0.1, 1], 10 10 timing: [10, 20], 11 11 }, ··· 19 19 20 20 clouds: { 21 21 count: 8, 22 - width: [40, 80], 23 - height: [15, 30], 24 - yGap: [15, 25], 22 + width: [400, 800], 23 + height: [150, 300], 24 + yGap: [150, 250], 25 25 26 26 bumpRadius: [20, 60], 27 27 gradientStops: [35, 80], ··· 29 29 30 30 stars: { 31 31 count: 40, 32 - size: [2, 5], 32 + size: [20, 50], 33 33 prongs: [4, 8], 34 34 rotateSpeed: [20, 40], 35 35 }, ··· 40 40 }, 41 41 balloons: { 42 42 numBalloons: [1, 3], 43 - length: [5, 15], 44 - offset: [-2.5, 2.5], 43 + length: [50, 150], 44 + offset: [-25, 25], 45 45 rotation: [-10, 10], 46 46 timing: [30, 45], 47 47 size: [ 48 - [5, 10], 49 - [10, 20], 48 + [50, 100], 49 + [100, 200], 50 50 ], 51 51 opacity: [0.6, 0.9], 52 52 time: [2, 5], 53 53 }, 54 54 palette: { 55 - sky: { 56 - // blue 57 - day: "#1E90FF", 58 - // black 59 - night: "#39375B" 55 + environment: { 56 + sky: { 57 + // blue 58 + day: "#1E90FF", 59 + // black 60 + night: "#39375B", 61 + }, 62 + // yellow 63 + sun: "#FFEC51", 64 + // whites 65 + moon: "#E5D4ED", 66 + clouds: "#E5D4ED", 67 + stars: "#ffffff", 68 + }, 69 + 70 + post: { 71 + light: { 72 + background: { 73 + main: "#f2f6fc", 74 + secondary: "#e7e8ea", 75 + code: "#ffffff", 76 + }, 77 + typography: { 78 + body: "#070e21", 79 + heading: "#070e21", 80 + subheading: "#040710", 81 + url: "#1f3e98", 82 + visited: "#931f82", 83 + code: "#137B81", 84 + }, 85 + }, 86 + 87 + dark: { 88 + background: { 89 + main: "#262428", 90 + secondary: "#161418", 91 + code: "#303446", 92 + }, 93 + typography: { 94 + body: "#ffebff", 95 + heading: "#ffe8ff", 96 + subheading: "#ffe0ff", 97 + url: "#a8a8ff", 98 + visited: "#ff80ff", 99 + code: "#81c8be", 100 + }, 101 + }, 102 + 103 + rainbow: [ 104 + "#F09094", 105 + "#F6B379", 106 + "#F6E8A2", 107 + "#97BB77", 108 + "#C2D7FF", 109 + "#D784C9", 110 + ], 60 111 }, 61 - // yellow 62 - sun: "#FFEC51", 63 - // whites 64 - moon: "#E5D4ED", 65 - clouds: "#E5D4ED", 66 - stars: "#ffffff", 67 - } 112 + }, 68 113 } as const; 69 114 70 115 export const utils = {
+50
src/content/config.ts
··· 1 + import { defineCollection, z } from "astro:content"; 2 + import { file, glob } from "astro/loaders"; 3 + 4 + const blog = defineCollection({ 5 + loader: glob({ pattern: "**/*.md", base: "./src/content/posts" }), 6 + schema: z.object({ 7 + title: z.string(), 8 + date: z.date(), 9 + colour: z.string(), 10 + // no alt (empty as decorative) 11 + image: z 12 + .string() 13 + .refine( 14 + (value) => value.endsWith(".png"), 15 + (val) => ({ 16 + message: `${val} must end with .png`, 17 + }), 18 + ) 19 + .optional(), 20 + hasMdx: z.boolean().default(false), 21 + }), 22 + }); 23 + 24 + const blogMdx = defineCollection({ 25 + loader: glob({ pattern: "**/*.mdx", base: "./src/content/posts" }), 26 + schema: z.object({ 27 + title: z.string(), 28 + }), 29 + }); 30 + 31 + export type nav = { 32 + name: string; 33 + url: string | false; 34 + children?: nav[]; 35 + }; 36 + 37 + const navSchema: z.ZodType<nav> = z.lazy(() => 38 + z.object({ 39 + name: z.string(), 40 + url: z.string().or(z.literal(false)), 41 + children: z.optional(z.array(navSchema)), 42 + }), 43 + ); 44 + 45 + const nav = defineCollection({ 46 + loader: file("src/content/navList.json"), 47 + schema: z.array(navSchema), 48 + }); 49 + 50 + export const collections = { blog, blogMdx, nav };
+76
src/content/navList.json
··· 1 + { 2 + "urls": [ 3 + { 4 + "url": "/", 5 + "name": "Home" 6 + }, 7 + { 8 + "url": "/blog", 9 + "name": "Blog" 10 + }, 11 + { 12 + "url": false, 13 + "name": "More", 14 + "children": [ 15 + { 16 + "url": "/rss.xml", 17 + "name": "RSS" 18 + }, 19 + { 20 + "url": "/ai", 21 + "name": "AI Usage" 22 + }, 23 + { 24 + "url": "/use", 25 + "name": "Things I Use" 26 + } 27 + ] 28 + }, 29 + { 30 + "url": false, 31 + "name": "Socials", 32 + "children": [ 33 + { 34 + "url": "https://pdsls.dev/at://did:plc:4zht3z4caxwrw3dlsybodywc", 35 + "name": "atproto (pdsls)", 36 + "children": [ 37 + { 38 + "url": "https://deer.social/profile/did:plc:4zht3z4caxwrw3dlsybodywc", 39 + "name": "Bluesky" 40 + }, 41 + { 42 + "url": "https://tangled.sh/@vielle.dev", 43 + "name": "Tangled.sh" 44 + } 45 + ] 46 + }, 47 + { 48 + "url": "https://what-if-doctor-who-was-yuri-yaoi.tumblr.com/", 49 + "name": "Tumblr" 50 + }, 51 + { 52 + "url": "https://github.com/afterlifepro/", 53 + "name": "Github" 54 + } 55 + ] 56 + }, 57 + { 58 + "url": false, 59 + "name": "Projects", 60 + "children": [ 61 + { 62 + "url": "https://dong.vielle.dev", 63 + "name": "Dong (web)" 64 + }, 65 + { 66 + "url": "https://saltire-the-gays.vielle.dev", 67 + "name": "Saltire the Gays" 68 + }, 69 + { 70 + "url": "https://afterlifepro.neocities.org", 71 + "name": "Neocities (old site)" 72 + } 73 + ] 74 + } 75 + ] 76 + }
+1
src/content/posts
··· 1 + ../../posts
-49
src/content.config.ts
··· 1 - import { defineCollection, z } from "astro:content"; 2 - import { file, glob } from "astro/loaders"; 3 - 4 - const blog = defineCollection({ 5 - loader: glob({ pattern: "**/*.md", base: "./src/posts" }), 6 - schema: z.object({ 7 - title: z.string(), 8 - date: z.date(), 9 - colour: z.string(), 10 - // no alt (empty as decorative) 11 - image: z 12 - .string() 13 - .refine( 14 - (value) => value.endsWith(".png"), 15 - (val) => ({ 16 - message: `${val} must end with .png`, 17 - }), 18 - ) 19 - .optional(), 20 - hasMdx: z.boolean().default(false), 21 - }), 22 - }); 23 - 24 - const blogMdx = defineCollection({ 25 - loader: glob({ pattern: "**/*.mdx", base: "./src/posts" }), 26 - schema: z.object({ 27 - title: z.string(), 28 - }), 29 - }); 30 - 31 - const baseNav = z.object({ 32 - slug: z.string(), 33 - name: z.string(), 34 - }); 35 - 36 - export type nav = z.infer<typeof baseNav> & { 37 - children?: nav[]; 38 - }; 39 - 40 - const navSchema: z.ZodType<nav> = baseNav.extend({ 41 - children: z.lazy(() => navSchema.array()), 42 - }); 43 - 44 - const nav = defineCollection({ 45 - loader: file("src/navList.json"), 46 - schema: navSchema, 47 - }); 48 - 49 - export const collections = { blog, blogMdx, nav };
-28
src/navList.json
··· 1 - [ 2 - { 3 - "slug": "/", 4 - "name": "Home", 5 - "children": [] 6 - }, 7 - { 8 - "slug": "/blog", 9 - "name": "Blog", 10 - "children": [] 11 - }, 12 - { 13 - "slug": "/testing", 14 - "name": "Testing", 15 - "children": [ 16 - { 17 - "slug": "/123", 18 - "name": "123", 19 - "children": [] 20 - }, 21 - { 22 - "slug": "/maoii", 23 - "name": "Me!", 24 - "children": [] 25 - } 26 - ] 27 - } 28 - ]
+178 -6
src/pages/blog/[id].astro
··· 1 1 --- 2 2 import Base from "@/Base.astro"; 3 + import Nav from "@/components/navigation/Nav.astro"; 4 + import LightDarkToggle from "@/components/generic/LightDarkToggle.astro"; 5 + import CodeHeading from "@/components/blog/CodeHeading.astro"; 6 + 7 + import { render } from "astro:content"; 3 8 import { getEntry } from "astro:content"; 9 + import { parse } from "node:path"; 10 + 11 + import { blog } from "@/config"; 12 + import "@/components/blog/post.css"; 13 + 14 + const { 15 + post: { light, dark, rainbow }, 16 + } = blog.palette; 17 + 4 18 const { id } = Astro.params; 5 - const r404 = Astro.redirect("/404"); 19 + if (!id) return Astro.redirect("/404"); 6 20 7 - if (!id) return r404; 8 21 const post = await getEntry("blog", id); 9 - if (!post) return r404; 22 + if (!post) return Astro.redirect("/404"); 23 + 24 + const { 25 + data: { title, hasMdx, colour }, 26 + } = post; 27 + 28 + const Content = await (async () => { 29 + if (!hasMdx) return render(post).then((x) => x.Content); 30 + 31 + if (!post.filePath) throw new Error("Post does not have a filepath"); 32 + return import(`../../content/posts/${parse(post.filePath).name}.mdx`).then( 33 + (x) => x.Content, 34 + ); 35 + })(); 10 36 --- 11 37 12 - <Base title={post.data.title}> 13 - <h1>{post.data.title}</h1> 14 - <p>{JSON.stringify(post)}</p> 38 + <Base {title}> 39 + <Nav /> 40 + <header> 41 + <h1>{title}</h1> 42 + 43 + <LightDarkToggle 44 + colours={{ fg: "var(--bg-main)", bg: "var(--typo-body)" }} 45 + /> 46 + </header> 47 + 48 + <main style={`--accent: ${colour};`}> 49 + <div class="content"> 50 + <Content /> 51 + </div> 52 + </main> 15 53 </Base> 54 + 55 + <CodeHeading colours={{ text: "var(--typo-body)", border: "var(--bg-main)" }} /> 56 + 57 + <script> 58 + document.querySelectorAll(".astro-code").forEach((code) => { 59 + if (!(code instanceof HTMLElement)) return; 60 + 61 + const heading = document.createElement("code-heading"); 62 + heading.setAttribute("contents", code.innerText); 63 + 64 + const lang = code.dataset["language"]; 65 + if (lang && lang !== "plaintext") { 66 + const langEl = document.createElement("span"); 67 + langEl.innerText = "." + lang; 68 + heading.append(langEl); 69 + } 70 + 71 + code.prepend(heading); 72 + }); 73 + </script> 74 + 75 + <!-- page styles --> 76 + <style> 77 + header { 78 + position: fixed; 79 + top: 0; 80 + width: 100%; 81 + z-index: 999; 82 + display: flex; 83 + align-items: center; 84 + justify-content: space-between; 85 + 86 + background-color: var(--bg-secondary); 87 + color: var(--typo-heading); 88 + padding-bottom: 5px; 89 + 90 + & > :global(:not(style, script):last-of-type) { 91 + margin-right: 10px; 92 + margin-top: 5px; 93 + } 94 + } 95 + 96 + body { 97 + background-color: var(--bg-main); 98 + color: var(--typo-body); 99 + } 100 + 101 + .content { 102 + padding-block: 4em; 103 + } 104 + 105 + .content, 106 + :global(.full-width), 107 + :global([data-para-flag--full-width]) { 108 + --padding-inline: 20px; 109 + --content-max-width: 60ch; 110 + --breakout-max-width: 80ch; 111 + 112 + --content-size: min( 113 + 100% - var(--padding-inline) * 2, 114 + var(--content-max-width) 115 + ); 116 + --breakout-size: calc( 117 + (var(--breakout-max-width) - var(--content-max-width)) / 2 118 + ); 119 + 120 + display: grid; 121 + grid-template-columns: 122 + [full-width-start] minmax(var(--padding-inline), 1fr) 123 + [breakout-start] minmax(0, var(--breakout-size)) 124 + [content-start] var(--content-size) [content-end] 125 + minmax(0, var(--breakout-size)) [breakout-end] 126 + minmax(var(--padding-inline), 1fr) [full-width-end]; 127 + 128 + & > :global(*) { 129 + grid-column: content; 130 + } 131 + 132 + & > :global(.breakout), 133 + & > :global([data-para-flag--breakout]) { 134 + grid-column: breakout; 135 + } 136 + 137 + & > :global(.full-width), 138 + & > :global([data-para-flag--full-width]) { 139 + grid-column: full-width; 140 + } 141 + } 142 + 143 + :global(#nav-menu) { 144 + height: 3em; 145 + aspect-ratio: 1; 146 + display: flex; 147 + justify-content: center; 148 + align-items: center; 149 + 150 + & :global(svg) { 151 + stroke: var(--typo-body); 152 + } 153 + } 154 + </style> 155 + 156 + <style 157 + set:html={` 158 + :root { 159 + color-scheme: ${ 160 + Astro.cookies.get("colour-mode")?.value === "light" 161 + ? "light" 162 + : Astro.cookies.get("colour-mode")?.value === "dark" 163 + ? "dark" 164 + : "light dark" 165 + }; 166 + 167 + --bg-main: light-dark(${light.background.main}, ${dark.background.main}); 168 + --bg-secondary: light-dark(${light.background.secondary}, ${dark.background.secondary}); 169 + --bg-code: light-dark(${light.background.code}, ${dark.background.code}); 170 + 171 + --typo-body: light-dark(${light.typography.body}, ${dark.typography.body}); 172 + --typo-heading: light-dark(${light.typography.heading}, ${dark.typography.heading}); 173 + --typo-subheading: light-dark(${light.typography.subheading}, ${dark.typography.subheading}); 174 + --typo-code: light-dark(${light.typography.code}, ${dark.typography.code}); 175 + 176 + --typo-url: light-dark(${light.typography.url}, ${dark.typography.url}); 177 + --typo-visited: light-dark(${light.typography.visited}, ${dark.typography.visited}); 178 + 179 + --rainbow-0: ${rainbow[0]}; 180 + --rainbow-1: ${rainbow[1]}; 181 + --rainbow-2: ${rainbow[2]}; 182 + --rainbow-3: ${rainbow[3]}; 183 + --rainbow-4: ${rainbow[4]}; 184 + --rainbow-5: ${rainbow[5]}; 185 + } 186 + `} 187 + ></style>
+12 -15
src/pages/blog/index.astro
··· 2 2 import Base from "@/Base.astro"; 3 3 import Post from "@/components/blog/Post.astro"; 4 4 import Background from "@/components/blog/Background.astro"; 5 - import Nav from "@/components/generic/Nav.astro"; 5 + import Nav from "@/components/navigation/Nav.astro"; 6 6 7 7 import Rss from "@/assets/rss.svg"; 8 8 ··· 10 10 import { blog } from "@/config"; 11 11 12 12 const posts = await getCollection("blog").then((x) => 13 - x.sort((a, b) => b.data.date.getTime() - a.data.date.getTime()) 13 + x.sort((a, b) => b.data.date.getTime() - a.data.date.getTime()), 14 14 ); 15 - const nav = await getCollection("nav").then((x) => x.map((x) => x.data)); 16 15 --- 17 16 18 17 <style> ··· 22 21 height: 2em; 23 22 aspect-ratio: 1; 24 23 padding: 0.2em; 25 - border-radius: 0.5rem; 24 + border-radius: 5px; 26 25 27 26 & svg { 28 27 width: 1.6em; ··· 31 30 } 32 31 33 32 heading { 34 - font-size: 1.6rem; 35 33 margin-bottom: var(--y-gap); 36 34 color: white; 37 35 ··· 44 42 justify-content: space-between; 45 43 46 44 & a { 47 - margin: 0.5rem 1rem; 45 + margin: 5px 10px; 48 46 } 49 47 } 50 48 51 49 .top-offset { 52 50 height: var(--top-gap); 53 51 } 52 + 53 + :global(#nav-menu) { 54 + background-color: black; 55 + padding: 10px; 56 + border-radius: 0 0 50% 0; 57 + } 54 58 </style> 55 59 56 60 <Base title="Blog"> 61 + <Nav /> 57 62 <Background /> 58 63 <main> 59 - <heading style={`--y-gap: ${blog.post.yGap}rem`}> 60 - <Nav data={nav} current="Blog" /> 61 - 62 - <a href="/rss.xml" aria-label="Rss Feed"> 63 - <Rss width="1.6em" height="1.6em" /> 64 - </a> 65 - </heading> 66 - 67 - <div class="top-offset" style={`--top-gap: ${blog.post.topYGap}rem;`}></div> 64 + <div class="top-offset" style={`--top-gap: ${blog.post.topYGap}px;`}></div> 68 65 69 66 { 70 67 posts.map((x, i) => (
+12 -51
src/pages/index.astro
··· 1 1 --- 2 - // import Map from "@/components/map.astro" 3 2 import Base from "@/Base.astro"; 4 - import Map from "@/components/index/Map.astro"; 3 + import Nav from "@/components/navigation/Nav.astro"; 5 4 --- 6 5 7 - <Base title="home"> 8 - <style slot="head"> 9 - body { 10 - width: 100vw; 11 - height: 100vh; 12 - display: flex; 13 - align-items: center; 14 - justify-content: center; 15 - flex-direction: column; 16 - gap: 5rem; 17 - padding: 0; 18 - margin: 0; 19 - background-color: #011627; 20 - color: #f8f8f2; 21 - } 22 - </style> 23 - <h1>vielle.dev ๐Ÿชค | wip</h1> 24 - <Map 25 - locations={[ 26 - { 27 - colour: "red", 28 - name: "Blog", 29 - href: "/blog", 30 - x: 6, 31 - y: 2, 32 - }, 33 - { 34 - colour: "magenta", 35 - name: "Projects", 36 - href: "/projects", 37 - x: 6, 38 - y: 5, 39 - }, 40 - { 41 - colour: "blue", 42 - name: "Hobbies", 43 - href: "/hobbies", 44 - x: 1, 45 - y: 4, 46 - }, 47 - { 48 - colour: "cyan", 49 - name: "Linktree", 50 - href: "/linktree", 51 - x: 3, 52 - y: 2, 53 - }, 54 - ]} 55 - /> 6 + <Base> 7 + <Nav /> 56 8 </Base> 9 + 10 + <style> 11 + body { 12 + width: 100vw; 13 + height: 100vh; 14 + background-color: #011627; 15 + color: #f8f8f2; 16 + } 17 + </style>
-1
src/posts
··· 1 - ../posts
-5
svelte.config.js
··· 1 - import { vitePreprocess } from "@astrojs/svelte"; 2 - 3 - export default { 4 - preprocess: vitePreprocess(), 5 - };
-86
wrangler.toml
··· 1 - #:schema node_modules/wrangler/config-schema.json 2 - name = "astral-powers-v2" 3 - compatibility_date = "2025-01-09" 4 - compatibility_flags = ["nodejs_compat"] 5 - pages_build_output_dir = "./dist" 6 - 7 - # Automatically place your workloads in an optimal location to minimize latency. 8 - # If you are running back-end logic in a Pages Function, running it closer to your back-end infrastructure 9 - # rather than the end user may result in better performance. 10 - # Docs: https://developers.cloudflare.com/pages/functions/smart-placement/#smart-placement 11 - # [placement] 12 - # mode = "smart" 13 - 14 - # Variable bindings. These are arbitrary, plaintext strings (similar to environment variables) 15 - # Docs: 16 - # - https://developers.cloudflare.com/pages/functions/bindings/#environment-variables 17 - # Note: Use secrets to store sensitive data. 18 - # - https://developers.cloudflare.com/pages/functions/bindings/#secrets 19 - # [vars] 20 - # MY_VARIABLE = "production_value" 21 - 22 - # Bind the Workers AI model catalog. Run machine learning models, powered by serverless GPUs, on Cloudflareโ€™s global network 23 - # Docs: https://developers.cloudflare.com/pages/functions/bindings/#workers-ai 24 - # [ai] 25 - # binding = "AI" 26 - 27 - # Bind a D1 database. D1 is Cloudflareโ€™s native serverless SQL database. 28 - # Docs: https://developers.cloudflare.com/pages/functions/bindings/#d1-databases 29 - # [[d1_databases]] 30 - # binding = "MY_DB" 31 - # database_name = "my-database" 32 - # database_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" 33 - 34 - # Bind a Durable Object. Durable objects are a scale-to-zero compute primitive based on the actor model. 35 - # Durable Objects can live for as long as needed. Use these when you need a long-running "server", such as in realtime apps. 36 - # Docs: https://developers.cloudflare.com/workers/runtime-apis/durable-objects 37 - # [[durable_objects.bindings]] 38 - # name = "MY_DURABLE_OBJECT" 39 - # class_name = "MyDurableObject" 40 - # script_name = 'my-durable-object' 41 - 42 - # Bind a KV Namespace. Use KV as persistent storage for small key-value pairs. 43 - # Docs: https://developers.cloudflare.com/pages/functions/bindings/#kv-namespaces 44 - # [[kv_namespaces]] 45 - # binding = "MY_KV_NAMESPACE" 46 - # id = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" 47 - 48 - # Bind a Queue producer. Use this binding to schedule an arbitrary task that may be processed later by a Queue consumer. 49 - # Docs: https://developers.cloudflare.com/pages/functions/bindings/#queue-producers 50 - # [[queues.producers]] 51 - # binding = "MY_QUEUE" 52 - # queue = "my-queue" 53 - 54 - # Bind an R2 Bucket. Use R2 to store arbitrarily large blobs of data, such as files. 55 - # Docs: https://developers.cloudflare.com/pages/functions/bindings/#r2-buckets 56 - # [[r2_buckets]] 57 - # binding = "MY_BUCKET" 58 - # bucket_name = "my-bucket" 59 - 60 - # Bind another Worker service. Use this binding to call another Worker without network overhead. 61 - # Docs: https://developers.cloudflare.com/pages/functions/bindings/#service-bindings 62 - # [[services]] 63 - # binding = "MY_SERVICE" 64 - # service = "my-service" 65 - 66 - # To use different bindings for preview and production environments, follow the examples below. 67 - # When using environment-specific overrides for bindings, ALL bindings must be specified on a per-environment basis. 68 - # Docs: https://developers.cloudflare.com/pages/functions/wrangler-configuration#environment-specific-overrides 69 - 70 - ######## PREVIEW environment config ######## 71 - 72 - # [env.preview.vars] 73 - # API_KEY = "xyz789" 74 - 75 - # [[env.preview.kv_namespaces]] 76 - # binding = "MY_KV_NAMESPACE" 77 - # id = "<PREVIEW_NAMESPACE_ID>" 78 - 79 - ######## PRODUCTION environment config ######## 80 - 81 - # [env.production.vars] 82 - # API_KEY = "abc123" 83 - 84 - # [[env.production.kv_namespaces]] 85 - # binding = "MY_KV_NAMESPACE" 86 - # id = "<PRODUCTION_NAMESPACE_ID>"