Files for my website bwc9876.dev

Add Some More Projects

+599 -76
+1
.markdownlint.yml
··· 1 + MD041: false
+1 -2
astro.config.mjs
··· 13 13 image({ 14 14 serviceEntryPoint: "@astrojs/image/sharp" 15 15 }), 16 - sitemap(), 17 - react() 16 + sitemap() 18 17 ] 19 18 });
+1 -3
package.json
··· 20 20 "@types/react": "^18.0.21", 21 21 "@types/react-dom": "^18.0.6", 22 22 "astro": "^2.4.5", 23 - "react": "^18.0.0", 24 - "react-dom": "^18.0.0", 25 - "react-icons": "^4.8.0", 23 + "astro-icon": "^0.8.0", 26 24 "sharp": "^0.32.1" 27 25 }, 28 26 "devDependencies": {
+249 -20
pnpm-lock.yaml
··· 25 25 astro: 26 26 specifier: ^2.4.5 27 27 version: 2.4.5(sharp@0.32.1) 28 - react: 29 - specifier: ^18.0.0 30 - version: 18.2.0 31 - react-dom: 32 - specifier: ^18.0.0 33 - version: 18.2.0(react@18.2.0) 34 - react-icons: 35 - specifier: ^4.8.0 36 - version: 4.8.0(react@18.2.0) 28 + astro-icon: 29 + specifier: ^0.8.0 30 + version: 0.8.0 37 31 sharp: 38 32 specifier: ^0.32.1 39 33 version: 0.32.1 ··· 1012 1006 rollup: 3.21.7 1013 1007 dev: false 1014 1008 1009 + /@trysound/sax@0.2.0: 1010 + resolution: 1011 + { 1012 + integrity: sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA== 1013 + } 1014 + engines: { node: ">=10.13.0" } 1015 + dev: false 1016 + 1015 1017 /@types/acorn@4.0.6: 1016 1018 resolution: 1017 1019 { ··· 1355 1357 hasBin: true 1356 1358 dev: false 1357 1359 1360 + /astro-icon@0.8.0: 1361 + resolution: 1362 + { 1363 + integrity: sha512-sCzhZcl46BIyLJVg89hq5J4uQABn2psmGvK4cUaxib+M/kBI3TrPy1w3g2O5h/WcrTHLVN9LyxjgkD4lEJJFpw== 1364 + } 1365 + dependencies: 1366 + node-fetch: 3.3.1 1367 + resolve-pkg: 2.0.0 1368 + svgo: 2.8.0 1369 + dev: false 1370 + 1358 1371 /astro@2.4.5(sharp@0.32.1): 1359 1372 resolution: 1360 1373 { ··· 1481 1494 buffer: 6.0.3 1482 1495 inherits: 2.0.4 1483 1496 readable-stream: 3.6.2 1497 + dev: false 1498 + 1499 + /boolbase@1.0.0: 1500 + resolution: 1501 + { 1502 + integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww== 1503 + } 1484 1504 dev: false 1485 1505 1486 1506 /boxen@6.2.1: ··· 1780 1800 } 1781 1801 dev: false 1782 1802 1803 + /commander@7.2.0: 1804 + resolution: 1805 + { 1806 + integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== 1807 + } 1808 + engines: { node: ">= 10" } 1809 + dev: false 1810 + 1783 1811 /common-ancestor-path@1.0.1: 1784 1812 resolution: 1785 1813 { ··· 1813 1841 shebang-command: 2.0.0 1814 1842 which: 2.0.2 1815 1843 1844 + /css-select@4.3.0: 1845 + resolution: 1846 + { 1847 + integrity: sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ== 1848 + } 1849 + dependencies: 1850 + boolbase: 1.0.0 1851 + css-what: 6.1.0 1852 + domhandler: 4.3.1 1853 + domutils: 2.8.0 1854 + nth-check: 2.1.1 1855 + dev: false 1856 + 1857 + /css-tree@1.1.3: 1858 + resolution: 1859 + { 1860 + integrity: sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q== 1861 + } 1862 + engines: { node: ">=8.0.0" } 1863 + dependencies: 1864 + mdn-data: 2.0.14 1865 + source-map: 0.6.1 1866 + dev: false 1867 + 1868 + /css-what@6.1.0: 1869 + resolution: 1870 + { 1871 + integrity: sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw== 1872 + } 1873 + engines: { node: ">= 6" } 1874 + dev: false 1875 + 1876 + /csso@4.2.0: 1877 + resolution: 1878 + { 1879 + integrity: sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA== 1880 + } 1881 + engines: { node: ">=8.0.0" } 1882 + dependencies: 1883 + css-tree: 1.1.3 1884 + dev: false 1885 + 1816 1886 /csstype@3.1.2: 1817 1887 resolution: 1818 1888 { 1819 1889 integrity: sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ== 1820 1890 } 1891 + dev: false 1892 + 1893 + /data-uri-to-buffer@4.0.1: 1894 + resolution: 1895 + { 1896 + integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A== 1897 + } 1898 + engines: { node: ">= 12" } 1821 1899 dev: false 1822 1900 1823 1901 /debug@4.3.4: ··· 1944 2022 { 1945 2023 integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA== 1946 2024 } 2025 + dev: false 2026 + 2027 + /dom-serializer@1.4.1: 2028 + resolution: 2029 + { 2030 + integrity: sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag== 2031 + } 2032 + dependencies: 2033 + domelementtype: 2.3.0 2034 + domhandler: 4.3.1 2035 + entities: 2.2.0 2036 + dev: false 2037 + 2038 + /domelementtype@2.3.0: 2039 + resolution: 2040 + { 2041 + integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw== 2042 + } 2043 + dev: false 2044 + 2045 + /domhandler@4.3.1: 2046 + resolution: 2047 + { 2048 + integrity: sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ== 2049 + } 2050 + engines: { node: ">= 4" } 2051 + dependencies: 2052 + domelementtype: 2.3.0 2053 + dev: false 2054 + 2055 + /domutils@2.8.0: 2056 + resolution: 2057 + { 2058 + integrity: sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A== 2059 + } 2060 + dependencies: 2061 + dom-serializer: 1.4.1 2062 + domelementtype: 2.3.0 2063 + domhandler: 4.3.1 1947 2064 dev: false 1948 2065 1949 2066 /dset@3.1.2: ··· 2001 2118 once: 1.4.0 2002 2119 dev: false 2003 2120 2121 + /entities@2.2.0: 2122 + resolution: 2123 + { 2124 + integrity: sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A== 2125 + } 2126 + dev: false 2127 + 2004 2128 /es-module-lexer@1.2.1: 2005 2129 resolution: 2006 2130 { ··· 2251 2375 format: 0.2.2 2252 2376 dev: false 2253 2377 2378 + /fetch-blob@3.2.0: 2379 + resolution: 2380 + { 2381 + integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ== 2382 + } 2383 + engines: { node: ^12.20 || >= 14.13 } 2384 + dependencies: 2385 + node-domexception: 1.0.0 2386 + web-streams-polyfill: 3.2.1 2387 + dev: false 2388 + 2254 2389 /fill-range@7.0.1: 2255 2390 resolution: 2256 2391 { ··· 2298 2433 integrity: sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww== 2299 2434 } 2300 2435 engines: { node: ">=0.4.x" } 2436 + dev: false 2437 + 2438 + /formdata-polyfill@4.0.10: 2439 + resolution: 2440 + { 2441 + integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g== 2442 + } 2443 + engines: { node: ">=12.20.0" } 2444 + dependencies: 2445 + fetch-blob: 3.2.0 2301 2446 dev: false 2302 2447 2303 2448 /fs-constants@1.0.0: ··· 3262 3407 "@types/mdast": 3.0.11 3263 3408 dev: false 3264 3409 3410 + /mdn-data@2.0.14: 3411 + resolution: 3412 + { 3413 + integrity: sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow== 3414 + } 3415 + dev: false 3416 + 3265 3417 /merge-stream@2.0.0: 3266 3418 resolution: 3267 3419 { ··· 3836 3988 } 3837 3989 dev: false 3838 3990 3991 + /node-domexception@1.0.0: 3992 + resolution: 3993 + { 3994 + integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ== 3995 + } 3996 + engines: { node: ">=10.5.0" } 3997 + dev: false 3998 + 3999 + /node-fetch@3.3.1: 4000 + resolution: 4001 + { 4002 + integrity: sha512-cRVc/kyto/7E5shrWca1Wsea4y6tL9iYJE5FBCius3JQfb/4P4I295PfhgbJQBLTx6lATE4z+wK0rPM4VS2uow== 4003 + } 4004 + engines: { node: ^12.20.0 || ^14.13.1 || >=16.0.0 } 4005 + dependencies: 4006 + data-uri-to-buffer: 4.0.1 4007 + fetch-blob: 3.2.0 4008 + formdata-polyfill: 4.0.10 4009 + dev: false 4010 + 3839 4011 /node-releases@2.0.10: 3840 4012 resolution: 3841 4013 { ··· 3868 4040 engines: { node: ^12.20.0 || ^14.13.1 || >=16.0.0 } 3869 4041 dependencies: 3870 4042 path-key: 4.0.0 4043 + 4044 + /nth-check@2.1.1: 4045 + resolution: 4046 + { 4047 + integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w== 4048 + } 4049 + dependencies: 4050 + boolbase: 1.0.0 4051 + dev: false 3871 4052 3872 4053 /ometa@0.2.2: 3873 4054 resolution: ··· 4238 4419 scheduler: 0.23.0 4239 4420 dev: false 4240 4421 4241 - /react-icons@4.8.0(react@18.2.0): 4242 - resolution: 4243 - { 4244 - integrity: sha512-N6+kOLcihDiAnj5Czu637waJqSnwlMNROzVZMhfX68V/9bu9qHaMIJC4UdozWoOk57gahFCNHwVvWzm0MTzRjg== 4245 - } 4246 - peerDependencies: 4247 - react: "*" 4248 - dependencies: 4249 - react: 18.2.0 4250 - dev: false 4251 - 4252 4422 /react@18.2.0: 4253 4423 resolution: 4254 4424 { ··· 4400 4570 retext: 8.1.0 4401 4571 retext-smartypants: 5.2.0 4402 4572 unist-util-visit: 4.1.2 4573 + dev: false 4574 + 4575 + /resolve-from@5.0.0: 4576 + resolution: 4577 + { 4578 + integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== 4579 + } 4580 + engines: { node: ">=8" } 4581 + dev: false 4582 + 4583 + /resolve-pkg@2.0.0: 4584 + resolution: 4585 + { 4586 + integrity: sha512-+1lzwXehGCXSeryaISr6WujZzowloigEofRB+dj75y9RRa/obVcYgbHJd53tdYw8pvZj8GojXaaENws8Ktw/hQ== 4587 + } 4588 + engines: { node: ">=8" } 4589 + dependencies: 4590 + resolve-from: 5.0.0 4403 4591 dev: false 4404 4592 4405 4593 /resolve@1.22.2: ··· 4721 4909 resolution: 4722 4910 { 4723 4911 integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== 4912 + } 4913 + engines: { node: ">=0.10.0" } 4914 + dev: false 4915 + 4916 + /source-map@0.6.1: 4917 + resolution: 4918 + { 4919 + integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== 4724 4920 } 4725 4921 engines: { node: ">=0.10.0" } 4726 4922 dev: false ··· 4747 4943 } 4748 4944 dev: false 4749 4945 4946 + /stable@0.1.8: 4947 + resolution: 4948 + { 4949 + integrity: sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w== 4950 + } 4951 + deprecated: "Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility" 4952 + dev: false 4953 + 4750 4954 /stdin-discarder@0.1.0: 4751 4955 resolution: 4752 4956 { ··· 4926 5130 integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== 4927 5131 } 4928 5132 engines: { node: ">= 0.4" } 5133 + dev: false 5134 + 5135 + /svgo@2.8.0: 5136 + resolution: 5137 + { 5138 + integrity: sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg== 5139 + } 5140 + engines: { node: ">=10.13.0" } 5141 + hasBin: true 5142 + dependencies: 5143 + "@trysound/sax": 0.2.0 5144 + commander: 7.2.0 5145 + css-select: 4.3.0 5146 + css-tree: 1.1.3 5147 + csso: 4.2.0 5148 + picocolors: 1.0.0 5149 + stable: 0.1.8 4929 5150 dev: false 4930 5151 4931 5152 /synckit@0.8.5: ··· 5412 5633 { 5413 5634 integrity: sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ== 5414 5635 } 5636 + dev: false 5637 + 5638 + /web-streams-polyfill@3.2.1: 5639 + resolution: 5640 + { 5641 + integrity: sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q== 5642 + } 5643 + engines: { node: ">= 8" } 5415 5644 dev: false 5416 5645 5417 5646 /which-pm-runs@1.1.0:
public/project-images/default.webp src/images/default-project-image.webp
+5 -3
src/components/IconLink.astro
··· 1 1 --- 2 - import type { IconType } from "react-icons"; 2 + import { Icon } from "astro-icon"; 3 3 import ExtLink from "./ExtLink.astro"; 4 4 5 5 export interface Props { 6 6 href: string; 7 + icon: string; 8 + size: number; 7 9 label: string; 8 10 placement: "top" | "left" | "right" | "bottom"; 9 11 } 10 12 11 - const { href, label, placement } = Astro.props; 13 + const { href, label, placement, icon, size } = Astro.props; 12 14 --- 13 15 14 16 <ExtLink data-tooltip={label} data-placement={placement} href={href}> 15 - <slot /> 17 + <Icon width={size} height={size} name={`bi:${icon}`} /> 16 18 </ExtLink>
+23 -12
src/components/projects/ProjectCard.astro
··· 11 11 12 12 <article> 13 13 <header> 14 - <div> 15 - <a href={`/projects/${project.slug}/`}> 14 + <a href={`/projects/${project.slug}/`}> 15 + <div> 16 16 <Image 17 - width={1920} 18 - height={1800} 17 + width={500} 18 + height={500} 19 19 format="webp" 20 20 position="center" 21 21 alt={project.data.name} 22 - src={`/project-images/${project.data.image ?? "default"}.webp`} 22 + src={project.data.image?.src ?? "/project-images/default.webp"} 23 23 /> 24 - </a> 25 - </div> 26 - <strong class="project-name">{project.data.name}</strong> 24 + </div> 25 + <strong class="project-name">{project.data.name}</strong> 26 + </a> 27 27 </header> 28 - {project.data.summary} 28 + <span>{project.data.summary}</span> 29 29 <footer> 30 30 <small>{project.data.tags.join(" • ")}</small> 31 31 </footer> ··· 35 35 article { 36 36 margin-bottom: 0; 37 37 margin-top: 0; 38 + display: flex; 39 + flex-direction: column; 40 + } 41 + 42 + article:hover { 43 + transform: translateY(calc(var(--spacing) * -1)); 44 + transition: transform cubic-bezier(0.68, -0.55, 0.27, 1.55) 0.4s; 38 45 } 39 46 40 47 article:hover img { 41 - transform: scale(1.25); 48 + transform: scale(1.06); 42 49 } 43 50 44 - header > div { 51 + header > a > div { 45 52 overflow: hidden; 46 53 width: 100%; 47 54 margin-bottom: var(--spacing); 48 55 } 49 56 50 - header > strong { 57 + header > a > strong { 51 58 font-size: larger; 59 + } 60 + 61 + span { 62 + flex-grow: 1; 52 63 } 53 64 54 65 img {
+20 -1
src/components/projects/ProjectGrid.astro
··· 2 2 import ProjectCard from "./ProjectCard.astro"; 3 3 import { getCollection } from "astro:content"; 4 4 5 + export interface Props { 6 + limitTo?: number; 7 + } 8 + 5 9 const projectEntries = await getCollection("projects"); 10 + let entries = projectEntries.sort((a, b) => { 11 + const [yearA, yearB] = [a.data.timespan.from, b.data.timespan.from]; 12 + if (yearA === yearB) { 13 + return 0; 14 + } else if (yearA > yearB) { 15 + return -1; 16 + } else { 17 + return 1; 18 + } 19 + }); 20 + 21 + const { limitTo } = Astro.props; 22 + if (limitTo) { 23 + entries = entries.slice(0, limitTo); 24 + } 6 25 --- 7 26 8 27 <div> 9 - {projectEntries.map((p) => <ProjectCard project={p} />)} 28 + {entries.map((p) => <ProjectCard project={p} />)} 10 29 </div> 11 30 12 31 <style>
+7 -1
src/content/config.ts
··· 9 9 from: z.number(), 10 10 to: z.number().or(z.string()).optional() 11 11 }), 12 - image: z.string().optional(), 12 + image: z 13 + .object({ 14 + src: z.string(), 15 + width: z.number(), 16 + height: z.number() 17 + }) 18 + .optional(), 13 19 links: z 14 20 .object({ 15 21 github: z.string().optional(),
+30
src/content/projects/coop-scheduler.mdx
··· 1 + --- 2 + name: Co-op Scheduler 3 + summary: A WinForms app for scheduling students for job shadows 4 + timespan: 5 + from: 2022 6 + tags: 7 + - c# 8 + - win-forms 9 + - school 10 + links: 11 + github: Bwc9876/Coop-Schedule 12 + image: 13 + src: https://user-images.githubusercontent.com/25644444/190924859-c55535fc-5d1b-4f08-9e1b-620b4060b335.png 14 + width: 600 15 + height: 518 16 + --- 17 + 18 + import ExtLink from "@components/ExtLink.astro"; 19 + export const components = { a: ExtLink }; 20 + 21 + This program was a favor for one of the teachers in my school, she needed a 22 + program that generates a schedule automatically for students 23 + following some simple rules. 24 + 25 + It uses the concepts of [Wave Function Collapse](https://en.wikipedia.org/wiki/Wave_function_collapse) 26 + to generate the schedule, for more info check out the program's [README](https://github.com/Bwc9876/Coop-Schedule#readme). 27 + 28 + In all I'm happy to have done this, I go to learn some algorithms and experiment 29 + with bitwise arithmetic. The UI could have been more tactful, but for WinForms I'm 30 + satisfied with how it came out.
+31
src/content/projects/djazztro.mdx
··· 1 + --- 2 + name: Djazztro 3 + summary: The web framework for astronauts with deadlines 4 + timespan: 5 + from: 2022 6 + tags: 7 + - web 8 + - astro 9 + - python 10 + - django 11 + links: 12 + github: Bwc9876/Djazztro 13 + image: 14 + src: https://user-images.githubusercontent.com/25644444/201508399-c98f41ab-3790-4c20-b82c-5b47ff3370f2.png 15 + width: 367 16 + height: 401 17 + --- 18 + 19 + import ExtLink from "@components/ExtLink.astro"; 20 + export const components = { a: ExtLink }; 21 + 22 + Djazztro combines the versatility of [Django](https://www.djangoproject.com/) 23 + with the elegance of the [Astro](https://astro.build) static site generator. 24 + 25 + I thought about this project a lot before I made it. I loved how Django handled 26 + everything backend, and Astro's way of handling frontend, so I tried to combine 27 + the two. 28 + 29 + In the end I'm semi-happy with how it turned out, good experience with packaging 30 + python and javascript packages, as well as making some GitHub actions 31 + to automate it all.
+33
src/content/projects/menagerie.mdx
··· 1 + --- 2 + name: Menagerie 3 + summary: A simple and quick SSG framework for documentation sites 4 + timespan: 5 + from: 2022 6 + tags: 7 + - web 8 + - bootstrap 9 + - python 10 + - jinja 11 + links: 12 + github: Bwc9876/menagerie 13 + other: 14 + Docs: https://bwc9876.github.io/menagerie/ 15 + image: 16 + src: https://bwc9876.github.io/menagerie/images/logo.svg 17 + width: 400 18 + height: 400 19 + --- 20 + 21 + import ExtLink from "@components/ExtLink.astro"; 22 + export const components = { a: ExtLink }; 23 + 24 + Menagerie is a simple SSG framework for generating documentation sites from 25 + simple markdown files. It's main feature is its ability to create pages for schema 26 + pages. It uses Python and [Jinja](https://jinja.palletsprojects.com/en/3.1.x/) 27 + for generating the site, 28 + [Bootstrap](https://getbootstrap.com/) for styling, and 29 + [json-schema-for-humans](https://github.com/coveooss/json-schema-for-humans) 30 + 31 + This was my first public package, I learned a lot when making like how to make 32 + python packages and designing a framework that other people will use. 33 + It was made to create the [New Horizons Docs](https://nh.outerwildsmods.com).
+48
src/content/projects/ow-mod-man.mdx
··· 1 + --- 2 + name: Outer Wilds Mod Manager 3 + summary: A fully-featured manager for Outer Wilds mods 4 + timespan: 5 + from: 2023 6 + tags: 7 + - tauri 8 + - rust 9 + - web 10 + - react 11 + - outer-wilds 12 + links: 13 + github: Bwc9876/ow-mod-man 14 + other: 15 + Mods Website: https://outerwildsmods.com/mod-manager 16 + image: 17 + src: https://github.com/Bwc9876/ow-mod-man/blob/main/.github/assets/screenshots/main.png?raw=true 18 + width: 600 19 + height: 518 20 + --- 21 + 22 + import ExtLink from "@components/ExtLink.astro"; 23 + export const components = { a: ExtLink }; 24 + 25 + This was my first fully-fledged desktop app, it was made with [Tauri](https://tauri.app) 26 + which is a frontend-agnostic application development framework that uses Rust. 27 + 28 + For the frontend portion I went with [React](https://react.dev/) as the UI framework 29 + and [Pico CSS](https://picocss.com/) for styling. 30 + 31 + It was based on the [old mod manager](https://github.com/ow-mods/ow-mod-manager) 32 + and tried to improve functionality and Linux support. 33 + 34 + Development took a little bit over 4 months, I made a 35 + [core package](https://github.com/Bwc9876/ow-mod-man/tree/main/owmods_core) 36 + so anyone can interface with mods, a 37 + [CLI](https://github.com/Bwc9876/ow-mod-man/tree/main/owmods_cli) for 38 + easily managing mods without a full GUI, 39 + and a [GUI](https://github.com/Bwc9876/ow-mod-man/tree/main/owmods_gui) for 40 + user-friendliness. 41 + 42 + It was my first time distributing a full app to multiple sources and testing 43 + compatibility across multiple platforms. Getting Steam Deck support via [Flathub](https://flathub.org/apps/com.outerwildsmods.owmods_gui) 44 + was especially challenging. 45 + 46 + In hindsight, I think that Pico CSS wasn't a great fit for this project as it's 47 + more oriented towards simple static sites. 48 + Something like Material UI might have worked better.
+47
src/content/projects/ow-new-horizons.mdx
··· 1 + --- 2 + name: Outer Wilds New Horizons 3 + summary: A modding framework for creating custom content in Outer Wilds 4 + timespan: 5 + from: 2021 6 + tags: 7 + - c# 8 + - unity 9 + - outer-wilds 10 + links: 11 + github: Outer-Wilds-New-Horizons/new-horizons 12 + other: 13 + Documentation Site: https://nh.outerwildsmods.com/ 14 + image: 15 + src: https://nh.outerwildsmods.com/images/home/home_logo.webp 16 + width: 1200 17 + height: 400 18 + --- 19 + 20 + import ExtLink from "@components/ExtLink.astro"; 21 + export const components = { a: ExtLink }; 22 + 23 + For a few years I've been contributing to New Horizons, 24 + a modding framework for generating Outer Wilds planets from simple JSON and XML files. 25 + 26 + Some of my contributions include: 27 + 28 + - Adding custom ship logs 29 + - Adding a "Reload Configs" button for refreshing JSON files without needing to 30 + restart the game 31 + - Implementing various 32 + [CI/CD pipelines](https://github.com/Outer-Wilds-New-Horizons/new-horizons/tree/main/.github/workflows) 33 + with GitHub Actions. 34 + - Implementing a schema exporter system to allow for generation of JSON schemas 35 + from C# classes 36 + - Writing & Implementing The 37 + [Documentation Site](https://nh.outerwildsmods.com/) (made with [menagerie](/projects/menagerie)) 38 + 39 + New Horizons was my first time working in a team and collaborating with others 40 + on code, it took some getting used to but in the end this project has given 41 + invaluable skills in being a team-player. 42 + 43 + The documentation website was also fun to implement, while I did 44 + this part solo, the docs made me realize how much I enjoy writing 45 + documentation and guides to help others. [Menagerie](/projects/menagerie) was 46 + built because of the NH docs, I wanted to make a simple SSG system that supported 47 + JSON schemas.
+9 -4
src/content/projects/portfolio-site.mdx
··· 9 9 links: 10 10 github: Bwc9876/portfolio-site 11 11 other: 12 - Website: https://bwc9876.dev/ 12 + "Website (...In case you didn't know it???)": https://bwc9876.dev/ 13 13 --- 14 14 15 15 import ExtLink from "@components/ExtLink.astro"; 16 16 export const components = { a: ExtLink }; 17 17 18 - This is the site you're looking at, it was made in [Astro](https://astro.build) and the styling used is [Pico CSS](https://picocss.com/). 18 + This is the site you're looking at, it was made in [Astro](https://astro.build) 19 + and the styling used is [Pico CSS](https://picocss.com/). 19 20 20 - After using my [GitHub profile](https://github.com/Bwc9876) as a portfolio for a few years I decided to switch things up and make a site, it was a good opportunity to learn some more about Astro's amazing content system and other features. 21 + After using my [GitHub profile](https://github.com/Bwc9876) as a portfolio for a 22 + few years I decided to switch things up and make a site, 23 + it was a good opportunity to learn some more about Astro's 24 + amazing content system and other features. 21 25 22 - It's styling is fairly simple, nothing too flashy. But I'm happy with how it turned out, nice simple and fast. 26 + It's styling is fairly simple, nothing too flashy. 27 + But I'm happy with how it turned out, nice and simple.
+28
src/content/projects/tech-trends.mdx
··· 1 + --- 2 + name: Tech Trends 3 + summary: A website showing the latest tech trends of 2022 & 2023 4 + timespan: 5 + from: 2022 6 + to: 2023 7 + tags: 8 + - web 9 + - astro 10 + - school 11 + links: 12 + github: Bwc9876/TechTrends2022 13 + other: 14 + Website: https://bwc9876.github.io/TechTrends2022/ 15 + image: 16 + src: "https://repository-images.githubusercontent.com/530871937/f8029203-1f9d-4f02-be75-40873b286797" 17 + width: 800 18 + height: 800 19 + --- 20 + 21 + import ExtLink from "@components/ExtLink.astro"; 22 + export const components = { a: ExtLink }; 23 + 24 + This website was made for school, it was my first time [Astro](https://astro.build/) 25 + which was neat. 26 + 27 + Not very much to report, a pretty simple website. I went a bit crazy 28 + with gradients in it if you couldn't tell.
src/images/tech-trends.png

This is a binary file and will not be displayed.

+8 -5
src/layouts/Layout.astro
··· 2 2 import { Image } from "@astrojs/image/components"; 3 3 import IconLink from "@components/IconLink.astro"; 4 4 import "@picocss/pico/css/pico.min.css"; 5 - 6 - import { BsGithub } from "react-icons/bs"; 5 + import github from "bootstrap-icons/icons/github.svg"; 7 6 8 7 export interface Props { 9 8 title: string; ··· 48 47 </ul> 49 48 <ul> 50 49 <li> 51 - <IconLink href="https://github.com/Bwc9876" label="GitHub" placement="left"> 52 - <BsGithub size="30px" /> 53 - </IconLink> 50 + <IconLink 51 + icon="github" 52 + size={30} 53 + href="https://github.com/Bwc9876" 54 + label="GitHub" 55 + placement="left" 56 + /> 54 57 </li> 55 58 </ul> 56 59 </nav>
+9 -3
src/pages/index.astro
··· 17 17 </p> 18 18 <p> 19 19 I like to try a lot of things, from <ExtLink href="#">modding games</ExtLink> 20 - to building <ExtLink href="#">websites</ExtLink> and <ExtLink href="#"> 20 + to building <ExtLink href="#">websites</ExtLink> and <ExtLink href="/projects/ow-mod-man"> 21 21 desktop apps 22 22 </ExtLink>. I enjoy pretty much any aspect of software development. 23 23 </p> 24 24 <p>On this site you'll find my current and past projects.</p> 25 25 26 - <h2>Projects</h2> 27 - <ProjectGrid /> 26 + <hgroup> 27 + <h1>Recent Projects</h1> 28 + <h2>Sorted By Date</h2> 29 + </hgroup> 30 + <ProjectGrid limitTo={6} /> 31 + <div class="view-more"> 32 + <a href="/projects">View All</a> 33 + </div> 28 34 </Layout>
+41 -18
src/pages/projects/[...slug].astro
··· 1 1 --- 2 2 import { getCollection } from "astro:content"; 3 + import { Image } from "@astrojs/image/components"; 3 4 import Layout from "@layouts/Layout.astro"; 4 - import ExtLink from "@components/ExtLink.astro"; 5 5 import IconLink from "@components/IconLink.astro"; 6 - import { BsGithub, BsLink45Deg } from "react-icons/bs"; 6 + import defaultSrc from "@images/default-project-image.webp"; 7 7 export async function getStaticPaths() { 8 8 const renameEntries = await getCollection("projects"); 9 9 return renameEntries.map((entry) => ({ ··· 16 16 --- 17 17 18 18 <Layout title={entry.data.name}> 19 - <div class="grid"> 19 + <header> 20 + <div class="img-container"> 21 + <Image 22 + fit="inside" 23 + position="center" 24 + format="webp" 25 + background="#18232C" 26 + width={entry.data.image?.width ?? 474} 27 + height={entry.data.image?.height ?? 474} 28 + alt={entry.data.name} 29 + src={entry.data.image?.src ?? defaultSrc} 30 + /> 31 + </div> 20 32 <hgroup> 21 33 <h1>{entry.data.name}</h1> 22 34 <h2> 23 35 {entry.data.timespan.from}{ 24 36 entry.data.timespan.to && <> - {entry.data.timespan.to}</> 25 - } | 37 + } • 26 38 {entry.data.tags.join(" • ")} 27 39 </h2> 28 40 </hgroup> 29 41 <div class="links"> 30 42 { 31 - entry.data.links.github && ( 43 + entry.data.links?.github && ( 32 44 <span> 33 45 <IconLink 46 + icon="github" 47 + size={25} 34 48 label="GitHub" 35 - placement="left" 49 + placement="bottom" 36 50 href={`https://github.com/${entry.data.links.github}`} 37 - > 38 - <BsGithub size="25px" /> 39 - </IconLink> 51 + /> 40 52 </span> 41 53 ) 42 54 } 43 55 { 44 - entry.data.links.other && 56 + entry.data.links?.other && 45 57 Object.keys(entry.data.links.other).map((k) => ( 46 58 <span> 47 - <IconLink href={entry.data.links.other[k]} placement="left" label={k}> 48 - <BsLink45Deg size="25px" /> 49 - </IconLink> 59 + <IconLink 60 + icon="link-45deg" 61 + size={25} 62 + href={entry.data.links.other[k]} 63 + placement="bottom" 64 + label={k} 65 + /> 50 66 </span> 51 67 )) 52 68 } 53 69 </div> 54 - </div> 70 + </header> 55 71 <Content /> 56 72 </Layout> 57 73 58 74 <style> 59 - .grid > hgroup { 60 - margin-bottom: 0; 75 + header > hgroup { 76 + margin-bottom: var(--spacing); 61 77 } 62 78 63 - .grid { 79 + header > div.img-container { 80 + width: 100%; 81 + display: flex; 82 + justify-content: center; 83 + } 84 + 85 + header { 64 86 background-color: var(--card-sectionning-background-color); 65 87 padding: var(--spacing); 66 88 border-radius: 5px; 67 89 margin-bottom: calc(var(--spacing) * 5) !important; 90 + text-align: center; 68 91 } 69 92 70 93 .links { 71 94 display: flex; 72 95 align-content: center; 73 - justify-content: end; 96 + justify-content: center; 74 97 gap: var(--spacing); 75 98 } 76 99
+7 -1
src/pages/projects/index.astro
··· 1 1 --- 2 2 import Layout from "@layouts/Layout.astro"; 3 3 import ProjectGrid from "@components/projects/ProjectGrid.astro"; 4 + import { getCollection } from "astro:content"; 5 + 6 + const projectEntries = (await getCollection("projects")).length; 4 7 --- 5 8 6 9 <Layout title="Projects"> 7 - <h1>My Projects</h1> 10 + <hgroup> 11 + <h1>My Projects</h1> 12 + <h2>Sorted By Year • {projectEntries} Total</h2> 13 + </hgroup> 8 14 <ProjectGrid /> 9 15 </Layout>
+1 -3
tsconfig.json
··· 7 7 "@components/*": ["src/components/*"], 8 8 "@assets/*": ["src/assets/*"], 9 9 "@images/*": ["src/images/*"] 10 - }, 11 - "jsx": "react-jsx", 12 - "jsxImportSource": "react" 10 + } 13 11 } 14 12 }