From 3c40e469889bdce57df08e2cc83d237da7b08417 Mon Sep 17 00:00:00 2001 From: heyjoeway Date: Wed, 4 Dec 2024 20:01:06 +0000 Subject: [PATCH] deploy: 1b63edea33fe30618b7eb1b484eae95e6855b424 --- __data.json | 2 +- _app/immutable/assets/2.CiemoY37.css | 1 - _app/immutable/assets/2.uYDo6Uun.css | 1 + _app/immutable/assets/_layout.CiemoY37.css | 1 - _app/immutable/assets/_layout.uYDo6Uun.css | 1 + _app/immutable/chunks/entry.B8mbJhC8.js | 3 --- _app/immutable/chunks/entry.BDNlJs-a.js | 3 +++ .../{app.DVk21-Gt.js => app.ByzdQ3zT.js} | 4 ++-- _app/immutable/entry/start.B8xNVxTP.js | 1 - _app/immutable/entry/start.BGhs6bZM.js | 1 + .../nodes/{1.wGGJZu2n.js => 1.B-BTWY2M.js} | 2 +- _app/immutable/nodes/11.BvPbAE-y.js | 1 - _app/immutable/nodes/11.HSC-p8U2.js | 1 + _app/immutable/nodes/12.Ca8uRgGS.js | 1 - _app/immutable/nodes/12.D8NuWnIb.js | 1 + _app/immutable/nodes/2.Bxop8xDV.js | 1 - _app/immutable/nodes/2.JLOnKEIi.js | 1 + .../nodes/{7.gsEXZ4EK.js => 7.BFR6zYzy.js} | 2 +- _app/version.json | 2 +- articles/gd-anime-was-a-mistake/index.html | 20 ++++++++-------- articles/gd-mayolo/__data.json | 2 +- articles/gd-mayolo/index.html | 22 ++++++++--------- articles/gd-whiteface/__data.json | 2 +- articles/gd-whiteface/index.html | 22 ++++++++--------- articles/lcatdb/__data.json | 2 +- articles/lcatdb/index.html | 24 +++++++++---------- articles/mac-hidden-files/__data.json | 2 +- articles/mac-hidden-files/index.html | 24 +++++++++---------- articles/new-blog/__data.json | 2 +- articles/new-blog/index.html | 22 ++++++++--------- articles/photo-megapost/__data.json | 2 +- articles/photo-megapost/index.html | 22 ++++++++--------- articles/portfolio/__data.json | 2 +- articles/portfolio/index.html | 24 +++++++++---------- articles/responsive-design/__data.json | 2 +- articles/responsive-design/index.html | 24 +++++++++---------- articles/software-dump-mac/__data.json | 2 +- articles/software-dump-mac/index.html | 22 ++++++++--------- articles/software-dump-multiplat/__data.json | 2 +- articles/software-dump-multiplat/index.html | 22 ++++++++--------- articles/switch-ports/__data.json | 2 +- articles/switch-ports/index.html | 22 ++++++++--------- category/design/__data.json | 2 +- category/design/index.html | 14 +++++------ category/games/__data.json | 2 +- category/games/index.html | 14 +++++------ category/life/__data.json | 2 +- category/life/index.html | 14 +++++------ category/macos/__data.json | 2 +- category/macos/index.html | 14 +++++------ category/meta/__data.json | 2 +- category/meta/index.html | 14 +++++------ category/multiplatform/__data.json | 2 +- category/multiplatform/index.html | 14 +++++------ category/photography/__data.json | 2 +- category/photography/index.html | 14 +++++------ category/portfolio/__data.json | 2 +- category/portfolio/index.html | 14 +++++------ category/programming/__data.json | 2 +- category/programming/index.html | 14 +++++------ category/software/__data.json | 2 +- category/software/index.html | 14 +++++------ category/ui/__data.json | 2 +- category/ui/index.html | 14 +++++------ category/work/__data.json | 2 +- category/work/index.html | 14 +++++------ index.html | 14 +++++------ 67 files changed, 264 insertions(+), 264 deletions(-) delete mode 100644 _app/immutable/assets/2.CiemoY37.css create mode 100644 _app/immutable/assets/2.uYDo6Uun.css delete mode 100644 _app/immutable/assets/_layout.CiemoY37.css create mode 100644 _app/immutable/assets/_layout.uYDo6Uun.css delete mode 100644 _app/immutable/chunks/entry.B8mbJhC8.js create mode 100644 _app/immutable/chunks/entry.BDNlJs-a.js rename _app/immutable/entry/{app.DVk21-Gt.js => app.ByzdQ3zT.js} (74%) delete mode 100644 _app/immutable/entry/start.B8xNVxTP.js create mode 100644 _app/immutable/entry/start.BGhs6bZM.js rename _app/immutable/nodes/{1.wGGJZu2n.js => 1.B-BTWY2M.js} (92%) delete mode 100644 _app/immutable/nodes/11.BvPbAE-y.js create mode 100644 _app/immutable/nodes/11.HSC-p8U2.js delete mode 100644 _app/immutable/nodes/12.Ca8uRgGS.js create mode 100644 _app/immutable/nodes/12.D8NuWnIb.js delete mode 100644 _app/immutable/nodes/2.Bxop8xDV.js create mode 100644 _app/immutable/nodes/2.JLOnKEIi.js rename _app/immutable/nodes/{7.gsEXZ4EK.js => 7.BFR6zYzy.js} (72%) diff --git a/__data.json b/__data.json index 09aba6c..9980018 100644 --- a/__data.json +++ b/__data.json @@ -1 +1 @@ -{"type":"data","nodes":[{"type":"data","data":[{"categories":1},[2,3,4,5,6,7,8,9,10,11,12,13],"design","ui","programming","life","software","meta","photography","work","portfolio","macos","multiplatform","games"],"uses":{},"slash":"always"},{"type":"data","data":[{"articles":1},[2,12,21,31,40,48,57,66,75,83,91,100],{"slug":3,"path":4,"code":5,"fm":6},"lcatdb","/articles/lcatdb","\u003Cscript context=\"module\">\n\texport const metadata = {\"layout\":\"post\",\"title\":\"Retrospective: lcatDB\",\"date\":\"2023-05-18T00:00:00.000Z\",\"categories\":\"programming life software ui\",\"last_modified_at\":\"2023-05-19T00:00:00.000Z\"};\n\tconst { layout, title, date, categories, last_modified_at } = metadata;\n\u003C/script>\n\u003Cscript lang=\"ts\">\n import Image from \"$lib/Image.svelte\";\n import Video from \"$lib/Video.svelte\";\n import Figure from \"$lib/Figure.svelte\";\n \n import imgLogin1 from \"./localhost_3000_login.html.png?as=run\";\n import imgLogin2 from \"./localhost_3000_login.html (1).png?as=run\";\n import vidResponsive from \"./lcatDB Responsive Demo.mp4\";\n import vidPages from \"./lcatDB Pages Demo.mp4\";\n\u003C/script>\n\n\u003Cp>lcatDB (Lake Champlain Anglers’ Temperature Database) was an online database and single page application (SPA) meant to provide a centralized means of recording, accessing, and analyzing vertical water temperature profiles to citizens of the Lake Champlain region. It was supported by the SUNY Plattsburgh Center for Earth & Environmental Science and the Lake Champlain Sea Grant. lcatDB was (what I consider to be) my first attempt at full stack software development. While the app never really took off and is no longer around today, I was able to demonstrate many of my strengths and learned several valuable lessons.\u003C/p>\n\u003Ch1>Client\u003C/h1>\n\u003CFigure>\n \u003CImage src={imgLogin1} />\n \u003CImage src={imgLogin2} />\n\u003C/Figure>\n\u003Cp>The UI of lcatDB very closely follows Google’s (now aged) Material Design guidelines. A hamburger menu is always available for root-level navigation and all elements are kept large, touch-friendly, and readable. Subtle iconography and color is used to draw attention to important elements and differentiate options. One element that I take pride in across most of my projects is responsive CSS, allowing for automatic adaptations to mobile, tablet, and desktop layouts.\u003C/p>\n\u003CFigure>\n \u003CVideo src={vidResponsive} />\n\u003C/Figure>\n\u003Cp>lcatDB’s client was built using several JavaScript libraries, frameworks, and related tools, including:\u003C/p>\n\u003Cul>\n\u003Cli>Grunt (build system)\u003C/li>\n\u003Cli>Cordova (Android/iOS builds)\u003C/li>\n\u003Cli>jQuery\u003C/li>\n\u003Cli>Bootstrap\u003C/li>\n\u003Cli>Leaflet\u003C/li>\n\u003Cli>Mustache (for page templating)\u003C/li>\n\u003C/ul>\n\u003Cp>The client is structured as a SPA, and thus can run offline (albeit with some restrictions). An event queue was added to allow for offline operations to be uploaded to the server later, which was a required use-case due to poor cell service in the middle of the lake. Below is a video demo of various features and interactions!\u003C/p>\n\u003CFigure>\n \u003CVideo src={vidPages} />\n\u003C/Figure>\n\u003Ch1>Server-Side\u003C/h1>\n\u003Cp>The server is probably the least interesting to talk about, but still quite important! It’s written for Node.JS and uses a mongoDB database to store users, readings, sensor information, and various other data! It provides a REST API and, in addition to serving the static pages (the same ones available offline), it uses mustache templates to provide pages for content only accessible online. I’ll talk more about this system later and my thoughts on its use.\u003C/p>\n\u003Cp>Passwords are stored as bcrypt hashes for essential user security. Users authenticate using \u003Ca\n href=\"https://hacks.mozilla.org/2012/12/using-secure-client-side-sessions-to-build-simple-and-scalable-node-js-applications-a-node-js-holiday-season-part-3/\"\n rel=\"nofollow\"\n>client sessions\u003C/a> which can be invalidated (logged out) server-side in case of certain user actions (eg. password resets). To ensure the accountability of data while providing basic editing features, edits to sensor information are tracked using mongoDB middleware and made visible to users. These are just some examples of security considerations made during development.\u003C/p>\n\u003Ch1>What I Would Do Differently\u003C/h1>\n\u003Cp>First of all, revisiting this codebase after working in Python 3 primarily for a few years and getting used to type annotations and IntelliSense… ouch. Very frequently I have to trace the flow of control manually with \u003Ccode>console.log\u003C/code>s and a lot of reading. I would definitely switch to TypeScript nowadays as things would be much more navigable. However, there’s a lot of deeper issues than that. Some of these arose due to the simple passage of time: some libraries and tools get adopted and maintained, some don’t.\u003C/p>\n\u003Cp>Firstly, lets address the use of Grunt. At the time of using it, it was relatively well-supported and I got used to it fairly quickly. The ability to mix JS scripting into your build system has a lot of benefits, though maybe a bit encouraging of hacky-ness. Nowadays, Grunt is pretty much dead. No releases in 2 years, and its mainly been surpassed by webpack. I’ve taken the hint and also begun migrating some of my older projects to webpack.\u003C/p>\n\u003Cp>Some issues, however, arose from my own inexperience. The biggest pill to swallow from this project is: it’s time to move on from jQuery and onto a proper framework. Not to push this thing any harder than it already is, but it is infinitely less prone to breaking, with less time spent programming destructor functions and chasing bugs. jQuery’s purpose still feels mostly limited to small sites that need some dumb small feature quick and you don’t wanna deal with the poor syntax of vanilla JS, which between TS IntelliSense and GitHub Copilot isn’t really even a problem anymore.\u003C/p>\n\u003Cp>Which leads to the root of that issue: lcatDB did not originally plan to be a SPA. That was added after many of the pages were implemented and the code was written, so the SPA capabilities were hacked on top. jQuery does not suit itself well in this use case, but at the time I couldn’t justify the time as a single developer to go back and start from scratch. And with the scale of the project growing further in a small scope-creep, the problems from jQuery scaled up as well. Despite the amount of time I spent considering my options for each choice of a library or platform, the change in scope caused a pile-up of other issues. This was the single-biggest takeaway: nail down the entire set of specifications first, then implement. Be very specific and thorough. I attempted to do this to some degree at the time, but did not yet know the right questions to ask.\u003C/p>\n\n",{"layout":7,"title":8,"date":9,"categories":10,"last_modified_at":11},"post","Retrospective: lcatDB",["Date","2023-05-18T00:00:00.000Z"],"programming life software ui",["Date","2023-05-19T00:00:00.000Z"],{"slug":13,"path":14,"code":15,"fm":16},"responsive-design","/articles/responsive-design","\u003Cscript context=\"module\">\n\texport const metadata = {\"layout\":\"post\",\"title\":\"Responsive Design Showcase\",\"date\":\"2023-05-18T00:00:00.000Z\",\"categories\":\"programming ui design\",\"last_modified_at\":\"2023-05-19T00:00:00.000Z\"};\n\tconst { layout, title, date, categories, last_modified_at } = metadata;\n\u003C/script>\n\u003Cscript lang=\"ts\">\n import Image from \"$lib/Image.svelte\";\n import Video from \"$lib/Video.svelte\";\n import Figure from \"$lib/Figure.svelte\";\n\n import vidWhiteface from \"./Whiteface Responsive Demo.mp4\";\n import vidLcatDB from \"./lcatDB Responsive Demo.mp4\";\n import vidHeyjoeway from \"./heyjoeway Responsive Demo.mp4\";\n\u003C/script>\n\n\u003Cp>Here’s a mainly visual showcase of some of my work in responsive design!\u003C/p>\n\u003CFigure>\n \u003CVideo src={vidWhiteface} />\n \u003Cfigcaption slot=\"caption\">The Whiteface Tour is a 360-degree virtual tour of the Whiteface Mountain summit I developed using three.js. \u003Ca href=\"https://whitefacetour.app\">Try it for yourself here!\u003C/a>\u003C/figcaption>\n\u003C/Figure>\n\u003CFigure>\n \u003CVideo src={vidLcatDB} />\n \u003Cfigcaption slot=\"caption\">lcatDB was a full-stack single-page application which I developed. \u003Ca href=\"/articles/lcatdb\">You can read more about it here.\u003C/a>\u003C/figcaption>\n\u003C/Figure>\n\u003CFigure>\n \u003CVideo src={vidHeyjoeway} />\n \u003Cfigcaption slot=\"caption\">And last but not least, the site you're reading this on! Try it for yourself!\u003C/figcaption>\n\u003C/Figure>\n\n",{"layout":7,"title":17,"date":18,"categories":19,"last_modified_at":20},"Responsive Design Showcase",["Date","2023-05-18T00:00:00.000Z"],"programming ui design",["Date","2023-05-19T00:00:00.000Z"],{"slug":22,"path":23,"code":24,"fm":25},"software-dump-multiplat","/articles/software-dump-multiplat","\u003Cscript context=\"module\">\n\texport const metadata = {\"layout\":\"post\",\"title\":\"The Software Dump Part 2: Multiplatform\",\"date\":\"2023-04-11T00:00:00.000Z\",\"categories\":\"software life multiplatform\",\"last_modified_at\":\"2023-04-11T00:00:00.000Z\",\"comments\":true};\n\tconst { layout, title, date, categories, last_modified_at, comments } = metadata;\n\u003C/script>\n\u003Cscript lang=\"ts\">\n import Link from \"$lib/Link.svelte\";\n\u003C/script>\n\n\n\u003Cp>This is a continuation of my last post where I discussed programs exclusively for macOS. The software here should be available on all platforms (Win/Mac/Linux). If it’s software that’s already been covered ad nauseam everywhere else or something I don’t use regularly, it’s probably not getting listed here.\u003C/p>\n\u003Ch1>Audio\u003C/h1>\n\u003Ch2>Plexamp\u003C/h2>\n\u003Cul>\n\u003Cli>\u003Cstrong>Price:\u003C/strong> \u003Ca href=\"https://www.plex.tv/plex-pass\" rel=\"nofollow\">It’s complicated\u003C/a>\u003C/li>\n\u003Cli>\u003Cstrong>OSS:\u003C/strong> No\u003C/li>\n\u003Cli>\u003Cstrong>Download:\u003C/strong> \u003CLink href=\"https://plexamp.com/\" />\u003C/li>\n\u003C/ul>\n\u003Cp>My favorite music app. Great offline support (either through manual downloads or automatic caching), a good amount of customization, and the now forgotten ability to curate a music collection outside of what some streaming service dictates you should have.\u003C/p>\n\u003Ch1>Content Creation\u003C/h1>\n\u003Ch2>Blender\u003C/h2>\n\u003Cul>\n\u003Cli>\u003Cstrong>Price:\u003C/strong> Free\u003C/li>\n\u003Cli>\u003Cstrong>OSS:\u003C/strong> Yes (\u003CLink href=\"https://projects.blender.org/blender/blender.git\" />)\u003C/li>\n\u003Cli>\u003Cstrong>Download:\u003C/strong> \u003CLink href=\"https://www.blender.org/\" />\u003C/li>\n\u003C/ul>\n\u003Cp>We are absolutely blessed that a tool this powerful is completely free and open source. I’m no expert at modeling, but it absolutely excels when I need to do stuff in 3D.\u003C/p>\n\u003Ch2>GIMP\u003C/h2>\n\u003Cul>\n\u003Cli>\u003Cstrong>Price:\u003C/strong> Free\u003C/li>\n\u003Cli>\u003Cstrong>OSS:\u003C/strong> Yes (\u003CLink href=\"https://gitlab.gnome.org/GNOME/gimp.git\" />)\u003C/li>\n\u003Cli>\u003Cstrong>Download:\u003C/strong> \u003CLink href=\"https://www.gimp.org/\" />\u003C/li>\n\u003C/ul>\n\u003Cp>Tried and true, and (mostly) gets the job done. Pace of development seems to have slowed and I will be migrating away from it as time goes on.\u003C/p>\n\u003Ch2>Krita\u003C/h2>\n\u003Cul>\n\u003Cli>\u003Cstrong>Price:\u003C/strong> Free\u003C/li>\n\u003Cli>\u003Cstrong>OSS:\u003C/strong> Yes (\u003CLink href=\"https://gitlab.gnome.org/GNOME/gimp.git\" />)\u003C/li>\n\u003Cli>\u003Cstrong>Download:\u003C/strong> \u003CLink href=\"https://krita.org\" />\u003C/li>\n\u003C/ul>\n\u003Cp>My prospective replacement for GIMP. Feels a bit more modern and is seemingly being developed at a faster pace.\u003C/p>\n\u003Ch2>OBS Studio\u003C/h2>\n\u003Cul>\n\u003Cli>\u003Cstrong>Price:\u003C/strong> Free\u003C/li>\n\u003Cli>\u003Cstrong>OSS:\u003C/strong> Yes (\u003CLink href=\"https://github.com/obsproject/obs-studio\" />)\u003C/li>\n\u003Cli>\u003Cstrong>Download:\u003C/strong> \u003CLink href=\"https://obsproject.com/\" />\u003C/li>\n\u003C/ul>\n\u003Cp>Streaming, recording, and live video composition all in one. Really really great at what it does.\u003C/p>\n\u003Ch2>imitone\u003C/h2>\n\u003Cul>\n\u003Cli>\u003Cstrong>Price:\u003C/strong> $30\u003C/li>\n\u003Cli>\u003Cstrong>OSS:\u003C/strong> No\u003C/li>\n\u003Cli>\u003Cstrong>Download:\u003C/strong> \u003CLink href=\"https://imitone.com/\" />\u003C/li>\n\u003C/ul>\n\u003Cp>Basically a voice-controlled theremin with MIDI support. Really great as a teaching tool if you’re not familiar with note scales. Worth the asking price if you want an easy and fun way to play with music.\u003C/p>\n\u003Ch1>Internet\u003C/h1>\n\u003Ch2>Waterfox\u003C/h2>\n\u003Cul>\n\u003Cli>\u003Cstrong>Price:\u003C/strong> Free\u003C/li>\n\u003Cli>\u003Cstrong>OSS:\u003C/strong> Yes (\u003CLink href=\"https://github.com/WaterfoxCo/Waterfox\" />)\u003C/li>\n\u003Cli>\u003Cstrong>Download:\u003C/strong> \u003CLink href=\"https://www.waterfox.net/\" />\u003C/li>\n\u003C/ul>\n\u003Cp>A well-supported Firefox fork with sane defaults and some extra features, such as Chrome extension support. Very clean UI, especially coming from Chrome. Still has Firefox Sync, for those who need iOS password sync.\u003C/p>\n\u003Ch2>Insomnia\u003C/h2>\n\u003Cul>\n\u003Cli>\u003Cstrong>Price:\u003C/strong> Free\u003C/li>\n\u003Cli>\u003Cstrong>OSS:\u003C/strong> Yes (\u003CLink href=\"https://github.com/Kong/insomnia\" />)\u003C/li>\n\u003Cli>\u003Cstrong>Download:\u003C/strong> \u003CLink href=\"https://insomnia.rest/\" />\u003C/li>\n\u003C/ul>\n\u003Cp>Swiss-army knife for debugging APIs, including REST and WebSockets. Makes it much easier to organize tests and try things quickly, as opposed to having a bunch of random JS/CURL snippets laying around.\u003C/p>\n\u003Ch2>Angry IP Scanner\u003C/h2>\n\u003Cul>\n\u003Cli>\u003Cstrong>Price:\u003C/strong> Free\u003C/li>\n\u003Cli>\u003Cstrong>OSS:\u003C/strong> Yes (\u003CLink href=\"https://github.com/angryip/ipscan\" />)\u003C/li>\n\u003Cli>\u003Cstrong>Download:\u003C/strong> \u003CLink href=\"https://angryip.org/\" />\u003C/li>\n\u003C/ul>\n\u003Cp>Scans for active IP addresses and ports on a given network, angrily.\u003C/p>\n\u003Ch1>System\u003C/h1>\n\u003Ch2>balenaEtcher\u003C/h2>\n\u003Cul>\n\u003Cli>\u003Cstrong>Price:\u003C/strong> Free, with pro version upgrade\u003C/li>\n\u003Cli>\u003Cstrong>OSS:\u003C/strong> Yes (\u003CLink href=\"https://github.com/balena-io/etcher\" />)\u003C/li>\n\u003Cli>\u003Cstrong>Download:\u003C/strong> \u003CLink href=\"https://www.balena.io/etcher/\" />\u003C/li>\n\u003C/ul>\n\u003Cp>Before you raise your pitchforks: yeah yeah, electron bad, yadda yadda. Is it more bloated than what it should be? Probably. At the same time, I’ve never had it flash something incorrectly, and it supports on-the-fly decompression. Werks for me, so I’m recommending it. If you just wanna roll with dd though, that’s fine too.\u003C/p>\n\u003Ch2>Parsec\u003C/h2>\n\u003Cul>\n\u003Cli>\u003Cstrong>Price:\u003C/strong> Free, with pro version subscription\u003C/li>\n\u003Cli>\u003Cstrong>OSS:\u003C/strong> No\u003C/li>\n\u003Cli>\u003Cstrong>Download:\u003C/strong> \u003CLink href=\"https://parsec.app/\" />\u003C/li>\n\u003C/ul>\n\u003Cp>My personal favorite remote desktop software. Their tagline “Forget you’re somewhere else” comes pretty close to reality on a good network connection. Pairs well with GPU passthrough.\u003C/p>\n\u003Cp>Streaming, recording, and live video composition all in one. Really really great at what it does.\u003C/p>\n\u003Ch1>Other\u003C/h1>\n\u003Ch2>Zotero\u003C/h2>\n\u003Cul>\n\u003Cli>\u003Cstrong>Price:\u003C/strong> Free\u003C/li>\n\u003Cli>\u003Cstrong>OSS:\u003C/strong> Yes, with centrally hosted services for accounts \u003CLink href=\"https://github.com/zotero\" />\u003C/li>\n\u003Cli>\u003Cstrong>Download:\u003C/strong> \u003CLink href=\"https://www.zotero.org/\" />\u003C/li>\n\u003C/ul>\n\u003Cp>Need to cite something? Just use this. Makes the generation of citations and bibliographies super easy. Comes with integrations for browser and Word to get stuff from the web into your document quickly.\u003C/p>\n",{"layout":7,"title":26,"date":27,"categories":28,"last_modified_at":29,"comments":30},"The Software Dump Part 2: Multiplatform",["Date","2023-04-11T00:00:00.000Z"],"software life multiplatform",["Date","2023-04-11T00:00:00.000Z"],true,{"slug":32,"path":33,"code":34,"fm":35},"portfolio","/articles/portfolio","\u003Cscript context=\"module\">\n\texport const metadata = {\"layout\":\"post\",\"title\":\"The Portfolio Post\",\"date\":\"2023-04-10T00:00:00.000Z\",\"last_modified_at\":\"2023-04-12T00:00:00.000Z\",\"categories\":\"life work portfolio\",\"pinned\":true,\"bgText\":\"portfolio\"};\n\tconst { layout, title, date, last_modified_at, categories, pinned, bgText } = metadata;\n\u003C/script>\n\u003Cscript lang=\"ts\">\n import Image from \"$lib/Image.svelte\";\n import Figure from \"$lib/Figure.svelte\";\n\n import imgVRC from \"./IMG_AA04362AF84B-1.jpeg?as=run\";\n import imgWhiteface from \"./SCR-20230412-r9e.png?as=run\";\n import imgLCATDB from \"./localhost_3000_login.html.png?as=run\";\n\u003C/script>\n\n\u003Cp>Welcome to the site! If you’re on this post, you’re probably wondering who I am and what I do! I’ve recently graduated from Clarkson University with a Master’s in Computer Science. However, I’d like to use this post to show what I am outside of just this degree!\u003C/p>\n\u003Chr>\n\u003Ch1>Professional Projects\u003C/h1>\n\u003CFigure align=\"right\">\n \u003CImage src={imgVRC} />\n\u003C/Figure>\n\u003Ch2>Clarkson University Virtual Reality Coaster\u003C/h2>\n\u003Cp>In February of 2021, I began a refresh of Clarkson University’s MaxFlight VR2004 motion simulator, dubbed the Virtual Reality Coaster (VRC)! This machine was purchased from a local arcade sometime in the mid-2000s, and it shows. The software is quite dated, and so I’ve produced several resources to bring it up towards modern standards:\u003C/p>\n\u003Cul>\n\u003Cli>\n\u003Cp>\u003Cstrong>pyMaxFlight:\u003C/strong> Exposes all capabilities of the MaxFlight Motion Client programmatically through Python on a the Control PC. This module only allows for local operation. MaxFlight did not provide their own public API for interfacing with the simulator, so this module works by directly accessing the Motion Client window using the Win32 API, simulating button presses and slider movements. (\u003Ca\n href=\"https://github.com/Clarkson-IMPETUS/pyMaxFlight\"\n rel=\"nofollow\"\n>GitHub\u003C/a>, \u003Ca\n href=\"https://pymaxflight.readthedocs.io/en/latest/src/pyMaxFlight/\"\n rel=\"nofollow\"\n>Docs\u003C/a>)\u003C/p>\n\u003C/li>\n\u003Cli>\n\u003Cp>\u003Cstrong>pyWSConsole:\u003C/strong> Provides network communication via WebSockets, with a focus on stability and persistent connections. Uses a console-like interface and provides automatic help documentation. (\u003Ca\n href=\"https://github.com/heyjoeway/pyWSConsole\"\n rel=\"nofollow\"\n>GitHub\u003C/a>)\u003C/p>\n\u003C/li>\n\u003Cli>\n\u003Cp>\u003Cstrong>VRC-Apps:\u003C/strong> A collection of Python applications to extend the functionality of the VRC using the aforementioned libraries. (\u003Ca\n href=\"https://github.com/Clarkson-IMPETUS/VRC-Apps\"\n rel=\"nofollow\"\n>GitHub\u003C/a>)\u003C/p>\n\u003C/li>\n\u003C/ul>\n\u003CFigure align=\"right\">\n \u003CImage src={imgWhiteface} maxHeight=\"270px\" />\n\u003C/Figure>\n\u003Ch2>Whiteface Tour\u003C/h2>\n\u003Cp>\u003Cstrong>Link:\u003C/strong> \u003Ca href=\"https://whitefacetour.app\" rel=\"nofollow\">https://whitefacetour.app\u003C/a>\u003C/p>\n\u003Cp>In the summer of 2018, I had the opportunity to work as an intern at the Whiteface Mountain Atmospheric Sciences Research Center (ASRC). During my time there, I worked on several projects related to outreach. The most substantial of these is the Whiteface Tour: a 360-degree virtual tour of the mountain summit. This uses the library three.js for rendering, and a custom JSON-based system for scene management. This does not use a pre-existing game engine. All resources are packaged using webpack, deployed through GitHub Actions, and hosted using GitHub Pages.\u003C/p>\n\u003Cbr>\n\u003CFigure align=\"right\">\n \u003CImage src={imgLCATDB} maxHeight=\"330px\" />\n\u003C/Figure>\n\u003Ch2>lcatDB (Lake Champlain Anglers’ Temperature Database)\u003C/h2>\n\u003Cp>lcatDB (Lake Champlain Anglers’ Temperature Database) was an online database and single page application (SPA) meant to provide a centralized means of recording, accessing, and analyzing vertical water temperature profiles to citizens of the Lake Champlain region. It was supported by the SUNY Plattsburgh Center for Earth & Environmental Science and the Lake Champlain Sea Grant. lcatDB was (what I consider to be) my first attempt at full stack software development. It primarily uses Bootstrap, jQuery, and Grunt for the frontend, while the backend was built using Node.JS, Express, and MongoDB. \u003Ca\n href=\"/articles/lcatdb\"\n>Read the full retrospective here.\u003C/a>\u003C/p>\n\u003Ch1>Personal Projects\u003C/h1>\n\u003Ch2>Switch Ports\u003C/h2>\n\u003Cp>\u003Cstrong>Page:\u003C/strong> \u003Ca href=\"/articles/switch-ports\">Switch Ports\u003C/a>\u003C/p>\n\u003Cp>The Nintendo Switch has a thriving homebrew scene that I took part in between 2019 and 2022. I developed ports of several games and maintained them through the 3 years that I was active. These ports were generally maintained as forks so as to not pollute the upstream repositories with platform specific material. This required working with upstream maintainers to merge in changes to these fork and produce installable packages, which was eventually automated using GitHub Actions.\u003C/p>\n\u003Ch2>ICBINS1\u003C/h2>\n\u003Cp>\u003Cstrong>Link:\u003C/strong> \u003Ca\n href=\"http://jojudge.com/ICBINS1/\"\n rel=\"nofollow\"\n>http://jojudge.com/ICBINS1/\u003C/a>\u003C/p>\n\u003Cp>As a test to learn the ins-and-outs of Unity, I tried recreating the original Sonic the Hedgehog from observation! This was a great learning experience and taught me a lot about the game engine and C#. I later moved away from this project due to it being superseded in its goal by the decompilation of the 2013 remake, the exploration of other approaches, and shifting priorities in my personal life.\u003C/p>\n\u003Ch2>S2CX/Genesis Plus GX Wide\u003C/h2>\n\u003Cp>\u003Cstrong>News Article:\u003C/strong> \u003Ca\n href=\"https://www.libretro.com/index.php/genesis-plus-gx-wide-now-available-for-libretroretroarch/\"\n rel=\"nofollow\"\n>https://www.libretro.com/index.php/genesis-plus-gx-wide-now-available-for-libretroretroarch/\u003C/a>\u003C/p>\n\u003Cp>Another project launched before the 2013 remake decompilations that took the Genesis version of Sonic 2 and added widescreen capabilities. When first developed, my modification of Genesis Plus GX was only meant to add widescreen to that one game. After people began experimenting with other games, it was found that this approach worked for many other games, and so the project now lives on as an upstream RetroArch core (you can find it in the core downloader)! I no longer maintain this fork, but it has been picked up by other passionate community members!\u003C/p>\n\u003Ch1>Other Stuff\u003C/h1>\n\u003Cp>Here’s a miscellaneous list of other things I written/done you should check out!\u003C/p>\n\u003Cul>\n\u003Cli>GitHub: \u003Ca href=\"https://github.com/heyjoeway\" rel=\"nofollow\">heyjoeway\u003C/a>\u003C/li>\n\u003Cli>\u003Ca href=\"/articles/responsive-design\">Responsive Design Showcase\u003C/a>\u003C/li>\n\u003C/ul>\n\u003Ch1>Other Hobbies\u003C/h1>\n\u003Cp>Believe it or not, I do get sick of programming! When I do, I like dipping my toes into other domains to pick up new skills! Some of my “side gigs” include:\u003C/p>\n\u003Ch2>Photography\u003C/h2>\n\u003Cp>Check out my \u003Ca href=\"/articles/photo-megapost\">photography megapost\u003C/a>!\u003C/p>\n\u003Ch2>Music\u003C/h2>\n\u003Cp>Since 2022 I’ve been dabbling in mashups using Logic Pro X and AI tools such as demucs! You can find tracks on my \u003Ca\n href=\"https://soundcloud.com/heyjoeway\"\n rel=\"nofollow\"\n>SoundCloud\u003C/a>. Be warned that my taste is… inexplicable? I often pull tracks from my mid-2000s childhood for material. Some of main inspirations include Neil Cicierega, Triple-Q, Galactic Hole, and SiivaGunner. I’m hoping to produce some original pieces in the future!\u003C/p>\n\u003Ch2>Graphic Design\u003C/h2>\n\u003Cp>You can see my graphic design skills reflected in many of my projects, as I found it to be necessary to pair with programming for many projects. I tend to follow more minimalist standards such as Material Design and Metro while retaining some personalized flair.\u003C/p>\n\u003Ch2>3D Modeling\u003C/h2>\n\u003Cp>I’m familiar with the basics of modeling in Blender and SketchUp and can produce simple models/renders for both graphic design and CAD. (Some examples: \u003Ca\n href=\"/articles/gd-anime-was-a-mistake\"\n>“Anime Was a Mistake”\u003C/a>, \u003Ca href=\"/articles/gd-mayolo\">“Mayolo”\u003C/a>) I have 2 3D printers (one SLA, one FDM) and experiment with them from time to time to produce both figurines and more utilitarian parts.s\u003C/p>\n\u003Chr>\n\u003Ch1>Philosophy\u003C/h1>\n\u003Cp>I’ve spent a lot of time programming in a lot of different languages, working with many different technologies, and aiming for several different goals. It’s easy to get lost in programming as an art in-and-of-itself, but as a goal-oriented individual, I see programming as a tool I use to accomplish said goals. That being said, I personally recognize it as one of the most important tools in modern society, and thus I have spent a lot of time refining it as a craft.\u003C/p>\n\u003Cp>I always consider the long-term viability of projects and weigh all possible approaches. There are many great ideas for products, but without a efficient plan, many of them lie on the table or the cutting-room floor. I often look to automation to avoid these scenarios, even for tasks in my personal life. I always consider all available options and tools: sometimes the answer is to deploy pre-existing services or generally change my workflow. Very often, however, an idea will come along that is too specific to execute with anything other than my own code.\u003C/p>\n\u003Cblockquote>\n\u003Cp>\u003Cstrong>Side note:\u003C/strong> As AI becomes more adept, I am constantly searching for ways to use it in optimizing my workflow. However, I am more interested in using AI tools than developing them, as I do not currently have a great interest in AI training and data science. I am experimenting with tools such as OpenAI Playground and GitHub Copilot and, while not perfect, I am very impressed with their results and have begun using them to increase my own efficiency.\u003C/p>\n\u003C/blockquote>\n\u003Cp>When writing my code, I make sure that it is readable, modular, and generally reusable. Sometimes this may be as simple as providing reasoning through comments. Other times it may be more complex, such as producing documentation (generally using a pre-existing documentation system like mkdocs), segmenting code into more readable chunks, or even developing libraries to use in multiple projects. I prioritize compatibility with IntelliSense, which makes any codebase significantly more navigable. This is generally accomplished by following documentation standards, such as type annotations (ala Python 3) or JSDoc.\u003C/p>\n\u003Cp>My goal is to write software and produce products that will stand the test of time, that I’ll be able to look on years down the line and be proud of.\u003C/p>\n\n",{"layout":7,"title":36,"date":37,"last_modified_at":38,"categories":39,"pinned":30,"bgText":32},"The Portfolio Post",["Date","2023-04-10T00:00:00.000Z"],["Date","2023-04-12T00:00:00.000Z"],"life work portfolio",{"slug":41,"path":42,"code":43,"fm":44},"mac-hidden-files","/articles/mac-hidden-files","\u003Cscript context=\"module\">\n\texport const metadata = {\"layout\":\"post\",\"title\":\"Toggling Hidden Files in macOS except it's actually good\",\"date\":\"2023-02-14T00:00:00.000Z\",\"categories\":\"software macos\",\"comments\":true};\n\tconst { layout, title, date, categories, comments } = metadata;\n\u003C/script>\n\u003Cscript lang=\"ts\">\n import Image from \"$lib/Image.svelte\";\n import Figure from \"$lib/Figure.svelte\";\n\n import imgScreenshot1 from \"./SCR-20230214-ext.png?as=run:0\";\n import imgScreenshot2 from \"./SCR-20230214-f3i.png?as=run:0\";\n\u003C/script>\n\n\u003Cp>Happy Valentines Day. I’ve been trying to migrate from Forklift back to Finder since I’m not super happy with their progress and the issues that come with a non-native file manager, including but not limited to:\u003C/p>\n\u003Cul>\n\u003Cli>Getting nagged every boot\u003C/li>\n\u003Cli>Favorites not syncing when using a file picker\u003C/li>\n\u003Cli>Mediocre UI\u003C/li>\n\u003Cli>Remote integration not carrying over to other apps\u003C/li>\n\u003Cli>Needing to have two file managers in my dock at all times\u003C/li>\n\u003Cli>Window resizing being poor\u003C/li>\n\u003Cli>No support for per-folder view options (I think?)\u003C/li>\n\u003C/ul>\n\u003Cp>One thing I did miss was having a toolbar button for showing hidden files.\u003C/p>\n\u003Ch1>The Keyboard Shortcut\u003C/h1>\n\u003Cp>\u003Ccode>CMD + SHIFT + .\u003C/code>\u003C/p>\n\u003Cp>I’m going to be honest with you, I use this so infrequently that I don’t think there’s a chance in hell I’ll go 6 months without Googling this a couple times. And it’s different on Every. Single. OS. \u003Ccode>Alt + .\u003C/code>, \u003Ccode>Ctrl + H\u003C/code>, hell, why not \u003Ccode>Ctrl + Shift + Alt + Super + F12 + 2\u003C/code> next?\u003C/p>\n\u003Ch1>The “Automatic” Method\u003C/h1>\n\u003Cp>Does having all your Finder windows killed every time you want to toggle hidden files sound fun? No? Well apparently Stack Overflow thinks so because it’s the only other suggestion I’ve seen. I’m not even going to bother reposting it here because I think it’s that fucking awful. That is not a solution.\u003C/p>\n\u003Ch1>Why Did They Name The App “Shortcuts”\u003C/h1>\n\u003Cp>Oh yeah, Apple added that “Shortcuts” app huh? Too bad nobody really seems to care or talk about it, because it’s actually pretty decent. It’s also really hard to find anything relating to it online because the name is absolutely horrible.\u003C/p>\n\u003Cblockquote>\n\u003Cp>Did you mean?: Keyboard shortcuts\u003C/p>\n\u003C/blockquote>\n\u003Cp>Unfortunately, it ALSO has no ability to toggle hidden files. Or does it? We DO have the ability to run AppleScript, and combining a little bit of every approach, we get the following:\u003C/p>\n\u003Cpre class=\"language-applescript\">{@html `\u003Ccode class=\"language-applescript\">\u003Cspan class=\"token keyword\">on\u003C/span> run \u003Cspan class=\"token punctuation\">{\u003C/span>input\u003Cspan class=\"token punctuation\">,\u003C/span> parameters\u003Cspan class=\"token punctuation\">}\u003C/span>\n\tactivate \u003Cspan class=\"token class-name\">application\u003C/span> \u003Cspan class=\"token string\">\"Finder\"\u003C/span>\n\t\u003Cspan class=\"token keyword\">tell\u003C/span> \u003Cspan class=\"token class-name\">application\u003C/span> \u003Cspan class=\"token string\">\"System Events\"\u003C/span> \u003Cspan class=\"token keyword\">to\u003C/span> keystroke \u003Cspan class=\"token string\">\".\"\u003C/span> using \u003Cspan class=\"token punctuation\">{\u003C/span>command down\u003Cspan class=\"token punctuation\">,\u003C/span> shift down\u003Cspan class=\"token punctuation\">}\u003C/span>\n\t\u003Cspan class=\"token keyword\">return\u003C/span> input\n\u003Cspan class=\"token keyword\">end\u003C/span> run\u003C/code>`}\u003C/pre>\n\u003CFigure>\n \u003CImage src={imgScreenshot1} />\n\u003C/Figure>\n\u003Cp>You’ll need to give both Shortcuts and \u003Ccode>siriactionsd\u003C/code> Accessibility permissions. I think there might also be something you need to enable to use AppleScript. \u003Ca\n href=\"https://www.icloud.com/shortcuts/2d73e23025fd4a0e8f78c55600e92a04\"\n rel=\"nofollow\"\n>Here’s a pre-made shortcut if you’d like.\u003C/a>\u003C/p>\n\u003Ch1>“But you said you wanted a toolbar button!”\u003C/h1>\n\u003Cp>Here’s a secret: you can turn any shortcut into a \u003Ccode>.app\u003C/code> by right clicking on it and selecting “Add to Dock”. You’ll likely want to remove it from the dock after, and you’ll be able to find it in \u003Ccode>~/Applications\u003C/code>. I moved mine into a subfolder I named “Finder Toolbar”.\u003C/p>\n\u003Cp>Here’s ANOTHER secret: you can add any \u003Ccode>.app\u003C/code> to your Finder toolbar. Simply enter customization mode and drag the app in.\u003C/p>\n\u003CFigure>\n \u003CImage src={imgScreenshot2} />\n\u003C/Figure>\n\u003Cp>Apple sure does love to include useful features and never explain them, huh?\u003C/p>\n\n",{"layout":7,"title":45,"date":46,"categories":47,"comments":30},"Toggling Hidden Files in macOS except it's actually good",["Date","2023-02-14T00:00:00.000Z"],"software macos",{"slug":49,"path":50,"code":51,"fm":52},"software-dump-mac","/articles/software-dump-mac","\u003Cscript context=\"module\">\n\texport const metadata = {\"layout\":\"post\",\"title\":\"The Software Dump Part 1: macOS\",\"date\":\"2022-12-28T00:00:00.000Z\",\"categories\":\"software life macos\",\"last_modified_at\":\"2023-04-13T00:00:00.000Z\",\"comments\":true};\n\tconst { layout, title, date, categories, last_modified_at, comments } = metadata;\n\u003C/script>\n\u003Cscript lang=\"ts\">\n import Link from \"$lib/Link.svelte\";\n\u003C/script>\n\n\n\u003Cp>You know those shitty tabloid articles you find on Google when you try to find software recommendations, all of them having the same 10 programs over and over? Yeah, this isn’t another one of those. These are all programs I’ve spent a good deal of time with and will be giving genuine opinions on. Let’s get to it.\u003C/p>\n\u003Ch1>Window Management\u003C/h1>\n\u003Cp>For all the polish macOS has, its window management has been complete and utter \u003Cem>shit\u003C/em> for its entire life. These make it a little bit better.\u003C/p>\n\u003Ch2>Rectangle\u003C/h2>\n\u003Cul>\n\u003Cli>\u003Cstrong>Price:\u003C/strong> Free, with pro version upgrade\u003C/li>\n\u003Cli>\u003Cstrong>OSS:\u003C/strong> Yes, except pro (\u003CLink href=\"https://github.com/rxhanson/Rectangle\" />)\u003C/li>\n\u003Cli>\u003Cstrong>Download:\u003C/strong> \u003CLink href=\"https://rectangleapp.com/\" />\u003C/li>\n\u003C/ul>\n\u003Cp>Aero Snap for macOS, plus some extra snapping areas. That’s about it to me. It has some other features that I’ve never really messed with or cared about. Just install it already.\u003C/p>\n\u003Ch2>Swish\u003C/h2>\n\u003Cul>\n\u003Cli>\u003Cstrong>Price:\u003C/strong> $16, trial available\u003C/li>\n\u003Cli>\u003Cstrong>OSS:\u003C/strong> No\u003C/li>\n\u003Cli>\u003Cstrong>Download:\u003C/strong> \u003CLink href=\"https://highlyopinionated.co/swish/\" />\u003C/li>\n\u003C/ul>\n\u003Cp>Lets you move/tile windows using trackpad gestures. Honestly so vital to window management when not docked that I wish Apple would buy them up and include it by default. Absolutely worth the asking price.\u003C/p>\n\u003Ch1>Filesystem\u003C/h1>\n\u003Ch2>Disk Inventory X\u003C/h2>\n\u003Cul>\n\u003Cli>\u003Cstrong>Price:\u003C/strong> Free\u003C/li>\n\u003Cli>\u003Cstrong>OSS:\u003C/strong> Yes (\u003CLink href=\"https://gitlab.com/tderlien/disk-inventory-x\" />)\u003C/li>\n\u003Cli>\u003Cstrong>Download:\u003C/strong> \u003CLink href=\"https://www.derlien.com/\" />\u003C/li>\n\u003C/ul>\n\u003Cp>Ever used WinDirStat? It’s that. Basically, let’s you locate what’s taking up disk space more easily.\u003C/p>\n\u003Ch2>ForkLift\u003C/h2>\n\u003Cul>\n\u003Cli>\u003Cstrong>Price:\u003C/strong> Free, with pro version upgrade\u003C/li>\n\u003Cli>\u003Cstrong>OSS:\u003C/strong> No\u003C/li>\n\u003Cli>\u003Cstrong>Download:\u003C/strong> \u003CLink href=\"https://binarynights.com/\" />\u003C/li>\n\u003C/ul>\n\u003Cp>Better file manager. Dual panes, remote connections (including SFTP, FTP, GDrive, etc.), better view modes, “auto clean-up” (aka just listing files like a normal file manager does, yes I am still mad at Finder for this) and some other smaller features. The free version is supposedly a trial but I think it’s just nagware like WinRAR. I have this as my default file manager.\u003C/p>\n\u003Ch2>OpenMTP\u003C/h2>\n\u003Cul>\n\u003Cli>\u003Cstrong>Price:\u003C/strong> Free\u003C/li>\n\u003Cli>\u003Cstrong>OSS:\u003C/strong> Yes (\u003CLink href=\"https://github.com/ganeshrvel/openmtp\" />)\u003C/li>\n\u003Cli>\u003Cstrong>Download:\u003C/strong> \u003CLink href=\"https://openmtp.ganeshrvel.com/\" />\u003C/li>\n\u003C/ul>\n\u003Cp>Lets you access MTP devices (Android) under macOS. I sure do love meaningless segmentation of stuff that doesn’t need to be segmented.\u003C/p>\n\u003Ch1>Internet\u003C/h1>\n\u003Ch2>Canary Mail\u003C/h2>\n\u003Cul>\n\u003Cli>\u003Cstrong>Price:\u003C/strong> Free, with pro version upgrade\u003C/li>\n\u003Cli>\u003Cstrong>OSS:\u003C/strong> No\u003C/li>\n\u003Cli>\u003Cstrong>Download:\u003C/strong> \u003CLink href=\"https://canarymail.io/\" />\u003C/li>\n\u003C/ul>\n\u003Cp>The built-mail app with a few more features and a bit more customization. Honestly, if the default mail app does what you want it to, just stick with it.\u003C/p>\n\u003Ch2>NetNewsWire\u003C/h2>\n\u003Cul>\n\u003Cli>\u003Cstrong>Price:\u003C/strong> Free\u003C/li>\n\u003Cli>\u003Cstrong>OSS:\u003C/strong> Yes (\u003CLink href=\"https://github.com/Ranchero-Software/NetNewsWire\" />)\u003C/li>\n\u003Cli>\u003Cstrong>Download:\u003C/strong> \u003CLink href=\"https://netnewswire.com/\" />\u003C/li>\n\u003C/ul>\n\u003Cp>Native RSS reader.\u003C/p>\n\u003Ch1>Media\u003C/h1>\n\u003Ch2>IINA\u003C/h2>\n\u003Cul>\n\u003Cli>\u003Cstrong>Price:\u003C/strong> Free\u003C/li>\n\u003Cli>\u003Cstrong>OSS:\u003C/strong> Yes (\u003CLink href=\"https://github.com/iina/iina\" />)\u003C/li>\n\u003Cli>\u003Cstrong>Download:\u003C/strong> \u003CLink href=\"https://iina.io/\" />\u003C/li>\n\u003C/ul>\n\u003Cp>A media player that gets out of the way of the content. Much better than QuickTime or even VLC.\u003C/p>\n\u003Ch2>Logic Pro\u003C/h2>\n\u003Cul>\n\u003Cli>\u003Cstrong>Price:\u003C/strong> $200\u003C/li>\n\u003Cli>\u003Cstrong>OSS:\u003C/strong> …no.\u003C/li>\n\u003Cli>\u003Cstrong>Download:\u003C/strong> \u003CLink href=\"https://www.apple.com/logic-pro/\" />\u003C/li>\n\u003C/ul>\n\u003Cp>The Mac DAW. After trying other DAWs (mostly FL Studio) this one just clicked. The UI is great in the realm of audio work and gets shit done. Now I will plug my SoundCloud because I can use it as an example of how I’ve been using it: \u003CLink href=\"https://soundcloud.com/heyjoeway\" />\u003C/p>\n\u003Cp>If you’re not ready to pay the price or sail the seas, give GarageBand a shot. It’s basically the trial version of Logic and has a very similar UI.\u003C/p>\n\u003Ch1>Images\u003C/h1>\n\u003Ch2>Pixea\u003C/h2>\n\u003Cul>\n\u003Cli>\u003Cstrong>Price:\u003C/strong> Free, with pro version upgrade\u003C/li>\n\u003Cli>\u003Cstrong>OSS:\u003C/strong> No\u003C/li>\n\u003Cli>\u003Cstrong>Download:\u003C/strong> \u003CLink href=\"https://www.imagetasks.com/pixea/\" />\u003C/li>\n\u003C/ul>\n\u003Cp>Nice image viewer with an “invisible” header bar. Really nice for quickly previewing images. Preview is still king for PDFs and image conversion though. Pairs very nicely with IINA.\u003C/p>\n\u003Ch2>ColorSlurp\u003C/h2>\n\u003Cul>\n\u003Cli>\u003Cstrong>Price:\u003C/strong> Free, with pro version upgrade\u003C/li>\n\u003Cli>\u003Cstrong>OSS:\u003C/strong> No\u003C/li>\n\u003Cli>\u003Cstrong>Download:\u003C/strong> \u003CLink href=\"https://colorslurp.com/\" />\u003C/li>\n\u003C/ul>\n\u003Cp>Quick universal color picker.\u003C/p>\n\u003Ch2>Shottr\u003C/h2>\n\u003Cul>\n\u003Cli>\u003Cstrong>Price:\u003C/strong> Free\u003C/li>\n\u003Cli>\u003Cstrong>OSS:\u003C/strong> No (why?)\u003C/li>\n\u003Cli>\u003Cstrong>Download:\u003C/strong> \u003CLink href=\"https://shottr.cc/\" />\u003C/li>\n\u003C/ul>\n\u003Cp>Screenshot tool that doesn’t suck ass!\u003C/p>\n\u003Ch1>System\u003C/h1>\n\u003Ch2>Keka\u003C/h2>\n\u003Cul>\n\u003Cli>\u003Cstrong>Price:\u003C/strong> Free\u003C/li>\n\u003Cli>\u003Cstrong>OSS:\u003C/strong> Yes (\u003CLink href=\"https://github.com/aonez/Keka\" />)\u003C/li>\n\u003Cli>\u003Cstrong>Download:\u003C/strong> \u003CLink href=\"https://www.keka.io/ \" />\u003C/li>\n\u003C/ul>\n\u003Cp>A swiss army archival tool.\u003C/p>\n\u003Ch2>iTerm2\u003C/h2>\n\u003Cul>\n\u003Cli>\u003Cstrong>Price:\u003C/strong> Free\u003C/li>\n\u003Cli>\u003Cstrong>OSS:\u003C/strong> Yes (\u003CLink href=\"https://github.com/gnachman/iTerm2\" />)\u003C/li>\n\u003Cli>\u003Cstrong>Download:\u003C/strong> \u003CLink href=\"https://iterm2.com/\" />\u003C/li>\n\u003C/ul>\n\u003Cp>A better terminal. More customization, more features.\u003C/p>\n\u003Ch2>UTM\u003C/h2>\n\u003Cul>\n\u003Cli>\u003Cstrong>Price:\u003C/strong> Free\u003C/li>\n\u003Cli>\u003Cstrong>OSS:\u003C/strong> Yes (\u003CLink href=\"https://github.com/utmapp/UTM\" />)\u003C/li>\n\u003Cli>\u003Cstrong>Download:\u003C/strong> \u003CLink href=\"https://mac.getutm.app/\" />\u003C/li>\n\u003C/ul>\n\u003Cp>Virtualization/emulation of other computers. This is one way to run Windows ARM on macOS. I only need to use this every once in a while and it gets the job done. If you’re looking for something a little more advanced, try Parallels (paid).\u003C/p>\n\u003Ch2>KeyboardCleanTool\u003C/h2>\n\u003Cul>\n\u003Cli>\u003Cstrong>Price:\u003C/strong> Free\u003C/li>\n\u003Cli>\u003Cstrong>OSS:\u003C/strong> No (…why?)\u003C/li>\n\u003Cli>\u003Cstrong>Download:\u003C/strong> \u003CLink href=\"https://folivora.ai/keyboardcleantool\" />\u003C/li>\n\u003C/ul>\n\u003Cp>Temporarily disables the keyboard to let you clean it. It sounds stupid, but you’ll end up using it every once in a while anyway, trust me.\u003C/p>\n\u003Ch2>Karabiner-Elements\u003C/h2>\n\u003Cul>\n\u003Cli>\u003Cstrong>Price:\u003C/strong> Free\u003C/li>\n\u003Cli>\u003Cstrong>OSS:\u003C/strong> Yes (\u003CLink href=\"https://github.com/pqrs-org/Karabiner-Elements\" />)\u003C/li>\n\u003Cli>\u003Cstrong>Download:\u003C/strong> \u003CLink href=\"https://karabiner-elements.pqrs.org/\" />\u003C/li>\n\u003C/ul>\n\u003Cp>General purpose key-rebinding tool. Can rebind a lot of things that the built-in settings can’t. Perfect for muscle memory diehards, especially if coming from another platform. Here’s my config:\u003C/p>\n\u003Cul>\n\u003Cli>Simple Modifications\n\u003Cul>\n\u003Cli>All Devices\n\u003Cul>\n\u003Cli>caps_lock -> mission_control\u003C/li>\n\u003Cli>right_shift -> left_shift\n\u003Cul>\n\u003Cli>\u003Cstrong>NOTE:\u003C/strong> Fixes AnyDesk bug.\u003C/li>\n\u003C/ul>\n\u003C/li>\n\u003C/ul>\n\u003C/li>\n\u003Cli>Internal Keyboard\n\u003Cul>\n\u003Cli>fn -> left_command\u003C/li>\n\u003Cli>left_command -> left_option\u003C/li>\n\u003Cli>left_control -> fn\u003C/li>\n\u003Cli>left_option -> left_control\n\u003Cul>\n\u003Cli>\u003Cstrong>NOTE:\u003C/strong> On a PC keyboard, this layout would look like this: Ctrl, Fn, Win, Alt. I have custom key labels printed to make this less confusing.\u003C/li>\n\u003C/ul>\n\u003C/li>\n\u003C/ul>\n\u003C/li>\n\u003Cli>External Keyboard (PC)\n\u003Cul>\n\u003Cli>left_command -> left_control\u003C/li>\n\u003Cli>left_control -> left_command\u003C/li>\n\u003C/ul>\n\u003C/li>\n\u003C/ul>\n\u003C/li>\n\u003Cli>Function Keys\n\u003Cul>\n\u003Cli>Internal Keyboard\n\u003Cul>\n\u003Cli>f1 -> display_brightness_decrement\u003C/li>\n\u003Cli>f2 -> display_brightness_increment\u003C/li>\n\u003Cli>f3 -> print_screen\u003C/li>\n\u003Cli>f4 -> mute\u003C/li>\n\u003Cli>f5 -> volume_decrement\u003C/li>\n\u003Cli>f6 -> volume_increment\u003C/li>\n\u003Cli>f7 -> rewind\u003C/li>\n\u003Cli>f8 -> play_or_pause\u003C/li>\n\u003Cli>f9 -> fast_forward\u003C/li>\n\u003Cli>f10 -> home\u003C/li>\n\u003Cli>f11 -> end\u003C/li>\n\u003Cli>f12 -> delete_forward\n\u003Cul>\n\u003Cli>\u003Cstrong>NOTE:\u003C/strong> Those last 3 keys are the reason everything else got moved around.\u003C/li>\n\u003C/ul>\n\u003C/li>\n\u003C/ul>\n\u003C/li>\n\u003C/ul>\n\u003C/li>\n\u003Cli>Complex Modifications\n\u003Cul>\n\u003Cli>AnyDesk Left CTRL -> CMD\n\u003Cul>\n\u003Cli>\u003Cstrong>NOTE:\u003C/strong> Many remote desktop tools will try to swap left command and left control on their own to match the PC layout. Since we’re already doing this, we need to swap AGAIN to counter AnyDesk. Seriously, please add an option to disable this. This is stupid.\u003C/li>\n\u003C/ul>\n\u003C/li>\n\u003Cli>Spotlight on CMD\n\u003Cul>\n\u003Cli>\u003Cstrong>NOTE:\u003C/strong> Kinda simulates searching in the Start Menu with the Windows key.\u003C/li>\n\u003C/ul>\n\u003C/li>\n\u003C/ul>\n\u003C/li>\n\u003C/ul>\n\u003Cp>The complex modifications are custom. You can find them in my repo: \u003CLink href=\"https://github.com/heyjoeway/KE-complex_modifications\" />\u003C/p>\n\u003Ch2>LinearMouse\u003C/h2>\n\u003Cul>\n\u003Cli>\u003Cstrong>Price:\u003C/strong> Free\u003C/li>\n\u003Cli>\u003Cstrong>OSS:\u003C/strong> Yes (\u003CLink href=\"https://github.com/linearmouse/linearmouse\" />)\u003C/li>\n\u003Cli>\u003Cstrong>Download:\u003C/strong> \u003CLink href=\"https://linearmouse.app/\" />\u003C/li>\n\u003C/ul>\n\u003Cp>Lets you fine-tune individual pointing devices (yes, you can change the touchpad and mouse independently). I use this for reverse scrolling on mice and speed/acceleration tweaks.\u003C/p>\n\u003Ch2>Hidden Bar\u003C/h2>\n\u003Cul>\n\u003Cli>\u003Cstrong>Price:\u003C/strong> Free\u003C/li>\n\u003Cli>\u003Cstrong>OSS:\u003C/strong> Yes (\u003CLink href=\"https://github.com/dwarvesf/hidden\" />)\u003C/li>\n\u003Cli>\u003Cstrong>Download:\u003C/strong> \u003CLink href=\"https://itunes.apple.com/app/hidden-bar/id1452453066\" />\u003C/li>\n\u003C/ul>\n\u003Cp>Hides system tray icons. Why the HELL is this not built in? Windows has had it since, what, XP?\u003C/p>\n\u003Ch2>BetterDisplay (formerly BetterDummy)\u003C/h2>\n\u003Cul>\n\u003Cli>\u003Cstrong>Price:\u003C/strong> Free, with pro version upgrade\u003C/li>\n\u003Cli>\u003Cstrong>OSS:\u003C/strong> No (previously was, grrrr)\u003C/li>\n\u003Cli>\u003Cstrong>Download:\u003C/strong> \u003CLink href=\"https://github.com/waydabber/BetterDisplay\" />\u003C/li>\n\u003C/ul>\n\u003Cp>Lets you create fake displays and enable HiDPI on unsupported screens. Essential for people with 1440p+ monitors. Again, more functionality that should be there by default and just… isn’t.\u003C/p>\n\u003Ch2>MonitorControl\u003C/h2>\n\u003Cul>\n\u003Cli>\u003Cstrong>Price:\u003C/strong> Free, with pro version upgrade\u003C/li>\n\u003Cli>\u003Cstrong>OSS:\u003C/strong> Yes\u003C/li>\n\u003Cli>\u003Cstrong>Download:\u003C/strong> \u003CLink href=\"https://github.com/MonitorControl/MonitorControl\" />\u003C/li>\n\u003C/ul>\n\u003Cp>Lets you control the brightness of external displays from your Mac. No, I don’t mean applying a dimming filter over the image (though it can do that too on unsupported displays), I mean \u003Cstrong>actually changing the settings of the monitor directly to lower the brightness.\u003C/strong> I’m going to be totally honest, I didn’t even know this was possible until I found this app. \u003Ca\n href=\"https://en.wikipedia.org/wiki/Display_Data_Channel#DDC/CI\"\n rel=\"nofollow\"\n>Apparently it’s been a standard since 1998\u003C/a> and to my knowledge not a single OS has included support for it out of the box. What the fuck? WHY?\u003C/p>\n\u003Ch1>Other Notes\u003C/h1>\n\u003Cp>\u003Cdel>You might notice I don’t have a screenshot utility. I’d recommend Monosnap if it wasn’t buggy as shit, though that may have changed since I last used it. Right now, I’m just using some Shortcuts (as in the Shortcuts app ala Monterey):\u003C/del>\u003C/p>\n\u003Cul>\n\u003Cli>\u003Cdel>\u003Ca\n href=\"https://www.icloud.com/shortcuts/1c3f40fb719f4b6f9ab8564f429778e9\"\n rel=\"nofollow\"\n>Selection Screenshot\u003C/a>\u003C/del>\u003C/li>\n\u003Cli>\u003Cdel>\u003Ca\n href=\"https://www.icloud.com/shortcuts/0958cf10202a48f8929ca0567510037d\"\n rel=\"nofollow\"\n>Full Screen Screenshot\u003C/a>\u003C/del>\u003C/li>\n\u003Cli>\u003Cdel>\u003Ca\n href=\"https://www.icloud.com/shortcuts/8dc40861f6f341d98abf8092d3ceaf39\"\n rel=\"nofollow\"\n>Window Screenshot\u003C/a>\u003C/del>\u003C/li>\n\u003C/ul>\n\u003Cp>\u003Cdel>You may need to edit these to set the save paths properly. I’ve added these to my menu bar and they work okay-ish. I want to find a better solution in the long term.\u003C/del> EDIT 4/13/23: \u003Ca href=\"#shottr\">Just use Shottr.\u003C/a> For screen recording I just open up QuickTime.\u003C/p>\n\u003Cp>I think that’s it for now? I’ll update this post if I find any big stuff in the future or if there’s anything I missed. In the next part, I’ll be covering multiplatform software.\u003C/p>\n",{"layout":7,"title":53,"date":54,"categories":55,"last_modified_at":56,"comments":30},"The Software Dump Part 1: macOS",["Date","2022-12-28T00:00:00.000Z"],"software life macos",["Date","2023-04-13T00:00:00.000Z"],{"slug":58,"path":59,"code":60,"fm":61},"switch-ports","/articles/switch-ports","\u003Cscript context=\"module\">\n\texport const metadata = {\"layout\":\"post\",\"title\":\"My Switch Ports, and the Journey\",\"date\":\"2022-11-13T00:00:00.000Z\",\"categories\":\"games programming life\",\"last_modified_at\":\"2022-11-13T00:00:00.000Z\",\"comments\":true};\n\tconst { layout, title, date, categories, last_modified_at, comments } = metadata;\n\u003C/script>\n\u003Cscript lang=\"ts\">\n import Figure from \"$lib/Figure.svelte\";\n import Image from \"$lib/Image.svelte\";\n \n import imgSRB2Pic1 from \"./20221114185059_1.jpg?as=run\";\n import imgSRB2Pic2 from \"./20221114185137_1.jpg?as=run\";\n import imgSRB2KPic1 from \"./20221114191553_1.jpg?as=run\";\n import imgSRB2KPic2 from \"./20221114191630_1.jpg?as=run\";\n import imgS1Pic1 from \"./20221114185830_1.jpg?as=run\";\n import imgS1Pic2 from \"./20221114185444_1.jpg?as=run\";\n import imgS2Pic1 from \"./20221114185608_1.jpg?as=run\";\n import imgS2Pic2 from \"./20221114185539_1.jpg?as=run\";\n import imgS3AIRPic1 from \"./20221114185702_1.jpg?as=run\";\n import imgS3AIRPic2 from \"./20221114185646_1.jpg?as=run\";\n import imgDeck from \"./IMG_2002.jpg?as=run\";\n\u003C/script>\n\n\u003Cp>I was considering making this intro a lot more long winded, but instead I’ll just say this: my motivation for porting these games to the Switch mostly came from wanting to play them there myself. At the time I ported these games, the Switch was the best option to play these games on for several reasons:\u003C/p>\n\u003Cul>\n\u003Cli>Easy to homebrew (if you have a vulnerable unit)\u003C/li>\n\u003Cli>Dockable (obvious)\u003C/li>\n\u003Cli>Powerful enough to run ports without extensive optimization\u003C/li>\n\u003Cli>Good UI\u003C/li>\n\u003Cli>Good controllers\u003C/li>\n\u003Cli>Lightweight\u003C/li>\n\u003Cli>Decent battery\u003C/li>\n\u003C/ul>\n\u003Cp>Obviously, I could’ve just put these games on a laptop and played them there, or used an Android phone and a controller, or whatever else, but it just doesn’t fit the bill of having a console-like experience. The Switch is in my top 3 consoles with the GameCube. So, what did I want on there?\u003C/p>\n\u003Ch2>Sonic Robo Blast 2\u003C/h2>\n\u003CFigure>\n \u003CImage src={imgSRB2Pic1} />\n \u003CImage src={imgSRB2Pic2} />\n\u003C/Figure>\n\u003Cp>To people out of the know, this fangame has been in development for roughly 24 years. Twenty. Four. It’s based on Doom of all things (can’t recall which source port off the top of my head) and is unexpectedly super fun to play. I’ve followed the game since I was probably like 7 or 8 and wanted a portable version ever since. At the time I ported it, the best options were to play it on something like a GPD Win, OpenPandora, or a PSP, all pretty lacking consoles for the modern age. This was also before an Android or iOS port.\u003C/p>\n\u003Cp>carstene1ns was responsible for the original port. They released a proof-of-concept port that ran at a pretty terrible framerate. I decided to attempt building their port myself, and found that the poor framerate wasn’t their fault at all. In the time between our builds, the Switch port of SDL2 had been updated to where it just… worked. So, I picked up maintenance of the port and added some more quality of life things:\u003C/p>\n\u003Cul>\n\u003Cli>Internet connectivity\n\u003Cul>\n\u003Cli>Fixed on accident while adding nxlink support!\u003C/li>\n\u003C/ul>\n\u003C/li>\n\u003Cli>On-screen keyboard support\u003C/li>\n\u003Cli>Ability to run in background (to avoid people essentially lag-switching servers)\u003C/li>\n\u003C/ul>\n\u003Cp>In addition to C programming, these ports required knowledge of SDL2, libnx, and GNU Make. At the time, I had limited knowledge of each. By the time I was done, I became fairly affluent in each. Learning to read through git history to pick out breaking changes was an essential skill needed to accomplish this as well. All in all, it was a great learning experience and the gateway to all my other ports.\u003C/p>\n\u003Cp>My only big regret was that I never got GL working. I made a few attempts, but the sheer size of the codebase, uncertainty of the state of the DevkitPro ports, and layers of complexity involved made it a time sink I wasn’t ready to take on.\u003C/p>\n\u003Ch2>Sonic Robo Blast 2 Kart\u003C/h2>\n\u003CFigure>\n \u003CImage src={imgSRB2KPic1} />\n \u003CImage src={imgSRB2KPic2} />\n\u003C/Figure>\n\u003Cp>I could try and explain how a mod of a mod of Doom turned into a kart racer, but instead I’ll just say that it was a fairly simple ordeal to port changes from SRB2 here. Not much else to say honestly. Play SRB2Kart.\u003C/p>\n\u003Ch2>Sonic 1, 2, and CD Decompilations\u003C/h2>\n\u003CFigure layout=\"2col\">\n \u003CImage src={imgS1Pic1} />\n \u003CImage src={imgS1Pic2} />\n \u003CImage src={imgS2Pic1} />\n \u003CImage src={imgS2Pic2} />\n\u003C/Figure>\n\u003Cp>Sonic CD got a remake from the ground up in 2011, and Sonic 1 and 2 followed suit in 2013. These were all done with Christian Whitehead’s (taxman’s) Retro Engine. I don’t know the full details of how, but they all got decompiled in 2021 by RubberDuckyCooly and RMGRich (and probably a few others, sorry if I didn’t list you guys here). At this point, the DevkitPro Switch portlibs had matured to the point where I could mostly just create a Makefile for them and they were good to go. A few fixes to input were needed here and there, along with some cflag changes. These ports were feature-complete, as far as I’m aware.\u003C/p>\n\u003Ch2>Sonic 3 AIR\u003C/h2>\n\u003CFigure>\n \u003CImage src={imgS3AIRPic1} />\n \u003CImage src={imgS3AIRPic2} />\n\u003C/Figure>\n\u003Cp>Sonic 3 AIR is a fan-done translation of the original Sonic 3 and Knuckles source code into a custom engine and scripting language, done by the legendary Eukaryot. I reached out to them before the source code for this recreation was public, and they agreed to give me access for the production of a Switch port. I got it up and running, feature-complete in about a week, including hardware rendering.\u003C/p>\n\u003Ch1>Why I Left, and an Apology\u003C/h1>\n\u003Cp>This year has been filled with a lot of grief, anxiety, and pain. I had to make a lot of changes to find happiness. I’ll probably write more about this towards the end of the year. One of those change included dropping maintenance of my Switch port to make time for things that mattered more to me: friends, family, my studies, and my career goals.\u003C/p>\n\u003Cp>On top of that, the whole motivation for wanting these ports had been torn to shreds… remember how I mentioned that the Switch and the GameCube were two of my favorite consoles? You may have also noticed all these screenshots are 16:10. Well, I’ve got something else sitting at number one now…\u003C/p>\n\u003CFigure>\n \u003CImage src={imgDeck} />\n\u003C/Figure>\n\u003Cp>Yup. As soon as I got the thing in my hands, my motivation for Switch development honestly went out the window.\u003C/p>\n\u003Cul>\n\u003Cli>Similar form factor to the Switch\u003C/li>\n\u003Cli>Better feeling controls\u003C/li>\n\u003Cli>Easier to “homebrew” (I mean, it’s just Arch underneath)\u003C/li>\n\u003Cli>Don’t have to develop dedicated ports\u003C/li>\n\u003Cli>Plays literally everything else I want to play minus (online) Switch games\u003C/li>\n\u003C/ul>\n\u003Cp>Having grown up on homebrew and fan projects, I know how much it sucks to see something you like go unmaintained. Being on the other side of the fence now, I can see why so many people go ghost mode. I feel a bit guilty for dropping everything, but I need to change focus until I am happy.\u003C/p>\n\n",{"layout":7,"title":62,"date":63,"categories":64,"last_modified_at":65,"comments":30},"My Switch Ports, and the Journey",["Date","2022-11-13T00:00:00.000Z"],"games programming life",["Date","2022-11-13T00:00:00.000Z"],{"slug":67,"path":68,"code":69,"fm":70},"new-blog","/articles/new-blog","\u003Cscript context=\"module\">\n\texport const metadata = {\"layout\":\"post\",\"title\":\"New Blog?\",\"date\":\"2022-11-07T00:00:00.000Z\",\"last_modified_at\":\"2023-02-11T00:00:00.000Z\",\"categories\":\"meta life design ui\",\"comments\":true};\n\tconst { layout, title, date, last_modified_at, categories, comments } = metadata;\n\u003C/script>\n\u003Cscript lang=\"ts\">\n import Image from \"$lib/Image.svelte\";\n import Figure from \"$lib/Figure.svelte\";\n \n import imgWin98Logo from \"./Windows_98_logo.webp?as=run\";\n import imgXPMCE from \"./xpmce.jpg?as=run\";\n\u003C/script>\n\n\u003Cp>So, I’m restructuring a lot of things in life right now. Why don’t we take care of the website too?\u003C/p>\n\u003Cp>I’ve been trying to find a good balance between maintainability, customization, and weight. Usually the mainstream options don’t hold up to those principles very well, but Jekyll actually manages it. It’s nice to be able to write everything in markdown, and the default templates are pretty simple and lightweight. \u003Cdel>Right now the only JS dependency is jQuery, we’ll see if it stays that way.\u003C/del> \u003Cem>EDIT 2/11/23: Removed jQuery. ;)\u003C/em> Will probably at least add a lightbox plugin for images, eventually.\u003C/p>\n\u003Cp>My site was previously written using Hexo, and while it got close to what I wanted, it was one of those things that was just non-mainstream enough that I could never get back into the flow of setting up a dev environment so that I could actually get to the writing. Lets hope this time goes a bit better!\u003C/p>\n\u003Cp>My previous website designs very heavily followed Material Design. I wanted to try something a bit more unique with this new site. I’ve taken inspiration from a few places:\u003C/p>\n\u003Ch3>Windows 98/XP\u003C/h3>\n\u003CFigure align=\"right\">\n \u003CImage src={imgWin98Logo} />\n \u003CImage src={imgXPMCE} />\n\u003C/Figure>\n\u003Cp>Most people referencing this would just throw on a vaporwave skin, maybe sprinkle a few Arizona cans and Evangelion renders here and there. I wanted to be a bit more subtle. The main font for titles and headers is Franklin Gothic, lifted straight from the Windows 98 logo. In its black weight, it’s very pleasing to look at while remaining readable. The main font for content is MS Sans Serif, which was used in the Windows 98 UI. I’m not 100% set on it right now but I figure its a nice callback. I tried Tahoma at one point (Windows XP UI font) but it’s a bit too playful for my taste; gets kinda obnoxious to look at.\u003C/p>\n\u003Cp>The background elements also call back to the design of Windows Media Center, which was included with the first PC my parents’ bought new for me.\u003C/p>\n\u003Ch3>iOS\u003C/h3>\n\u003Cp>Well, not exclusive to iOS, but certain elements of the site have a frosted glass touch to them. It’s subtle with the mostly-black background, but adds some nice flair.\u003C/p>\n\u003Ch3>“The 70’s”\u003C/h3>\n\u003Cp>Bold text, bold colors, simple designs. No references to the era specifically, just kinda lifting the general vibe.\u003C/p>\n\u003Ch3>Life\u003C/h3>\n\u003Cp>Orange is my favorite color. Wife’s favorite color bounces between sky blue and pastel pink. Our wedding colors were orange and pink. Nuff’ said.\u003C/p>\n\u003Ch3>What’s with the dice?\u003C/h3>\n\u003Cp>Needed a background element to go with the XPMCE thing. I was demoing this aesthetic on a custom startpage, and I figured it’d be fun to make it something randomized. At the same time, I wanted it to have a utilitarian purpose, so if I ever need a quick dice roll, I can just open a new tab! I’ve carried it over here, but might switch it in the future.\u003C/p>\n\u003Ch2>Going Forward\u003C/h2>\n\u003Cp>I want to spend more time doing things that bring me instrinsic happiness. Will writing in this blog be one of them? Who knows! But I’ll make an effort to keep up. Before I move onto the future, I’ll be transferring some content from my old site, doing some write-ups and reflections on my past few years of projects/life stuff, and having a bit of fun.\u003C/p>\n\n",{"layout":7,"title":71,"date":72,"last_modified_at":73,"categories":74,"comments":30},"New Blog?",["Date","2022-11-07T00:00:00.000Z"],["Date","2023-02-11T00:00:00.000Z"],"meta life design ui",{"slug":76,"path":77,"code":78,"fm":79},"photo-megapost","/articles/photo-megapost","\u003Cscript context=\"module\">\n\texport const metadata = {\"layout\":\"post\",\"title\":\"Photography Megapost\",\"date\":\"2022-11-07T00:00:00.000Z\",\"categories\":\"life photography\",\"comments\":true};\n\tconst { layout, title, date, categories, comments } = metadata;\n\u003C/script>\n\u003Cscript lang=\"ts\">\n\nimport { type GalleryDescriptor } from \"$lib/Gallery\";\nimport Gallery from '$lib/Gallery.svelte';\n\nexport let data;\n\nconst galleries = [\n {\n name: \"January 2019\",\n description: \"Point Au Roche\",\n images: data.galleries[\"jan-2019-point-au-roche\"],\n thumbs: import.meta.glob('./jan-2019-point-au-roche/*.jpg', {\n eager: true,\n query: { w: 160, h: 160, fit: 'cover', as: 'run' }\n })\n },\n {\n name: \"Summer 2018\",\n description: \"Some miscellaneous photos taken during my time working at the Whiteface ASRC in 2018.\",\n images: data.galleries[\"summer-2018-whiteface\"],\n thumbs: import.meta.glob('./summer-2018-whiteface/*.jpg', {\n eager: true,\n query: { w: 160, h: 160, fit: 'cover', as: 'run' }\n })\n },\n {\n name: \"Summer 2018 (Night)\",\n description: \"During the summer of 2018, I was given the opportunity to spend the night on the summit of Whiteface. Here are some of the photos I took in all their post-processed glory.\",\n images: data.galleries[\"summer-2018-whiteface-night\"],\n thumbs: import.meta.glob('./summer-2018-whiteface-night/*.jpg', {\n eager: true,\n query: { w: 160, h: 160, fit: 'cover', as: 'run' }\n })\n },\n {\n name: \"Misc\",\n description: \"Miscellaneous photos from my younger years.\",\n images: data.galleries[\"misc\"],\n thumbs: import.meta.glob('./misc/*.jpg', {\n eager: true,\n query: { w: 160, h: 160, fit: 'cover', as: 'run' }\n })\n }\n] as GalleryDescriptor[];\n\n\u003C/script>\n\n\u003Cp>Various snapshots of my life.\u003C/p>\n\u003Cp>\u003Ca\n href=\"https://creativecommons.org/licenses/by/4.0/\"\n rel=\"nofollow\"\n>These photos are licensed under CC BY 4.0.\u003C/a>\u003C/p>\n{#each galleries as gallery}\n\u003CGallery {gallery} />\n{/each}\n\n",{"layout":7,"title":80,"date":81,"categories":82,"comments":30},"Photography Megapost",["Date","2022-11-07T00:00:00.000Z"],"life photography",{"slug":84,"path":85,"code":86,"fm":87},"gd-whiteface","/articles/gd-whiteface","\u003Cscript context=\"module\">\n\texport const metadata = {\"layout\":\"post\",\"title\":\"Graphic Design: Whiteface ASRC Logo\",\"date\":\"2022-01-20T00:00:00.000Z\",\"categories\":\"design\",\"comments\":true};\n\tconst { layout, title, date, categories, comments } = metadata;\n\u003C/script>\n\u003Cscript lang=\"ts\">\n import Image from \"$lib/Image.svelte\";\n import Figure from \"$lib/Figure.svelte\";\n \n import imgLogo from \"./logo.svg\";\n import imgPhoto from \"./photo.jpg?as=run\";\n\u003C/script>\n\n\u003Cp>A quick overview of my logo design.\u003C/p>\n\u003CFigure frame=\"light\">\n \u003CImage src={imgLogo} />\n\u003C/Figure>\n\u003Cp>The Whiteface ASRC logo was designed seperately from the SUNY Albany ASRC logo in order to give the Whiteface ASRC a more distinct identity without tampering with the historical background of the original ASRC logo. The logo is modeled after the shape of the round-house and silo at the top of the mountain while also including key elements such as the shape of the mountain, the silhouette of the cloud collector, and the radio platform.\u003C/p>\n\u003CFigure>\n \u003CImage src={imgPhoto} />\n\u003C/Figure>\n\u003Cp>The font used is Josefin Sans and was mainly chosen due to its bold look and the distinct shape of its W. The logo was designed in Inkscape.\u003C/p>\n\n",{"layout":7,"title":88,"date":89,"categories":90,"comments":30},"Graphic Design: Whiteface ASRC Logo",["Date","2022-01-20T00:00:00.000Z"],"design",{"slug":92,"path":93,"code":94,"fm":95},"gd-anime-was-a-mistake","/articles/gd-anime-was-a-mistake","\u003Cscript context=\"module\">\n\texport const metadata = {\"title\":\"Graphic Design: \\\"Anime was a Mistake\\\" Shirt\",\"date\":\"2019-01-21T00:00:00.000Z\",\"categories\":\"design ui\",\"last_modified_at\":\"2022-11-07T00:00:00.000Z\",\"comments\":true};\n\tconst { title, date, categories, last_modified_at, comments } = metadata;\n\u003C/script>\n\u003Cscript lang=\"ts\">\n\nimport Image from \"$lib/Image.svelte\";\nimport Figure from \"$lib/Figure.svelte\";\n\nimport imgFinal from \"./final.png?as=run:0\";\nimport imgLetters from \"./letters.svg\";\nimport imgBorder from \"./border.svg?as=run\";\nimport imgImport from \"./import.png?as=run\";\nimport imgTop from \"./top.png?as=run\";\nimport imgRaw from \"./raw.png?as=run:0\";\nimport imgOdyssey from \"./odyssey.png?as=run:0\";\n\nimport fileBlend from \"./anime.blend\";\n\n\u003C/script>\n\n\u003Cp>A not-so-serious venture into 3D graphic design.\u003C/p>\n\u003CFigure>\n \u003CImage src={imgFinal} />\n\u003C/Figure>\n\u003Cp>This design was published as a shirt design on RedBubble back in December of 2017. \u003Cdel>You can view the design on RedBubble here.\u003C/del> Unfortunately, Nintendo themselves DMCA’d it because they felt like being petty.\u003C/p>\n\u003Ch2>Making Of\u003C/h2>\n\u003Cp>The first thing that was done was obtaining the Super Mario 256 font. This was then used to type the words onto an SVG using Inkscape. A second SVG was used to make the borders for the letters.\u003C/p>\n\u003CFigure frame=\"light\">\n \u003CImage src={imgLetters} />\n \u003CImage src={imgBorder} />\n\u003C/Figure>\n\u003Cp>These were then imported into Blender and extruded to make them 3D. Although the letters seem shallow in the final render, they are actually quite long. The letters were then given materials with appropriate colors.\u003C/p>\n\u003CFigure>\n \u003CImage src={imgImport} />\n \u003CImage src={imgTop} />\n\u003C/Figure>\n\u003Cp>The raw render out of Blender is as follows:\u003C/p>\n\u003CFigure>\n \u003CImage src={imgRaw} />\n\u003C/Figure>\n\u003Cp>It’s lacking a lot of little details though. The letters have no gradients or sparkles, and the foreground graphic of Cappy is straight up missing. The final details were added in GIMP:\u003C/p>\n\u003CFigure>\n \u003CImage src={imgFinal} />\n\u003C/Figure>\n\u003Cp>For comparison, here’s the original logo:\u003C/p>\n\u003CFigure>\n \u003CImage src={imgOdyssey} />\n\u003C/Figure>\n\u003Ch2>Thoughts\u003C/h2>\n\u003Cp>There’s definitely a lot of things that could be improved here. The planet from the “O” in the original logo is completely missing. A lot of materials don’t look metallic enough. The borders don’t come down at the right angle and have gaps. Colors are a bit desaturated. The middle letters aren’t stretched enough and the top letters are too big. The Cappy graphic appears to be out of date (?). However, for something that was honestly just intended as a joke, I think it came out alright.\u003C/p>\n\u003Ch2>Download\u003C/h2>\n\u003Cp>If you’d like to download the original project file, you’re in luck! Here it is:\u003C/p>\n\u003Cp>\u003Ca href=\"{fileBlend}\">Blender Project\u003C/a>\u003C/p>\n\u003Cp>If you want the render itself, you should be able to just right click and “Save As”. Same goes for the SVGs.\u003C/p>\n\u003Cp>\u003Ca\n href=\"https://creativecommons.org/licenses/by/4.0/\"\n rel=\"nofollow\"\n>This project is licensed under CC BY 4.0.\u003C/a>\u003C/p>\n\n",{"title":96,"date":97,"categories":98,"last_modified_at":99,"comments":30},"Graphic Design: \"Anime was a Mistake\" Shirt",["Date","2019-01-21T00:00:00.000Z"],"design ui",["Date","2022-11-07T00:00:00.000Z"],{"slug":101,"path":102,"code":103,"fm":104},"gd-mayolo","/articles/gd-mayolo","\u003Cscript context=\"module\">\n\texport const metadata = {\"layout\":\"post\",\"title\":\"Graphic Design: \\\"Mayolo\\\" Shirt\",\"date\":\"2019-01-21T00:00:00.000Z\",\"categories\":\"design\",\"comments\":true};\n\tconst { layout, title, date, categories, comments } = metadata;\n\u003C/script>\n\u003Cscript lang=\"ts\">\n import Image from \"$lib/Image.svelte\";\n import Figure from \"$lib/Figure.svelte\";\n \n import imgFinal from \"./final.png?as=run:\";\n import imgText from \"./text.png?as=run:\";\n import imgTextSVG from \"./text.svg\";\n import imgEyes from \"./eyes.jpg?as=run:\";\n import imgEyesSVG from \"./eyes.svg\";\n import imgNormal from \"./normal.jpg?as=run:\";\n import imgBottle from \"./bottle.png?as=run:\";\n import imgLighting from \"./lighting.png?as=run:\";\n import imgFirst from \"./first.png?as=run:\";\n import imgTireBlur from \"./tireblur.png?as=run:\";\n import imgTire from \"./tire.png?as=run:\";\n import imgTracks from \"./tracks.png?as=run:\";\n import imgSecond from \"./second.png?as=run:\";\n\u003C/script>\n\n\u003Cp>How I did my first commission!\u003C/p>\n\u003CFigure>\n \u003CImage src={imgFinal} />\n\u003C/Figure>\n\u003Cp>This design was a done as commission for someone. I was given the general idea of a cartoon mayo bottle writing out the words “Mayolo”, and the rest was up to my own judgement.\u003C/p>\n\u003Ch2>Making Of\u003C/h2>\n\u003Cp>I started by writing out the text. After doing some sketches, I settled on this text and translated it into an SVG. I was requested to change the “Y” uppercase and did so in the final SVG. The line was brought down to a middle point in order to place the mayo bottle more easily.\u003C/p>\n\u003CFigure frame=\"light\">\n \u003CImage src={imgText} />\n \u003CImage src={imgTextSVG} />\n\u003C/Figure>\n\u003Cp>I originally wanted to draw in the eyes in either GIMP or Inkscape, but eventually found it to look bad. I made the eye shape into an SVG as well, and then imported both into Blender.\u003C/p>\n\u003CFigure>\n \u003CImage src={imgEyes} />\n \u003CImage src={imgEyesSVG} />\n\u003C/Figure>\n\u003Cp>The mayo bottle was modeled by hand, and is essentially just a carefully extruded circle. The edge of the cap was given a bumpy normal map in order to simulate the look of the grip.\u003C/p>\n\u003CFigure>\n \u003CImage src={imgNormal} />\n \u003CImage src={imgBottle} />\n\u003C/Figure>\n\u003Cp>The text and eyes were extruded, beveled, and given materials. The text was also given an armature so it could be “posed” more easily. The pupils were made from squished down spheres. Objects were positioned and then given numerous light sources. The lighting is crucial in order to give the final render a less gritty appearance. The below image highlights all the different light sources in the final render: 11 in total.\u003C/p>\n\u003CFigure>\n \u003CImage src={imgLighting} />\n\u003C/Figure>\n\u003Cp>The scene was rendered and then given some soft shadows and a background color using GIMP.\u003C/p>\n\u003CFigure>\n \u003CImage src={imgFirst} />\n\u003C/Figure>\n\u003Cp>However, the client wanted a few more details added:\u003C/p>\n\u003Cul>\n\u003Cli>Eyebrows\u003C/li>\n\u003Cli>Smile\u003C/li>\n\u003Cli>Wheels\u003C/li>\n\u003Cli>Skid marks\u003C/li>\n\u003Cli>Peel-out smoke/sparks\u003C/li>\n\u003C/ul>\n\u003Cp>The eyebrows were fairly straightforwards. Give an armature to a long cylinder and bend it. Same goes for the smile. The wheels were a bit trickier. A circle was extruded into the shape of half of a wheel, and then the model was mirrored. A tire texture was made and then UV mapped onto the model. The texture was then blurred in one direction to give the appearance of motion. The inner rim was given a silver, metalic material.\u003C/p>\n\u003CFigure>\n \u003CImage src={imgTireBlur} scale=0.01 />\n \u003CImage src={imgTire} />\n\u003C/Figure>\n\u003Cp>The skid marks were made from two long extended planes and given a blurry, transparent, and lightened version of the tire texture. Despite what it looks like in the render, the skid marks stretch extremely far into the background.\u003C/p>\n\u003CFigure>\n \u003CImage src={imgTracks} />\n\u003C/Figure>\n\u003Cp>The peel-out smoke and sparks were created using particle emitters placed directly behind the tires. These were given “Smoke” properties with a flow type of “Fire + Smoke”. The raw render looked like this after compositing all layers:\u003C/p>\n\u003CFigure>\n \u003CImage src={imgSecond} />\n\u003C/Figure>\n\u003Cp>However, the flame effect was a bit lacking. Extra flame and smoke graphics were added to make the final render:\u003C/p>\n\u003CFigure>\n \u003CImage src={imgFinal} />\n\u003C/Figure>\n\n",{"layout":7,"title":105,"date":106,"categories":90,"comments":30},"Graphic Design: \"Mayolo\" Shirt",["Date","2019-01-21T00:00:00.000Z"]],"uses":{}}]} +{"type":"data","nodes":[{"type":"data","data":[{"categories":1},[2,3,4,5,6,7,8,9,10,11,12,13],"design","ui","programming","life","software","meta","photography","work","portfolio","macos","multiplatform","games"],"uses":{},"slash":"always"},{"type":"data","data":[{"articles":1},[2,10,19,29,38,46,55,64,73,81,89,98],{"slug":3,"path":4,"code":5,"fm":6},"lcatdb","/articles/lcatdb","\u003Cscript context=\"module\">\n\texport const metadata = {\"title\":\"Retrospective: lcatDB\",\"date\":\"2023-05-18T00:00:00.000Z\",\"categories\":\"programming life software ui\"};\n\tconst { title, date, categories } = metadata;\n\u003C/script>\n\u003Cscript lang=\"ts\">\n import Image from \"$lib/Image.svelte\";\n import Video from \"$lib/Video.svelte\";\n import Figure from \"$lib/Figure.svelte\";\n \n import imgLogin1 from \"./localhost_3000_login.html.png?as=run\";\n import imgLogin2 from \"./localhost_3000_login.html (1).png?as=run\";\n import vidResponsive from \"./lcatDB Responsive Demo.mp4\";\n import vidPages from \"./lcatDB Pages Demo.mp4\";\n\u003C/script>\n\n\u003Cp>lcatDB (Lake Champlain Anglers’ Temperature Database) was an online database and single page application (SPA) meant to provide a centralized means of recording, accessing, and analyzing vertical water temperature profiles to citizens of the Lake Champlain region. It was supported by the SUNY Plattsburgh Center for Earth & Environmental Science and the Lake Champlain Sea Grant. lcatDB was (what I consider to be) my first attempt at full stack software development. While the app never really took off and is no longer around today, I was able to demonstrate many of my strengths and learned several valuable lessons.\u003C/p>\n\u003Ch1>Client\u003C/h1>\n\u003CFigure>\n \u003CImage src={imgLogin1} />\n \u003CImage src={imgLogin2} />\n\u003C/Figure>\n\u003Cp>The UI of lcatDB very closely follows Google’s (now aged) Material Design guidelines. A hamburger menu is always available for root-level navigation and all elements are kept large, touch-friendly, and readable. Subtle iconography and color is used to draw attention to important elements and differentiate options. One element that I take pride in across most of my projects is responsive CSS, allowing for automatic adaptations to mobile, tablet, and desktop layouts.\u003C/p>\n\u003CFigure>\n \u003CVideo src={vidResponsive} />\n\u003C/Figure>\n\u003Cp>lcatDB’s client was built using several JavaScript libraries, frameworks, and related tools, including:\u003C/p>\n\u003Cul>\n\u003Cli>Grunt (build system)\u003C/li>\n\u003Cli>Cordova (Android/iOS builds)\u003C/li>\n\u003Cli>jQuery\u003C/li>\n\u003Cli>Bootstrap\u003C/li>\n\u003Cli>Leaflet\u003C/li>\n\u003Cli>Mustache (for page templating)\u003C/li>\n\u003C/ul>\n\u003Cp>The client is structured as a SPA, and thus can run offline (albeit with some restrictions). An event queue was added to allow for offline operations to be uploaded to the server later, which was a required use-case due to poor cell service in the middle of the lake. Below is a video demo of various features and interactions!\u003C/p>\n\u003CFigure>\n \u003CVideo src={vidPages} />\n\u003C/Figure>\n\u003Ch1>Server-Side\u003C/h1>\n\u003Cp>The server is probably the least interesting to talk about, but still quite important! It’s written for Node.JS and uses a mongoDB database to store users, readings, sensor information, and various other data! It provides a REST API and, in addition to serving the static pages (the same ones available offline), it uses mustache templates to provide pages for content only accessible online. I’ll talk more about this system later and my thoughts on its use.\u003C/p>\n\u003Cp>Passwords are stored as bcrypt hashes for essential user security. Users authenticate using \u003Ca\n href=\"https://hacks.mozilla.org/2012/12/using-secure-client-side-sessions-to-build-simple-and-scalable-node-js-applications-a-node-js-holiday-season-part-3/\"\n rel=\"nofollow\"\n>client sessions\u003C/a> which can be invalidated (logged out) server-side in case of certain user actions (eg. password resets). To ensure the accountability of data while providing basic editing features, edits to sensor information are tracked using mongoDB middleware and made visible to users. These are just some examples of security considerations made during development.\u003C/p>\n\u003Ch1>What I Would Do Differently\u003C/h1>\n\u003Cp>First of all, revisiting this codebase after working in Python 3 primarily for a few years and getting used to type annotations and IntelliSense… ouch. Very frequently I have to trace the flow of control manually with \u003Ccode>console.log\u003C/code>s and a lot of reading. I would definitely switch to TypeScript nowadays as things would be much more navigable. However, there’s a lot of deeper issues than that. Some of these arose due to the simple passage of time: some libraries and tools get adopted and maintained, some don’t.\u003C/p>\n\u003Cp>Firstly, lets address the use of Grunt. At the time of using it, it was relatively well-supported and I got used to it fairly quickly. The ability to mix JS scripting into your build system has a lot of benefits, though maybe a bit encouraging of hacky-ness. Nowadays, Grunt is pretty much dead. No releases in 2 years, and its mainly been surpassed by webpack. I’ve taken the hint and also begun migrating some of my older projects to webpack.\u003C/p>\n\u003Cp>Some issues, however, arose from my own inexperience. The biggest pill to swallow from this project is: it’s time to move on from jQuery and onto a proper framework. They’re infinitely less prone to breaking, with less time spent programming destructor functions and chasing bugs. jQuery’s purpose still feels mostly limited to small sites that need some dumb small feature quick and you don’t wanna deal with the poor syntax of vanilla JS, which between TS IntelliSense and AI code suggestions isn’t really even a problem anymore.\u003C/p>\n\u003Cp>Which leads to the root of that issue: \u003Cem>lcatDB did not originally plan to be a SPA\u003C/em>. That was added after many of the pages were implemented and the code was written, so the SPA capabilities were hacked on top. jQuery does not suit itself well in this use case, but at the time I couldn’t justify the time as a single developer to go back and start from scratch. And with the scale of the project growing further in a small scope-creep, the problems from jQuery scaled up as well. Despite the amount of time I spent considering my options for libraries and platforms, the change in scope caused a pile-up of other issues. This was the single-biggest takeaway: nail down the entire set of specifications first, then implement. Be very specific and thorough. Even then, it’s still easier said than done; at the time I didn’t even know what questions to ask.\u003C/p>\n\n",{"title":7,"date":8,"categories":9},"Retrospective: lcatDB",["Date","2023-05-18T00:00:00.000Z"],"programming life software ui",{"slug":11,"path":12,"code":13,"fm":14},"responsive-design","/articles/responsive-design","\u003Cscript context=\"module\">\n\texport const metadata = {\"title\":\"Responsive Design Showcase\",\"date\":\"2023-05-18T00:00:00.000Z\",\"categories\":\"programming ui design\",\"last_modified_at\":\"2023-05-19T00:00:00.000Z\"};\n\tconst { title, date, categories, last_modified_at } = metadata;\n\u003C/script>\n\u003Cscript lang=\"ts\">\n import Image from \"$lib/Image.svelte\";\n import Video from \"$lib/Video.svelte\";\n import Figure from \"$lib/Figure.svelte\";\n\n import vidWhiteface from \"./Whiteface Responsive Demo.mp4\";\n import vidLcatDB from \"./lcatDB Responsive Demo.mp4\";\n import vidHeyjoeway from \"./heyjoeway Responsive Demo.mp4\";\n\u003C/script>\n\n\u003Cp>Here’s a mainly visual showcase of some of my work in responsive design!\u003C/p>\n\u003CFigure>\n \u003CVideo src={vidWhiteface} />\n \u003Cfigcaption slot=\"caption\">The Whiteface Tour is a 360-degree virtual tour of the Whiteface Mountain summit I developed using three.js. \u003Ca href=\"https://jojudge.com/whitefacetour\">Try it for yourself here!\u003C/a>\u003C/figcaption>\n\u003C/Figure>\n\u003CFigure>\n \u003CVideo src={vidLcatDB} />\n \u003Cfigcaption slot=\"caption\">lcatDB was a full-stack single-page application which I developed. \u003Ca href=\"/articles/lcatdb\">You can read more about it here.\u003C/a>\u003C/figcaption>\n\u003C/Figure>\n\u003CFigure>\n \u003CVideo src={vidHeyjoeway} />\n \u003Cfigcaption slot=\"caption\">And last but not least, the site you're reading this on! Try it for yourself!\u003C/figcaption>\n\u003C/Figure>\n\n",{"title":15,"date":16,"categories":17,"last_modified_at":18},"Responsive Design Showcase",["Date","2023-05-18T00:00:00.000Z"],"programming ui design",["Date","2023-05-19T00:00:00.000Z"],{"slug":20,"path":21,"code":22,"fm":23},"software-dump-multiplat","/articles/software-dump-multiplat","\u003Cscript context=\"module\">\n\texport const metadata = {\"title\":\"The Software Dump Part 2: Multiplatform\",\"date\":\"2023-04-11T00:00:00.000Z\",\"categories\":\"software life multiplatform\",\"last_modified_at\":\"2023-04-11T00:00:00.000Z\",\"comments\":true};\n\tconst { title, date, categories, last_modified_at, comments } = metadata;\n\u003C/script>\n\u003Cscript lang=\"ts\">\n import Link from \"$lib/Link.svelte\";\n\u003C/script>\n\n\n\u003Cp>This is a continuation of my last post where I discussed programs exclusively for macOS. The software here should be available on all platforms (Win/Mac/Linux). If it’s software that’s already been covered ad nauseam everywhere else or something I don’t use regularly, it’s probably not getting listed here.\u003C/p>\n\u003Ch1>Audio\u003C/h1>\n\u003Ch2>Plexamp\u003C/h2>\n\u003Cul>\n\u003Cli>\u003Cstrong>Price:\u003C/strong> \u003Ca href=\"https://www.plex.tv/plex-pass\" rel=\"nofollow\">It’s complicated\u003C/a>\u003C/li>\n\u003Cli>\u003Cstrong>OSS:\u003C/strong> No\u003C/li>\n\u003Cli>\u003Cstrong>Download:\u003C/strong> \u003CLink href=\"https://plexamp.com/\" />\u003C/li>\n\u003C/ul>\n\u003Cp>My favorite music app. Great offline support (either through manual downloads or automatic caching), a good amount of customization, and the now forgotten ability to curate a music collection outside of what some streaming service dictates you should have.\u003C/p>\n\u003Ch1>Content Creation\u003C/h1>\n\u003Ch2>Blender\u003C/h2>\n\u003Cul>\n\u003Cli>\u003Cstrong>Price:\u003C/strong> Free\u003C/li>\n\u003Cli>\u003Cstrong>OSS:\u003C/strong> Yes (\u003CLink href=\"https://projects.blender.org/blender/blender.git\" />)\u003C/li>\n\u003Cli>\u003Cstrong>Download:\u003C/strong> \u003CLink href=\"https://www.blender.org/\" />\u003C/li>\n\u003C/ul>\n\u003Cp>We are absolutely blessed that a tool this powerful is completely free and open source. I’m no expert at modeling, but it absolutely excels when I need to do stuff in 3D.\u003C/p>\n\u003Ch2>GIMP\u003C/h2>\n\u003Cul>\n\u003Cli>\u003Cstrong>Price:\u003C/strong> Free\u003C/li>\n\u003Cli>\u003Cstrong>OSS:\u003C/strong> Yes (\u003CLink href=\"https://gitlab.gnome.org/GNOME/gimp.git\" />)\u003C/li>\n\u003Cli>\u003Cstrong>Download:\u003C/strong> \u003CLink href=\"https://www.gimp.org/\" />\u003C/li>\n\u003C/ul>\n\u003Cp>Tried and true, and (mostly) gets the job done. Pace of development seems to have slowed and I will be migrating away from it as time goes on.\u003C/p>\n\u003Ch2>Krita\u003C/h2>\n\u003Cul>\n\u003Cli>\u003Cstrong>Price:\u003C/strong> Free\u003C/li>\n\u003Cli>\u003Cstrong>OSS:\u003C/strong> Yes (\u003CLink href=\"https://gitlab.gnome.org/GNOME/gimp.git\" />)\u003C/li>\n\u003Cli>\u003Cstrong>Download:\u003C/strong> \u003CLink href=\"https://krita.org\" />\u003C/li>\n\u003C/ul>\n\u003Cp>My prospective replacement for GIMP. Feels a bit more modern and is seemingly being developed at a faster pace.\u003C/p>\n\u003Ch2>OBS Studio\u003C/h2>\n\u003Cul>\n\u003Cli>\u003Cstrong>Price:\u003C/strong> Free\u003C/li>\n\u003Cli>\u003Cstrong>OSS:\u003C/strong> Yes (\u003CLink href=\"https://github.com/obsproject/obs-studio\" />)\u003C/li>\n\u003Cli>\u003Cstrong>Download:\u003C/strong> \u003CLink href=\"https://obsproject.com/\" />\u003C/li>\n\u003C/ul>\n\u003Cp>Streaming, recording, and live video composition all in one. Really really great at what it does.\u003C/p>\n\u003Ch2>imitone\u003C/h2>\n\u003Cul>\n\u003Cli>\u003Cstrong>Price:\u003C/strong> $30\u003C/li>\n\u003Cli>\u003Cstrong>OSS:\u003C/strong> No\u003C/li>\n\u003Cli>\u003Cstrong>Download:\u003C/strong> \u003CLink href=\"https://imitone.com/\" />\u003C/li>\n\u003C/ul>\n\u003Cp>Basically a voice-controlled theremin with MIDI support. Really great as a teaching tool if you’re not familiar with note scales. Worth the asking price if you want an easy and fun way to play with music.\u003C/p>\n\u003Ch1>Internet\u003C/h1>\n\u003Ch2>Waterfox\u003C/h2>\n\u003Cul>\n\u003Cli>\u003Cstrong>Price:\u003C/strong> Free\u003C/li>\n\u003Cli>\u003Cstrong>OSS:\u003C/strong> Yes (\u003CLink href=\"https://github.com/WaterfoxCo/Waterfox\" />)\u003C/li>\n\u003Cli>\u003Cstrong>Download:\u003C/strong> \u003CLink href=\"https://www.waterfox.net/\" />\u003C/li>\n\u003C/ul>\n\u003Cp>A well-supported Firefox fork with sane defaults and some extra features, such as Chrome extension support. Very clean UI, especially coming from Chrome. Still has Firefox Sync, for those who need iOS password sync.\u003C/p>\n\u003Ch2>Insomnia\u003C/h2>\n\u003Cul>\n\u003Cli>\u003Cstrong>Price:\u003C/strong> Free\u003C/li>\n\u003Cli>\u003Cstrong>OSS:\u003C/strong> Yes (\u003CLink href=\"https://github.com/Kong/insomnia\" />)\u003C/li>\n\u003Cli>\u003Cstrong>Download:\u003C/strong> \u003CLink href=\"https://insomnia.rest/\" />\u003C/li>\n\u003C/ul>\n\u003Cp>Swiss-army knife for debugging APIs, including REST and WebSockets. Makes it much easier to organize tests and try things quickly, as opposed to having a bunch of random JS/CURL snippets laying around.\u003C/p>\n\u003Ch2>Angry IP Scanner\u003C/h2>\n\u003Cul>\n\u003Cli>\u003Cstrong>Price:\u003C/strong> Free\u003C/li>\n\u003Cli>\u003Cstrong>OSS:\u003C/strong> Yes (\u003CLink href=\"https://github.com/angryip/ipscan\" />)\u003C/li>\n\u003Cli>\u003Cstrong>Download:\u003C/strong> \u003CLink href=\"https://angryip.org/\" />\u003C/li>\n\u003C/ul>\n\u003Cp>Scans for active IP addresses and ports on a given network, angrily.\u003C/p>\n\u003Ch1>System\u003C/h1>\n\u003Ch2>balenaEtcher\u003C/h2>\n\u003Cul>\n\u003Cli>\u003Cstrong>Price:\u003C/strong> Free, with pro version upgrade\u003C/li>\n\u003Cli>\u003Cstrong>OSS:\u003C/strong> Yes (\u003CLink href=\"https://github.com/balena-io/etcher\" />)\u003C/li>\n\u003Cli>\u003Cstrong>Download:\u003C/strong> \u003CLink href=\"https://www.balena.io/etcher/\" />\u003C/li>\n\u003C/ul>\n\u003Cp>Before you raise your pitchforks: yeah yeah, electron bad, yadda yadda. Is it more bloated than what it should be? Probably. At the same time, I’ve never had it flash something incorrectly, and it supports on-the-fly decompression. Werks for me, so I’m recommending it. If you just wanna roll with dd though, that’s fine too.\u003C/p>\n\u003Ch2>Parsec\u003C/h2>\n\u003Cul>\n\u003Cli>\u003Cstrong>Price:\u003C/strong> Free, with pro version subscription\u003C/li>\n\u003Cli>\u003Cstrong>OSS:\u003C/strong> No\u003C/li>\n\u003Cli>\u003Cstrong>Download:\u003C/strong> \u003CLink href=\"https://parsec.app/\" />\u003C/li>\n\u003C/ul>\n\u003Cp>My personal favorite remote desktop software. Their tagline “Forget you’re somewhere else” comes pretty close to reality on a good network connection. Pairs well with GPU passthrough.\u003C/p>\n\u003Cp>Streaming, recording, and live video composition all in one. Really really great at what it does.\u003C/p>\n\u003Ch1>Other\u003C/h1>\n\u003Ch2>Zotero\u003C/h2>\n\u003Cul>\n\u003Cli>\u003Cstrong>Price:\u003C/strong> Free\u003C/li>\n\u003Cli>\u003Cstrong>OSS:\u003C/strong> Yes, with centrally hosted services for accounts \u003CLink href=\"https://github.com/zotero\" />\u003C/li>\n\u003Cli>\u003Cstrong>Download:\u003C/strong> \u003CLink href=\"https://www.zotero.org/\" />\u003C/li>\n\u003C/ul>\n\u003Cp>Need to cite something? Just use this. Makes the generation of citations and bibliographies super easy. Comes with integrations for browser and Word to get stuff from the web into your document quickly.\u003C/p>\n",{"title":24,"date":25,"categories":26,"last_modified_at":27,"comments":28},"The Software Dump Part 2: Multiplatform",["Date","2023-04-11T00:00:00.000Z"],"software life multiplatform",["Date","2023-04-11T00:00:00.000Z"],true,{"slug":30,"path":31,"code":32,"fm":33},"portfolio","/articles/portfolio","\u003Cscript context=\"module\">\n\texport const metadata = {\"title\":\"The Portfolio Post\",\"date\":\"2023-04-10T00:00:00.000Z\",\"last_modified_at\":\"2023-04-12T00:00:00.000Z\",\"categories\":\"life work portfolio\",\"pinned\":true,\"bgText\":\"portfolio\"};\n\tconst { title, date, last_modified_at, categories, pinned, bgText } = metadata;\n\u003C/script>\n\u003Cscript lang=\"ts\">\n import Image from \"$lib/Image.svelte\";\n import Figure from \"$lib/Figure.svelte\";\n\n import imgVRC from \"./IMG_AA04362AF84B-1.jpeg?as=run\";\n import imgWhiteface from \"./SCR-20230412-r9e.png?as=run\";\n import imgLCATDB from \"./localhost_3000_login.html.png?as=run\";\n\u003C/script>\n\n\u003Cp>Welcome to the site! If you’re on this post, you’re probably wondering who I am and what I do! I’ve recently graduated from Clarkson University with a Master’s in Computer Science. However, I’d like to use this post to show what I am outside of just this degree!\u003C/p>\n\u003Chr>\n\u003Ch1>Professional Projects\u003C/h1>\n\u003CFigure align=\"right\">\n \u003CImage src={imgVRC} />\n\u003C/Figure>\n\u003Ch2>Clarkson University Virtual Reality Coaster\u003C/h2>\n\u003Cp>In February of 2021, I began a refresh of Clarkson University’s MaxFlight VR2004 motion simulator, dubbed the Virtual Reality Coaster (VRC)! This machine was purchased from a local arcade sometime in the mid-2000s, and it shows. The software is quite dated, and so I’ve produced several resources to bring it up towards modern standards:\u003C/p>\n\u003Cul>\n\u003Cli>\n\u003Cp>\u003Cstrong>pyMaxFlight:\u003C/strong> Exposes all capabilities of the MaxFlight Motion Client programmatically through Python on a the Control PC. This module only allows for local operation. MaxFlight did not provide their own public API for interfacing with the simulator, so this module works by directly accessing the Motion Client window using the Win32 API, simulating button presses and slider movements. (\u003Ca\n href=\"https://github.com/Clarkson-IMPETUS/pyMaxFlight\"\n rel=\"nofollow\"\n>GitHub\u003C/a>, \u003Ca\n href=\"https://pymaxflight.readthedocs.io/en/latest/src/pyMaxFlight/\"\n rel=\"nofollow\"\n>Docs\u003C/a>)\u003C/p>\n\u003C/li>\n\u003Cli>\n\u003Cp>\u003Cstrong>pyWSConsole:\u003C/strong> Provides network communication via WebSockets, with a focus on stability and persistent connections. Uses a console-like interface and provides automatic help documentation. (\u003Ca\n href=\"https://github.com/heyjoeway/pyWSConsole\"\n rel=\"nofollow\"\n>GitHub\u003C/a>)\u003C/p>\n\u003C/li>\n\u003Cli>\n\u003Cp>\u003Cstrong>VRC-Apps:\u003C/strong> A collection of Python applications to extend the functionality of the VRC using the aforementioned libraries. (\u003Ca\n href=\"https://github.com/Clarkson-IMPETUS/VRC-Apps\"\n rel=\"nofollow\"\n>GitHub\u003C/a>)\u003C/p>\n\u003C/li>\n\u003C/ul>\n\u003CFigure align=\"right\">\n \u003CImage src={imgWhiteface} maxHeight=\"270px\" />\n\u003C/Figure>\n\u003Ch2>Whiteface Tour\u003C/h2>\n\u003Cp>\u003Cstrong>Link:\u003C/strong> \u003Ca\n href=\"https://jojudge.com/whitefacetour\"\n rel=\"nofollow\"\n>https://jojudge.com/whitefacetour\u003C/a>\u003C/p>\n\u003Cp>In the summer of 2018, I had the opportunity to work as an intern at the Whiteface Mountain Atmospheric Sciences Research Center (ASRC). During my time there, I worked on several projects related to outreach. The most substantial of these is the Whiteface Tour: a 360-degree virtual tour of the mountain summit. This uses the library three.js for rendering, and a custom JSON-based system for scene management. This does not use a pre-existing game engine. All resources are packaged using webpack, deployed through GitHub Actions, and hosted using GitHub Pages.\u003C/p>\n\u003Cbr>\n\u003CFigure align=\"right\">\n \u003CImage src={imgLCATDB} maxHeight=\"330px\" />\n\u003C/Figure>\n\u003Ch2>lcatDB (Lake Champlain Anglers’ Temperature Database)\u003C/h2>\n\u003Cp>lcatDB (Lake Champlain Anglers’ Temperature Database) was an online database and single page application (SPA) meant to provide a centralized means of recording, accessing, and analyzing vertical water temperature profiles to citizens of the Lake Champlain region. It was supported by the SUNY Plattsburgh Center for Earth & Environmental Science and the Lake Champlain Sea Grant. lcatDB was (what I consider to be) my first attempt at full stack software development. It primarily uses Bootstrap, jQuery, and Grunt for the frontend, while the backend was built using Node.JS, Express, and MongoDB. \u003Ca\n href=\"/articles/lcatdb\"\n>Read the full retrospective here.\u003C/a>\u003C/p>\n\u003Ch1>Personal Projects\u003C/h1>\n\u003Ch2>Switch Ports\u003C/h2>\n\u003Cp>\u003Cstrong>Page:\u003C/strong> \u003Ca href=\"/articles/switch-ports\">Switch Ports\u003C/a>\u003C/p>\n\u003Cp>The Nintendo Switch has a thriving homebrew scene that I took part in between 2019 and 2022. I developed ports of several games and maintained them through the 3 years that I was active. These ports were generally maintained as forks so as to not pollute the upstream repositories with platform specific material. This required working with upstream maintainers to merge in changes to these fork and produce installable packages, which was eventually automated using GitHub Actions.\u003C/p>\n\u003Ch2>ICBINS1\u003C/h2>\n\u003Cp>\u003Cstrong>Link:\u003C/strong> \u003Ca\n href=\"http://jojudge.com/ICBINS1/\"\n rel=\"nofollow\"\n>http://jojudge.com/ICBINS1/\u003C/a>\u003C/p>\n\u003Cp>As a test to learn the ins-and-outs of Unity, I tried recreating the original Sonic the Hedgehog from observation! This was a great learning experience and taught me a lot about the game engine and C#. I later moved away from this project due to it being superseded in its goal by the decompilation of the 2013 remake, the exploration of other approaches, and shifting priorities in my personal life.\u003C/p>\n\u003Ch2>S2CX/Genesis Plus GX Wide\u003C/h2>\n\u003Cp>\u003Cstrong>News Article:\u003C/strong> \u003Ca\n href=\"https://www.libretro.com/index.php/genesis-plus-gx-wide-now-available-for-libretroretroarch/\"\n rel=\"nofollow\"\n>https://www.libretro.com/index.php/genesis-plus-gx-wide-now-available-for-libretroretroarch/\u003C/a>\u003C/p>\n\u003Cp>Another project launched before the 2013 remake decompilations that took the Genesis version of Sonic 2 and added widescreen capabilities. When first developed, my modification of Genesis Plus GX was only meant to add widescreen to that one game. After people began experimenting with other games, it was found that this approach worked for many other games, and so the project now lives on as an upstream RetroArch core (you can find it in the core downloader)! I no longer maintain this fork, but it has been picked up by other passionate community members!\u003C/p>\n\u003Ch1>Other Stuff\u003C/h1>\n\u003Cp>Here’s a miscellaneous list of other things I written/done you should check out!\u003C/p>\n\u003Cul>\n\u003Cli>GitHub: \u003Ca href=\"https://github.com/heyjoeway\" rel=\"nofollow\">heyjoeway\u003C/a>\u003C/li>\n\u003Cli>\u003Ca href=\"/articles/responsive-design\">Responsive Design Showcase\u003C/a>\u003C/li>\n\u003C/ul>\n\u003Ch1>Other Hobbies\u003C/h1>\n\u003Cp>Believe it or not, I do get sick of programming! When I do, I like dipping my toes into other domains to pick up new skills! Some of my “side gigs” include:\u003C/p>\n\u003Ch2>Photography\u003C/h2>\n\u003Cp>Check out my \u003Ca href=\"/articles/photo-megapost\">photography megapost\u003C/a>!\u003C/p>\n\u003Ch2>Music\u003C/h2>\n\u003Cp>Since 2022 I’ve been dabbling in mashups using Logic Pro X and AI tools such as demucs! You can find tracks on my \u003Ca\n href=\"https://soundcloud.com/heyjoeway\"\n rel=\"nofollow\"\n>SoundCloud\u003C/a>. Be warned that my taste is… inexplicable? I often pull tracks from my mid-2000s childhood for material. Some of main inspirations include Neil Cicierega, Triple-Q, Galactic Hole, and SiivaGunner. I’m hoping to produce some original pieces in the future!\u003C/p>\n\u003Ch2>Graphic Design\u003C/h2>\n\u003Cp>You can see my graphic design skills reflected in many of my projects, as I found it to be necessary to pair with programming for many projects. I tend to follow more minimalist standards such as Material Design and Metro while retaining some personalized flair.\u003C/p>\n\u003Ch2>3D Modeling\u003C/h2>\n\u003Cp>I’m familiar with the basics of modeling in Blender and SketchUp and can produce simple models/renders for both graphic design and CAD. (Some examples: \u003Ca\n href=\"/articles/gd-anime-was-a-mistake\"\n>“Anime Was a Mistake”\u003C/a>, \u003Ca href=\"/articles/gd-mayolo\">“Mayolo”\u003C/a>) I have 2 3D printers (one SLA, one FDM) and experiment with them from time to time to produce both figurines and more utilitarian parts.s\u003C/p>\n\u003Chr>\n\u003Ch1>Philosophy\u003C/h1>\n\u003Cp>I’ve spent a lot of time programming in a lot of different languages, working with many different technologies, and aiming for several different goals. It’s easy to get lost in programming as an art in-and-of-itself, but as a goal-oriented individual, I see programming as a tool I use to accomplish said goals. That being said, I personally recognize it as one of the most important tools in modern society, and thus I have spent a lot of time refining it as a craft.\u003C/p>\n\u003Cp>I always consider the long-term viability of projects and weigh all possible approaches. There are many great ideas for products, but without a efficient plan, many of them lie on the table or the cutting-room floor. I often look to automation to avoid these scenarios, even for tasks in my personal life. I always consider all available options and tools: sometimes the answer is to deploy pre-existing services or generally change my workflow. Very often, however, an idea will come along that is too specific to execute with anything other than my own code.\u003C/p>\n\u003Cblockquote>\n\u003Cp>\u003Cstrong>Side note:\u003C/strong> As AI becomes more adept, I am constantly searching for ways to use it in optimizing my workflow. However, I am more interested in using AI tools than developing them, as I do not currently have a great interest in AI training and data science. I am experimenting with tools such as OpenAI Playground and GitHub Copilot and, while not perfect, I am very impressed with their results and have begun using them to increase my own efficiency.\u003C/p>\n\u003C/blockquote>\n\u003Cp>When writing my code, I make sure that it is readable, modular, and generally reusable. Sometimes this may be as simple as providing reasoning through comments. Other times it may be more complex, such as producing documentation (generally using a pre-existing documentation system like mkdocs), segmenting code into more readable chunks, or even developing libraries to use in multiple projects. I prioritize compatibility with IntelliSense, which makes any codebase significantly more navigable. This is generally accomplished by following documentation standards, such as type annotations (ala Python 3) or JSDoc.\u003C/p>\n\u003Cp>My goal is to write software and produce products that will stand the test of time, that I’ll be able to look on years down the line and be proud of.\u003C/p>\n\n",{"title":34,"date":35,"last_modified_at":36,"categories":37,"pinned":28,"bgText":30},"The Portfolio Post",["Date","2023-04-10T00:00:00.000Z"],["Date","2023-04-12T00:00:00.000Z"],"life work portfolio",{"slug":39,"path":40,"code":41,"fm":42},"mac-hidden-files","/articles/mac-hidden-files","\u003Cscript context=\"module\">\n\texport const metadata = {\"title\":\"Toggling Hidden Files in macOS except it's actually good\",\"date\":\"2023-02-14T00:00:00.000Z\",\"categories\":\"software macos\",\"comments\":true};\n\tconst { title, date, categories, comments } = metadata;\n\u003C/script>\n\u003Cscript lang=\"ts\">\n import Image from \"$lib/Image.svelte\";\n import Figure from \"$lib/Figure.svelte\";\n\n import imgScreenshot1 from \"./SCR-20230214-ext.png?as=run:0\";\n import imgScreenshot2 from \"./SCR-20230214-f3i.png?as=run:0\";\n\u003C/script>\n\n\u003Cp>Happy Valentines Day. I’ve been trying to migrate from Forklift back to Finder since I’m not super happy with their progress and the issues that come with a non-native file manager, including but not limited to:\u003C/p>\n\u003Cul>\n\u003Cli>Getting nagged every boot\u003C/li>\n\u003Cli>Favorites not syncing when using a file picker\u003C/li>\n\u003Cli>Mediocre UI\u003C/li>\n\u003Cli>Remote integration not carrying over to other apps\u003C/li>\n\u003Cli>Needing to have two file managers in my dock at all times\u003C/li>\n\u003Cli>Window resizing being poor\u003C/li>\n\u003Cli>No support for per-folder view options (I think?)\u003C/li>\n\u003C/ul>\n\u003Cp>One thing I did miss was having a toolbar button for showing hidden files.\u003C/p>\n\u003Ch1>The Keyboard Shortcut\u003C/h1>\n\u003Cp>\u003Ccode>CMD + SHIFT + .\u003C/code>\u003C/p>\n\u003Cp>I’m going to be honest with you, I use this so infrequently that I don’t think there’s a chance in hell I’ll go 6 months without Googling this a couple times. And it’s different on Every. Single. OS. \u003Ccode>Alt + .\u003C/code>, \u003Ccode>Ctrl + H\u003C/code>, hell, why not \u003Ccode>Ctrl + Shift + Alt + Super + F12 + 2\u003C/code> next?\u003C/p>\n\u003Ch1>The “Automatic” Method\u003C/h1>\n\u003Cp>Does having all your Finder windows killed every time you want to toggle hidden files sound fun? No? Well apparently Stack Overflow thinks so because it’s the only other suggestion I’ve seen. I’m not even going to bother reposting it here because I think it’s that fucking awful. That is not a solution.\u003C/p>\n\u003Ch1>Why Did They Name The App “Shortcuts”\u003C/h1>\n\u003Cp>Oh yeah, Apple added that “Shortcuts” app huh? Too bad nobody really seems to care or talk about it, because it’s actually pretty decent. It’s also really hard to find anything relating to it online because the name is absolutely horrible.\u003C/p>\n\u003Cblockquote>\n\u003Cp>Did you mean?: Keyboard shortcuts\u003C/p>\n\u003C/blockquote>\n\u003Cp>Unfortunately, it ALSO has no ability to toggle hidden files. Or does it? We DO have the ability to run AppleScript, and combining a little bit of every approach, we get the following:\u003C/p>\n\u003Cpre class=\"language-applescript\">{@html `\u003Ccode class=\"language-applescript\">\u003Cspan class=\"token keyword\">on\u003C/span> run \u003Cspan class=\"token punctuation\">{\u003C/span>input\u003Cspan class=\"token punctuation\">,\u003C/span> parameters\u003Cspan class=\"token punctuation\">}\u003C/span>\n\tactivate \u003Cspan class=\"token class-name\">application\u003C/span> \u003Cspan class=\"token string\">\"Finder\"\u003C/span>\n\t\u003Cspan class=\"token keyword\">tell\u003C/span> \u003Cspan class=\"token class-name\">application\u003C/span> \u003Cspan class=\"token string\">\"System Events\"\u003C/span> \u003Cspan class=\"token keyword\">to\u003C/span> keystroke \u003Cspan class=\"token string\">\".\"\u003C/span> using \u003Cspan class=\"token punctuation\">{\u003C/span>command down\u003Cspan class=\"token punctuation\">,\u003C/span> shift down\u003Cspan class=\"token punctuation\">}\u003C/span>\n\t\u003Cspan class=\"token keyword\">return\u003C/span> input\n\u003Cspan class=\"token keyword\">end\u003C/span> run\u003C/code>`}\u003C/pre>\n\u003CFigure>\n \u003CImage src={imgScreenshot1} />\n\u003C/Figure>\n\u003Cp>You’ll need to give both Shortcuts and \u003Ccode>siriactionsd\u003C/code> Accessibility permissions. I think there might also be something you need to enable to use AppleScript. \u003Ca\n href=\"https://www.icloud.com/shortcuts/2d73e23025fd4a0e8f78c55600e92a04\"\n rel=\"nofollow\"\n>Here’s a pre-made shortcut if you’d like.\u003C/a>\u003C/p>\n\u003Ch1>“But you said you wanted a toolbar button!”\u003C/h1>\n\u003Cp>Here’s a secret: you can turn any shortcut into a \u003Ccode>.app\u003C/code> by right clicking on it and selecting “Add to Dock”. You’ll likely want to remove it from the dock after, and you’ll be able to find it in \u003Ccode>~/Applications\u003C/code>. I moved mine into a subfolder I named “Finder Toolbar”.\u003C/p>\n\u003Cp>Here’s ANOTHER secret: you can add any \u003Ccode>.app\u003C/code> to your Finder toolbar. Simply enter customization mode and drag the app in.\u003C/p>\n\u003CFigure>\n \u003CImage src={imgScreenshot2} />\n\u003C/Figure>\n\u003Cp>Apple sure does love to include useful features and never explain them, huh?\u003C/p>\n\n",{"title":43,"date":44,"categories":45,"comments":28},"Toggling Hidden Files in macOS except it's actually good",["Date","2023-02-14T00:00:00.000Z"],"software macos",{"slug":47,"path":48,"code":49,"fm":50},"software-dump-mac","/articles/software-dump-mac","\u003Cscript context=\"module\">\n\texport const metadata = {\"title\":\"The Software Dump Part 1: macOS\",\"date\":\"2022-12-28T00:00:00.000Z\",\"categories\":\"software life macos\",\"last_modified_at\":\"2023-04-13T00:00:00.000Z\",\"comments\":true};\n\tconst { title, date, categories, last_modified_at, comments } = metadata;\n\u003C/script>\n\u003Cscript lang=\"ts\">\n import Link from \"$lib/Link.svelte\";\n\u003C/script>\n\n\n\u003Cp>You know those shitty tabloid articles you find on Google when you try to find software recommendations, all of them having the same 10 programs over and over? Yeah, this isn’t another one of those. These are all programs I’ve spent a good deal of time with and will be giving genuine opinions on. Let’s get to it.\u003C/p>\n\u003Ch1>Window Management\u003C/h1>\n\u003Cp>For all the polish macOS has, its window management has been complete and utter \u003Cem>shit\u003C/em> for its entire life. These make it a little bit better.\u003C/p>\n\u003Ch2>Rectangle\u003C/h2>\n\u003Cul>\n\u003Cli>\u003Cstrong>Price:\u003C/strong> Free, with pro version upgrade\u003C/li>\n\u003Cli>\u003Cstrong>OSS:\u003C/strong> Yes, except pro (\u003CLink href=\"https://github.com/rxhanson/Rectangle\" />)\u003C/li>\n\u003Cli>\u003Cstrong>Download:\u003C/strong> \u003CLink href=\"https://rectangleapp.com/\" />\u003C/li>\n\u003C/ul>\n\u003Cp>Aero Snap for macOS, plus some extra snapping areas. That’s about it to me. It has some other features that I’ve never really messed with or cared about. Just install it already.\u003C/p>\n\u003Ch2>Swish\u003C/h2>\n\u003Cul>\n\u003Cli>\u003Cstrong>Price:\u003C/strong> $16, trial available\u003C/li>\n\u003Cli>\u003Cstrong>OSS:\u003C/strong> No\u003C/li>\n\u003Cli>\u003Cstrong>Download:\u003C/strong> \u003CLink href=\"https://highlyopinionated.co/swish/\" />\u003C/li>\n\u003C/ul>\n\u003Cp>Lets you move/tile windows using trackpad gestures. Honestly so vital to window management when not docked that I wish Apple would buy them up and include it by default. Absolutely worth the asking price.\u003C/p>\n\u003Ch1>Filesystem\u003C/h1>\n\u003Ch2>Disk Inventory X\u003C/h2>\n\u003Cul>\n\u003Cli>\u003Cstrong>Price:\u003C/strong> Free\u003C/li>\n\u003Cli>\u003Cstrong>OSS:\u003C/strong> Yes (\u003CLink href=\"https://gitlab.com/tderlien/disk-inventory-x\" />)\u003C/li>\n\u003Cli>\u003Cstrong>Download:\u003C/strong> \u003CLink href=\"https://www.derlien.com/\" />\u003C/li>\n\u003C/ul>\n\u003Cp>Ever used WinDirStat? It’s that. Basically, let’s you locate what’s taking up disk space more easily.\u003C/p>\n\u003Ch2>ForkLift\u003C/h2>\n\u003Cul>\n\u003Cli>\u003Cstrong>Price:\u003C/strong> Free, with pro version upgrade\u003C/li>\n\u003Cli>\u003Cstrong>OSS:\u003C/strong> No\u003C/li>\n\u003Cli>\u003Cstrong>Download:\u003C/strong> \u003CLink href=\"https://binarynights.com/\" />\u003C/li>\n\u003C/ul>\n\u003Cp>Better file manager. Dual panes, remote connections (including SFTP, FTP, GDrive, etc.), better view modes, “auto clean-up” (aka just listing files like a normal file manager does, yes I am still mad at Finder for this) and some other smaller features. The free version is supposedly a trial but I think it’s just nagware like WinRAR. I have this as my default file manager.\u003C/p>\n\u003Ch2>OpenMTP\u003C/h2>\n\u003Cul>\n\u003Cli>\u003Cstrong>Price:\u003C/strong> Free\u003C/li>\n\u003Cli>\u003Cstrong>OSS:\u003C/strong> Yes (\u003CLink href=\"https://github.com/ganeshrvel/openmtp\" />)\u003C/li>\n\u003Cli>\u003Cstrong>Download:\u003C/strong> \u003CLink href=\"https://openmtp.ganeshrvel.com/\" />\u003C/li>\n\u003C/ul>\n\u003Cp>Lets you access MTP devices (Android) under macOS. I sure do love meaningless segmentation of stuff that doesn’t need to be segmented.\u003C/p>\n\u003Ch1>Internet\u003C/h1>\n\u003Ch2>Canary Mail\u003C/h2>\n\u003Cul>\n\u003Cli>\u003Cstrong>Price:\u003C/strong> Free, with pro version upgrade\u003C/li>\n\u003Cli>\u003Cstrong>OSS:\u003C/strong> No\u003C/li>\n\u003Cli>\u003Cstrong>Download:\u003C/strong> \u003CLink href=\"https://canarymail.io/\" />\u003C/li>\n\u003C/ul>\n\u003Cp>The built-mail app with a few more features and a bit more customization. Honestly, if the default mail app does what you want it to, just stick with it.\u003C/p>\n\u003Ch2>NetNewsWire\u003C/h2>\n\u003Cul>\n\u003Cli>\u003Cstrong>Price:\u003C/strong> Free\u003C/li>\n\u003Cli>\u003Cstrong>OSS:\u003C/strong> Yes (\u003CLink href=\"https://github.com/Ranchero-Software/NetNewsWire\" />)\u003C/li>\n\u003Cli>\u003Cstrong>Download:\u003C/strong> \u003CLink href=\"https://netnewswire.com/\" />\u003C/li>\n\u003C/ul>\n\u003Cp>Native RSS reader.\u003C/p>\n\u003Ch1>Media\u003C/h1>\n\u003Ch2>IINA\u003C/h2>\n\u003Cul>\n\u003Cli>\u003Cstrong>Price:\u003C/strong> Free\u003C/li>\n\u003Cli>\u003Cstrong>OSS:\u003C/strong> Yes (\u003CLink href=\"https://github.com/iina/iina\" />)\u003C/li>\n\u003Cli>\u003Cstrong>Download:\u003C/strong> \u003CLink href=\"https://iina.io/\" />\u003C/li>\n\u003C/ul>\n\u003Cp>A media player that gets out of the way of the content. Much better than QuickTime or even VLC.\u003C/p>\n\u003Ch2>Logic Pro\u003C/h2>\n\u003Cul>\n\u003Cli>\u003Cstrong>Price:\u003C/strong> $200\u003C/li>\n\u003Cli>\u003Cstrong>OSS:\u003C/strong> …no.\u003C/li>\n\u003Cli>\u003Cstrong>Download:\u003C/strong> \u003CLink href=\"https://www.apple.com/logic-pro/\" />\u003C/li>\n\u003C/ul>\n\u003Cp>The Mac DAW. After trying other DAWs (mostly FL Studio) this one just clicked. The UI is great in the realm of audio work and gets shit done. Now I will plug my SoundCloud because I can use it as an example of how I’ve been using it: \u003CLink href=\"https://soundcloud.com/heyjoeway\" />\u003C/p>\n\u003Cp>If you’re not ready to pay the price or sail the seas, give GarageBand a shot. It’s basically the trial version of Logic and has a very similar UI.\u003C/p>\n\u003Ch1>Images\u003C/h1>\n\u003Ch2>Pixea\u003C/h2>\n\u003Cul>\n\u003Cli>\u003Cstrong>Price:\u003C/strong> Free, with pro version upgrade\u003C/li>\n\u003Cli>\u003Cstrong>OSS:\u003C/strong> No\u003C/li>\n\u003Cli>\u003Cstrong>Download:\u003C/strong> \u003CLink href=\"https://www.imagetasks.com/pixea/\" />\u003C/li>\n\u003C/ul>\n\u003Cp>Nice image viewer with an “invisible” header bar. Really nice for quickly previewing images. Preview is still king for PDFs and image conversion though. Pairs very nicely with IINA.\u003C/p>\n\u003Ch2>ColorSlurp\u003C/h2>\n\u003Cul>\n\u003Cli>\u003Cstrong>Price:\u003C/strong> Free, with pro version upgrade\u003C/li>\n\u003Cli>\u003Cstrong>OSS:\u003C/strong> No\u003C/li>\n\u003Cli>\u003Cstrong>Download:\u003C/strong> \u003CLink href=\"https://colorslurp.com/\" />\u003C/li>\n\u003C/ul>\n\u003Cp>Quick universal color picker.\u003C/p>\n\u003Ch2>Shottr\u003C/h2>\n\u003Cul>\n\u003Cli>\u003Cstrong>Price:\u003C/strong> Free\u003C/li>\n\u003Cli>\u003Cstrong>OSS:\u003C/strong> No (why?)\u003C/li>\n\u003Cli>\u003Cstrong>Download:\u003C/strong> \u003CLink href=\"https://shottr.cc/\" />\u003C/li>\n\u003C/ul>\n\u003Cp>Screenshot tool that doesn’t suck ass!\u003C/p>\n\u003Ch1>System\u003C/h1>\n\u003Ch2>Keka\u003C/h2>\n\u003Cul>\n\u003Cli>\u003Cstrong>Price:\u003C/strong> Free\u003C/li>\n\u003Cli>\u003Cstrong>OSS:\u003C/strong> Yes (\u003CLink href=\"https://github.com/aonez/Keka\" />)\u003C/li>\n\u003Cli>\u003Cstrong>Download:\u003C/strong> \u003CLink href=\"https://www.keka.io/ \" />\u003C/li>\n\u003C/ul>\n\u003Cp>A swiss army archival tool.\u003C/p>\n\u003Ch2>iTerm2\u003C/h2>\n\u003Cul>\n\u003Cli>\u003Cstrong>Price:\u003C/strong> Free\u003C/li>\n\u003Cli>\u003Cstrong>OSS:\u003C/strong> Yes (\u003CLink href=\"https://github.com/gnachman/iTerm2\" />)\u003C/li>\n\u003Cli>\u003Cstrong>Download:\u003C/strong> \u003CLink href=\"https://iterm2.com/\" />\u003C/li>\n\u003C/ul>\n\u003Cp>A better terminal. More customization, more features.\u003C/p>\n\u003Ch2>UTM\u003C/h2>\n\u003Cul>\n\u003Cli>\u003Cstrong>Price:\u003C/strong> Free\u003C/li>\n\u003Cli>\u003Cstrong>OSS:\u003C/strong> Yes (\u003CLink href=\"https://github.com/utmapp/UTM\" />)\u003C/li>\n\u003Cli>\u003Cstrong>Download:\u003C/strong> \u003CLink href=\"https://mac.getutm.app/\" />\u003C/li>\n\u003C/ul>\n\u003Cp>Virtualization/emulation of other computers. This is one way to run Windows ARM on macOS. I only need to use this every once in a while and it gets the job done. If you’re looking for something a little more advanced, try Parallels (paid).\u003C/p>\n\u003Ch2>KeyboardCleanTool\u003C/h2>\n\u003Cul>\n\u003Cli>\u003Cstrong>Price:\u003C/strong> Free\u003C/li>\n\u003Cli>\u003Cstrong>OSS:\u003C/strong> No (…why?)\u003C/li>\n\u003Cli>\u003Cstrong>Download:\u003C/strong> \u003CLink href=\"https://folivora.ai/keyboardcleantool\" />\u003C/li>\n\u003C/ul>\n\u003Cp>Temporarily disables the keyboard to let you clean it. It sounds stupid, but you’ll end up using it every once in a while anyway, trust me.\u003C/p>\n\u003Ch2>Karabiner-Elements\u003C/h2>\n\u003Cul>\n\u003Cli>\u003Cstrong>Price:\u003C/strong> Free\u003C/li>\n\u003Cli>\u003Cstrong>OSS:\u003C/strong> Yes (\u003CLink href=\"https://github.com/pqrs-org/Karabiner-Elements\" />)\u003C/li>\n\u003Cli>\u003Cstrong>Download:\u003C/strong> \u003CLink href=\"https://karabiner-elements.pqrs.org/\" />\u003C/li>\n\u003C/ul>\n\u003Cp>General purpose key-rebinding tool. Can rebind a lot of things that the built-in settings can’t. Perfect for muscle memory diehards, especially if coming from another platform. Here’s my config:\u003C/p>\n\u003Cul>\n\u003Cli>Simple Modifications\n\u003Cul>\n\u003Cli>All Devices\n\u003Cul>\n\u003Cli>caps_lock -> mission_control\u003C/li>\n\u003Cli>right_shift -> left_shift\n\u003Cul>\n\u003Cli>\u003Cstrong>NOTE:\u003C/strong> Fixes AnyDesk bug.\u003C/li>\n\u003C/ul>\n\u003C/li>\n\u003C/ul>\n\u003C/li>\n\u003Cli>Internal Keyboard\n\u003Cul>\n\u003Cli>fn -> left_command\u003C/li>\n\u003Cli>left_command -> left_option\u003C/li>\n\u003Cli>left_control -> fn\u003C/li>\n\u003Cli>left_option -> left_control\n\u003Cul>\n\u003Cli>\u003Cstrong>NOTE:\u003C/strong> On a PC keyboard, this layout would look like this: Ctrl, Fn, Win, Alt. I have custom key labels printed to make this less confusing.\u003C/li>\n\u003C/ul>\n\u003C/li>\n\u003C/ul>\n\u003C/li>\n\u003Cli>External Keyboard (PC)\n\u003Cul>\n\u003Cli>left_command -> left_control\u003C/li>\n\u003Cli>left_control -> left_command\u003C/li>\n\u003C/ul>\n\u003C/li>\n\u003C/ul>\n\u003C/li>\n\u003Cli>Function Keys\n\u003Cul>\n\u003Cli>Internal Keyboard\n\u003Cul>\n\u003Cli>f1 -> display_brightness_decrement\u003C/li>\n\u003Cli>f2 -> display_brightness_increment\u003C/li>\n\u003Cli>f3 -> print_screen\u003C/li>\n\u003Cli>f4 -> mute\u003C/li>\n\u003Cli>f5 -> volume_decrement\u003C/li>\n\u003Cli>f6 -> volume_increment\u003C/li>\n\u003Cli>f7 -> rewind\u003C/li>\n\u003Cli>f8 -> play_or_pause\u003C/li>\n\u003Cli>f9 -> fast_forward\u003C/li>\n\u003Cli>f10 -> home\u003C/li>\n\u003Cli>f11 -> end\u003C/li>\n\u003Cli>f12 -> delete_forward\n\u003Cul>\n\u003Cli>\u003Cstrong>NOTE:\u003C/strong> Those last 3 keys are the reason everything else got moved around.\u003C/li>\n\u003C/ul>\n\u003C/li>\n\u003C/ul>\n\u003C/li>\n\u003C/ul>\n\u003C/li>\n\u003Cli>Complex Modifications\n\u003Cul>\n\u003Cli>AnyDesk Left CTRL -> CMD\n\u003Cul>\n\u003Cli>\u003Cstrong>NOTE:\u003C/strong> Many remote desktop tools will try to swap left command and left control on their own to match the PC layout. Since we’re already doing this, we need to swap AGAIN to counter AnyDesk. Seriously, please add an option to disable this. This is stupid.\u003C/li>\n\u003C/ul>\n\u003C/li>\n\u003Cli>Spotlight on CMD\n\u003Cul>\n\u003Cli>\u003Cstrong>NOTE:\u003C/strong> Kinda simulates searching in the Start Menu with the Windows key.\u003C/li>\n\u003C/ul>\n\u003C/li>\n\u003C/ul>\n\u003C/li>\n\u003C/ul>\n\u003Cp>The complex modifications are custom. You can find them in my repo: \u003CLink href=\"https://github.com/heyjoeway/KE-complex_modifications\" />\u003C/p>\n\u003Ch2>LinearMouse\u003C/h2>\n\u003Cul>\n\u003Cli>\u003Cstrong>Price:\u003C/strong> Free\u003C/li>\n\u003Cli>\u003Cstrong>OSS:\u003C/strong> Yes (\u003CLink href=\"https://github.com/linearmouse/linearmouse\" />)\u003C/li>\n\u003Cli>\u003Cstrong>Download:\u003C/strong> \u003CLink href=\"https://linearmouse.app/\" />\u003C/li>\n\u003C/ul>\n\u003Cp>Lets you fine-tune individual pointing devices (yes, you can change the touchpad and mouse independently). I use this for reverse scrolling on mice and speed/acceleration tweaks.\u003C/p>\n\u003Ch2>Hidden Bar\u003C/h2>\n\u003Cul>\n\u003Cli>\u003Cstrong>Price:\u003C/strong> Free\u003C/li>\n\u003Cli>\u003Cstrong>OSS:\u003C/strong> Yes (\u003CLink href=\"https://github.com/dwarvesf/hidden\" />)\u003C/li>\n\u003Cli>\u003Cstrong>Download:\u003C/strong> \u003CLink href=\"https://itunes.apple.com/app/hidden-bar/id1452453066\" />\u003C/li>\n\u003C/ul>\n\u003Cp>Hides system tray icons. Why the HELL is this not built in? Windows has had it since, what, XP?\u003C/p>\n\u003Ch2>BetterDisplay (formerly BetterDummy)\u003C/h2>\n\u003Cul>\n\u003Cli>\u003Cstrong>Price:\u003C/strong> Free, with pro version upgrade\u003C/li>\n\u003Cli>\u003Cstrong>OSS:\u003C/strong> No (previously was, grrrr)\u003C/li>\n\u003Cli>\u003Cstrong>Download:\u003C/strong> \u003CLink href=\"https://github.com/waydabber/BetterDisplay\" />\u003C/li>\n\u003C/ul>\n\u003Cp>Lets you create fake displays and enable HiDPI on unsupported screens. Essential for people with 1440p+ monitors. Again, more functionality that should be there by default and just… isn’t.\u003C/p>\n\u003Ch2>MonitorControl\u003C/h2>\n\u003Cul>\n\u003Cli>\u003Cstrong>Price:\u003C/strong> Free, with pro version upgrade\u003C/li>\n\u003Cli>\u003Cstrong>OSS:\u003C/strong> Yes\u003C/li>\n\u003Cli>\u003Cstrong>Download:\u003C/strong> \u003CLink href=\"https://github.com/MonitorControl/MonitorControl\" />\u003C/li>\n\u003C/ul>\n\u003Cp>Lets you control the brightness of external displays from your Mac. No, I don’t mean applying a dimming filter over the image (though it can do that too on unsupported displays), I mean \u003Cstrong>actually changing the settings of the monitor directly to lower the brightness.\u003C/strong> I’m going to be totally honest, I didn’t even know this was possible until I found this app. \u003Ca\n href=\"https://en.wikipedia.org/wiki/Display_Data_Channel#DDC/CI\"\n rel=\"nofollow\"\n>Apparently it’s been a standard since 1998\u003C/a> and to my knowledge not a single OS has included support for it out of the box. What the fuck? WHY?\u003C/p>\n\u003Ch1>Other Notes\u003C/h1>\n\u003Cp>\u003Cdel>You might notice I don’t have a screenshot utility. I’d recommend Monosnap if it wasn’t buggy as shit, though that may have changed since I last used it. Right now, I’m just using some Shortcuts (as in the Shortcuts app ala Monterey):\u003C/del>\u003C/p>\n\u003Cul>\n\u003Cli>\u003Cdel>\u003Ca\n href=\"https://www.icloud.com/shortcuts/1c3f40fb719f4b6f9ab8564f429778e9\"\n rel=\"nofollow\"\n>Selection Screenshot\u003C/a>\u003C/del>\u003C/li>\n\u003Cli>\u003Cdel>\u003Ca\n href=\"https://www.icloud.com/shortcuts/0958cf10202a48f8929ca0567510037d\"\n rel=\"nofollow\"\n>Full Screen Screenshot\u003C/a>\u003C/del>\u003C/li>\n\u003Cli>\u003Cdel>\u003Ca\n href=\"https://www.icloud.com/shortcuts/8dc40861f6f341d98abf8092d3ceaf39\"\n rel=\"nofollow\"\n>Window Screenshot\u003C/a>\u003C/del>\u003C/li>\n\u003C/ul>\n\u003Cp>\u003Cdel>You may need to edit these to set the save paths properly. I’ve added these to my menu bar and they work okay-ish. I want to find a better solution in the long term.\u003C/del> EDIT 4/13/23: \u003Ca href=\"#shottr\">Just use Shottr.\u003C/a> For screen recording I just open up QuickTime.\u003C/p>\n\u003Cp>I think that’s it for now? I’ll update this post if I find any big stuff in the future or if there’s anything I missed. In the next part, I’ll be covering multiplatform software.\u003C/p>\n",{"title":51,"date":52,"categories":53,"last_modified_at":54,"comments":28},"The Software Dump Part 1: macOS",["Date","2022-12-28T00:00:00.000Z"],"software life macos",["Date","2023-04-13T00:00:00.000Z"],{"slug":56,"path":57,"code":58,"fm":59},"switch-ports","/articles/switch-ports","\u003Cscript context=\"module\">\n\texport const metadata = {\"title\":\"My Switch Ports, and the Journey\",\"date\":\"2022-11-13T00:00:00.000Z\",\"categories\":\"games programming life\",\"last_modified_at\":\"2022-11-13T00:00:00.000Z\",\"comments\":true};\n\tconst { title, date, categories, last_modified_at, comments } = metadata;\n\u003C/script>\n\u003Cscript lang=\"ts\">\n import Figure from \"$lib/Figure.svelte\";\n import Image from \"$lib/Image.svelte\";\n \n import imgSRB2Pic1 from \"./20221114185059_1.jpg?as=run\";\n import imgSRB2Pic2 from \"./20221114185137_1.jpg?as=run\";\n import imgSRB2KPic1 from \"./20221114191553_1.jpg?as=run\";\n import imgSRB2KPic2 from \"./20221114191630_1.jpg?as=run\";\n import imgS1Pic1 from \"./20221114185830_1.jpg?as=run\";\n import imgS1Pic2 from \"./20221114185444_1.jpg?as=run\";\n import imgS2Pic1 from \"./20221114185608_1.jpg?as=run\";\n import imgS2Pic2 from \"./20221114185539_1.jpg?as=run\";\n import imgS3AIRPic1 from \"./20221114185702_1.jpg?as=run\";\n import imgS3AIRPic2 from \"./20221114185646_1.jpg?as=run\";\n import imgDeck from \"./IMG_2002.jpg?as=run\";\n\u003C/script>\n\n\u003Cp>I was considering making this intro a lot more long winded, but instead I’ll just say this: my motivation for porting these games to the Switch mostly came from wanting to play them there myself. At the time I ported these games, the Switch was the best option to play these games on for several reasons:\u003C/p>\n\u003Cul>\n\u003Cli>Easy to homebrew (if you have a vulnerable unit)\u003C/li>\n\u003Cli>Dockable (obvious)\u003C/li>\n\u003Cli>Powerful enough to run ports without extensive optimization\u003C/li>\n\u003Cli>Good UI\u003C/li>\n\u003Cli>Good controllers\u003C/li>\n\u003Cli>Lightweight\u003C/li>\n\u003Cli>Decent battery\u003C/li>\n\u003C/ul>\n\u003Cp>Obviously, I could’ve just put these games on a laptop and played them there, or used an Android phone and a controller, or whatever else, but it just doesn’t fit the bill of having a console-like experience. The Switch is in my top 3 consoles with the GameCube. So, what did I want on there?\u003C/p>\n\u003Ch2>Sonic Robo Blast 2\u003C/h2>\n\u003CFigure>\n \u003CImage src={imgSRB2Pic1} />\n \u003CImage src={imgSRB2Pic2} />\n\u003C/Figure>\n\u003Cp>To people out of the know, this fangame has been in development for roughly 24 years. Twenty. Four. It’s based on Doom of all things (can’t recall which source port off the top of my head) and is unexpectedly super fun to play. I’ve followed the game since I was probably like 7 or 8 and wanted a portable version ever since. At the time I ported it, the best options were to play it on something like a GPD Win, OpenPandora, or a PSP, all pretty lacking consoles for the modern age. This was also before an Android or iOS port.\u003C/p>\n\u003Cp>carstene1ns was responsible for the original port. They released a proof-of-concept port that ran at a pretty terrible framerate. I decided to attempt building their port myself, and found that the poor framerate wasn’t their fault at all. In the time between our builds, the Switch port of SDL2 had been updated to where it just… worked. So, I picked up maintenance of the port and added some more quality of life things:\u003C/p>\n\u003Cul>\n\u003Cli>Internet connectivity\n\u003Cul>\n\u003Cli>Fixed on accident while adding nxlink support!\u003C/li>\n\u003C/ul>\n\u003C/li>\n\u003Cli>On-screen keyboard support\u003C/li>\n\u003Cli>Ability to run in background (to avoid people essentially lag-switching servers)\u003C/li>\n\u003C/ul>\n\u003Cp>In addition to C programming, these ports required knowledge of SDL2, libnx, and GNU Make. At the time, I had limited knowledge of each. By the time I was done, I became fairly affluent in each. Learning to read through git history to pick out breaking changes was an essential skill needed to accomplish this as well. All in all, it was a great learning experience and the gateway to all my other ports.\u003C/p>\n\u003Cp>My only big regret was that I never got GL working. I made a few attempts, but the sheer size of the codebase, uncertainty of the state of the DevkitPro ports, and layers of complexity involved made it a time sink I wasn’t ready to take on.\u003C/p>\n\u003Ch2>Sonic Robo Blast 2 Kart\u003C/h2>\n\u003CFigure>\n \u003CImage src={imgSRB2KPic1} />\n \u003CImage src={imgSRB2KPic2} />\n\u003C/Figure>\n\u003Cp>I could try and explain how a mod of a mod of Doom turned into a kart racer, but instead I’ll just say that it was a fairly simple ordeal to port changes from SRB2 here. Not much else to say honestly. Play SRB2Kart.\u003C/p>\n\u003Ch2>Sonic 1, 2, and CD Decompilations\u003C/h2>\n\u003CFigure layout=\"2col\">\n \u003CImage src={imgS1Pic1} />\n \u003CImage src={imgS1Pic2} />\n \u003CImage src={imgS2Pic1} />\n \u003CImage src={imgS2Pic2} />\n\u003C/Figure>\n\u003Cp>Sonic CD got a remake from the ground up in 2011, and Sonic 1 and 2 followed suit in 2013. These were all done with Christian Whitehead’s (taxman’s) Retro Engine. I don’t know the full details of how, but they all got decompiled in 2021 by RubberDuckyCooly and RMGRich (and probably a few others, sorry if I didn’t list you guys here). At this point, the DevkitPro Switch portlibs had matured to the point where I could mostly just create a Makefile for them and they were good to go. A few fixes to input were needed here and there, along with some cflag changes. These ports were feature-complete, as far as I’m aware.\u003C/p>\n\u003Ch2>Sonic 3 AIR\u003C/h2>\n\u003CFigure>\n \u003CImage src={imgS3AIRPic1} />\n \u003CImage src={imgS3AIRPic2} />\n\u003C/Figure>\n\u003Cp>Sonic 3 AIR is a fan-done translation of the original Sonic 3 and Knuckles source code into a custom engine and scripting language, done by the legendary Eukaryot. I reached out to them before the source code for this recreation was public, and they agreed to give me access for the production of a Switch port. I got it up and running, feature-complete in about a week, including hardware rendering.\u003C/p>\n\u003Ch1>Why I Left, and an Apology\u003C/h1>\n\u003Cp>This year has been filled with a lot of grief, anxiety, and pain. I had to make a lot of changes to find happiness. I’ll probably write more about this towards the end of the year. One of those change included dropping maintenance of my Switch port to make time for things that mattered more to me: friends, family, my studies, and my career goals.\u003C/p>\n\u003Cp>On top of that, the whole motivation for wanting these ports had been torn to shreds… remember how I mentioned that the Switch and the GameCube were two of my favorite consoles? You may have also noticed all these screenshots are 16:10. Well, I’ve got something else sitting at number one now…\u003C/p>\n\u003CFigure>\n \u003CImage src={imgDeck} />\n\u003C/Figure>\n\u003Cp>Yup. As soon as I got the thing in my hands, my motivation for Switch development honestly went out the window.\u003C/p>\n\u003Cul>\n\u003Cli>Similar form factor to the Switch\u003C/li>\n\u003Cli>Better feeling controls\u003C/li>\n\u003Cli>Easier to “homebrew” (I mean, it’s just Arch underneath)\u003C/li>\n\u003Cli>Don’t have to develop dedicated ports\u003C/li>\n\u003Cli>Plays literally everything else I want to play minus (online) Switch games\u003C/li>\n\u003C/ul>\n\u003Cp>Having grown up on homebrew and fan projects, I know how much it sucks to see something you like go unmaintained. Being on the other side of the fence now, I can see why so many people go ghost mode. I feel a bit guilty for dropping everything, but I need to change focus until I am happy.\u003C/p>\n\n",{"title":60,"date":61,"categories":62,"last_modified_at":63,"comments":28},"My Switch Ports, and the Journey",["Date","2022-11-13T00:00:00.000Z"],"games programming life",["Date","2022-11-13T00:00:00.000Z"],{"slug":65,"path":66,"code":67,"fm":68},"new-blog","/articles/new-blog","\u003Cscript context=\"module\">\n\texport const metadata = {\"title\":\"New Blog?\",\"date\":\"2022-11-07T00:00:00.000Z\",\"last_modified_at\":\"2023-02-11T00:00:00.000Z\",\"categories\":\"meta life design ui\",\"comments\":true};\n\tconst { title, date, last_modified_at, categories, comments } = metadata;\n\u003C/script>\n\u003Cscript lang=\"ts\">\n import Image from \"$lib/Image.svelte\";\n import Figure from \"$lib/Figure.svelte\";\n \n import imgWin98Logo from \"./Windows_98_logo.webp?as=run\";\n import imgXPMCE from \"./xpmce.jpg?as=run\";\n\u003C/script>\n\n\u003Cp>So, I’m restructuring a lot of things in life right now. Why don’t we take care of the website too?\u003C/p>\n\u003Cp>I’ve been trying to find a good balance between maintainability, customization, and weight. Usually the mainstream options don’t hold up to those principles very well, but Jekyll actually manages it. It’s nice to be able to write everything in markdown, and the default templates are pretty simple and lightweight. \u003Cdel>Right now the only JS dependency is jQuery, we’ll see if it stays that way.\u003C/del> \u003Cem>EDIT 2/11/23: Removed jQuery. ;)\u003C/em> Will probably at least add a lightbox plugin for images, eventually.\u003C/p>\n\u003Cp>My site was previously written using Hexo, and while it got close to what I wanted, it was one of those things that was just non-mainstream enough that I could never get back into the flow of setting up a dev environment so that I could actually get to the writing. Lets hope this time goes a bit better!\u003C/p>\n\u003Cp>My previous website designs very heavily followed Material Design. I wanted to try something a bit more unique with this new site. I’ve taken inspiration from a few places:\u003C/p>\n\u003Ch3>Windows 98/XP\u003C/h3>\n\u003CFigure align=\"right\">\n \u003CImage src={imgWin98Logo} />\n \u003CImage src={imgXPMCE} />\n\u003C/Figure>\n\u003Cp>Most people referencing this would just throw on a vaporwave skin, maybe sprinkle a few Arizona cans and Evangelion renders here and there. I wanted to be a bit more subtle. The main font for titles and headers is Franklin Gothic, lifted straight from the Windows 98 logo. In its black weight, it’s very pleasing to look at while remaining readable. The main font for content is MS Sans Serif, which was used in the Windows 98 UI. I’m not 100% set on it right now but I figure its a nice callback. I tried Tahoma at one point (Windows XP UI font) but it’s a bit too playful for my taste; gets kinda obnoxious to look at.\u003C/p>\n\u003Cp>The background elements also call back to the design of Windows Media Center, which was included with the first PC my parents’ bought new for me.\u003C/p>\n\u003Ch3>iOS\u003C/h3>\n\u003Cp>Well, not exclusive to iOS, but certain elements of the site have a frosted glass touch to them. It’s subtle with the mostly-black background, but adds some nice flair.\u003C/p>\n\u003Ch3>“The 70’s”\u003C/h3>\n\u003Cp>Bold text, bold colors, simple designs. No references to the era specifically, just kinda lifting the general vibe.\u003C/p>\n\u003Ch3>Life\u003C/h3>\n\u003Cp>Orange is my favorite color. Wife’s favorite color bounces between sky blue and pastel pink. Our wedding colors were orange and pink. Nuff’ said.\u003C/p>\n\u003Ch3>What’s with the dice?\u003C/h3>\n\u003Cp>Needed a background element to go with the XPMCE thing. I was demoing this aesthetic on a custom startpage, and I figured it’d be fun to make it something randomized. At the same time, I wanted it to have a utilitarian purpose, so if I ever need a quick dice roll, I can just open a new tab! I’ve carried it over here, but might switch it in the future.\u003C/p>\n\u003Ch2>Going Forward\u003C/h2>\n\u003Cp>I want to spend more time doing things that bring me instrinsic happiness. Will writing in this blog be one of them? Who knows! But I’ll make an effort to keep up. Before I move onto the future, I’ll be transferring some content from my old site, doing some write-ups and reflections on my past few years of projects/life stuff, and having a bit of fun.\u003C/p>\n\n",{"title":69,"date":70,"last_modified_at":71,"categories":72,"comments":28},"New Blog?",["Date","2022-11-07T00:00:00.000Z"],["Date","2023-02-11T00:00:00.000Z"],"meta life design ui",{"slug":74,"path":75,"code":76,"fm":77},"photo-megapost","/articles/photo-megapost","\u003Cscript context=\"module\">\n\texport const metadata = {\"title\":\"Photography Megapost\",\"date\":\"2022-11-07T00:00:00.000Z\",\"categories\":\"life photography\",\"comments\":true};\n\tconst { title, date, categories, comments } = metadata;\n\u003C/script>\n\u003Cscript lang=\"ts\">\n\nimport { type GalleryDescriptor } from \"$lib/Gallery\";\nimport Gallery from '$lib/Gallery.svelte';\n\nexport let data;\n\nconst galleries = [\n {\n name: \"January 2019\",\n description: \"Point Au Roche\",\n images: data.galleries[\"jan-2019-point-au-roche\"],\n thumbs: import.meta.glob('./jan-2019-point-au-roche/*.jpg', {\n eager: true,\n query: { w: 160, h: 160, fit: 'cover', as: 'run' }\n })\n },\n {\n name: \"Summer 2018\",\n description: \"Some miscellaneous photos taken during my time working at the Whiteface ASRC in 2018.\",\n images: data.galleries[\"summer-2018-whiteface\"],\n thumbs: import.meta.glob('./summer-2018-whiteface/*.jpg', {\n eager: true,\n query: { w: 160, h: 160, fit: 'cover', as: 'run' }\n })\n },\n {\n name: \"Summer 2018 (Night)\",\n description: \"During the summer of 2018, I was given the opportunity to spend the night on the summit of Whiteface. Here are some of the photos I took in all their post-processed glory.\",\n images: data.galleries[\"summer-2018-whiteface-night\"],\n thumbs: import.meta.glob('./summer-2018-whiteface-night/*.jpg', {\n eager: true,\n query: { w: 160, h: 160, fit: 'cover', as: 'run' }\n })\n },\n {\n name: \"Misc\",\n description: \"Miscellaneous photos from my younger years.\",\n images: data.galleries[\"misc\"],\n thumbs: import.meta.glob('./misc/*.jpg', {\n eager: true,\n query: { w: 160, h: 160, fit: 'cover', as: 'run' }\n })\n }\n] as GalleryDescriptor[];\n\n\u003C/script>\n\n\u003Cp>Various snapshots of my life.\u003C/p>\n\u003Cp>\u003Ca\n href=\"https://creativecommons.org/licenses/by/4.0/\"\n rel=\"nofollow\"\n>These photos are licensed under CC BY 4.0.\u003C/a>\u003C/p>\n{#each galleries as gallery}\n\u003CGallery {gallery} />\n{/each}\n\n",{"title":78,"date":79,"categories":80,"comments":28},"Photography Megapost",["Date","2022-11-07T00:00:00.000Z"],"life photography",{"slug":82,"path":83,"code":84,"fm":85},"gd-whiteface","/articles/gd-whiteface","\u003Cscript context=\"module\">\n\texport const metadata = {\"title\":\"Graphic Design: Whiteface ASRC Logo\",\"date\":\"2022-01-20T00:00:00.000Z\",\"categories\":\"design\",\"comments\":true};\n\tconst { title, date, categories, comments } = metadata;\n\u003C/script>\n\u003Cscript lang=\"ts\">\n import Image from \"$lib/Image.svelte\";\n import Figure from \"$lib/Figure.svelte\";\n \n import imgLogo from \"./logo.svg\";\n import imgPhoto from \"./photo.jpg?as=run\";\n\u003C/script>\n\n\u003Cp>A quick overview of my logo design.\u003C/p>\n\u003CFigure frame=\"light\">\n \u003CImage src={imgLogo} />\n\u003C/Figure>\n\u003Cp>The Whiteface ASRC logo was designed seperately from the SUNY Albany ASRC logo in order to give the Whiteface ASRC a more distinct identity without tampering with the historical background of the original ASRC logo. The logo is modeled after the shape of the round-house and silo at the top of the mountain while also including key elements such as the shape of the mountain, the silhouette of the cloud collector, and the radio platform.\u003C/p>\n\u003CFigure>\n \u003CImage src={imgPhoto} />\n\u003C/Figure>\n\u003Cp>The font used is Josefin Sans and was mainly chosen due to its bold look and the distinct shape of its W. The logo was designed in Inkscape.\u003C/p>\n\n",{"title":86,"date":87,"categories":88,"comments":28},"Graphic Design: Whiteface ASRC Logo",["Date","2022-01-20T00:00:00.000Z"],"design",{"slug":90,"path":91,"code":92,"fm":93},"gd-anime-was-a-mistake","/articles/gd-anime-was-a-mistake","\u003Cscript context=\"module\">\n\texport const metadata = {\"title\":\"Graphic Design: \\\"Anime was a Mistake\\\" Shirt\",\"date\":\"2019-01-21T00:00:00.000Z\",\"categories\":\"design ui\",\"last_modified_at\":\"2022-11-07T00:00:00.000Z\",\"comments\":true};\n\tconst { title, date, categories, last_modified_at, comments } = metadata;\n\u003C/script>\n\u003Cscript lang=\"ts\">\n\nimport Image from \"$lib/Image.svelte\";\nimport Figure from \"$lib/Figure.svelte\";\n\nimport imgFinal from \"./final.png?as=run:0\";\nimport imgLetters from \"./letters.svg\";\nimport imgBorder from \"./border.svg?as=run\";\nimport imgImport from \"./import.png?as=run\";\nimport imgTop from \"./top.png?as=run\";\nimport imgRaw from \"./raw.png?as=run:0\";\nimport imgOdyssey from \"./odyssey.png?as=run:0\";\n\nimport fileBlend from \"./anime.blend\";\n\n\u003C/script>\n\n\u003Cp>A not-so-serious venture into 3D graphic design.\u003C/p>\n\u003CFigure>\n \u003CImage src={imgFinal} />\n\u003C/Figure>\n\u003Cp>This design was published as a shirt design on RedBubble back in December of 2017. \u003Cdel>You can view the design on RedBubble here.\u003C/del> Unfortunately, Nintendo themselves DMCA’d it because they felt like being petty.\u003C/p>\n\u003Ch2>Making Of\u003C/h2>\n\u003Cp>The first thing that was done was obtaining the Super Mario 256 font. This was then used to type the words onto an SVG using Inkscape. A second SVG was used to make the borders for the letters.\u003C/p>\n\u003CFigure frame=\"light\">\n \u003CImage src={imgLetters} />\n \u003CImage src={imgBorder} />\n\u003C/Figure>\n\u003Cp>These were then imported into Blender and extruded to make them 3D. Although the letters seem shallow in the final render, they are actually quite long. The letters were then given materials with appropriate colors.\u003C/p>\n\u003CFigure>\n \u003CImage src={imgImport} />\n \u003CImage src={imgTop} />\n\u003C/Figure>\n\u003Cp>The raw render out of Blender is as follows:\u003C/p>\n\u003CFigure>\n \u003CImage src={imgRaw} />\n\u003C/Figure>\n\u003Cp>It’s lacking a lot of little details though. The letters have no gradients or sparkles, and the foreground graphic of Cappy is straight up missing. The final details were added in GIMP:\u003C/p>\n\u003CFigure>\n \u003CImage src={imgFinal} />\n\u003C/Figure>\n\u003Cp>For comparison, here’s the original logo:\u003C/p>\n\u003CFigure>\n \u003CImage src={imgOdyssey} />\n\u003C/Figure>\n\u003Ch2>Thoughts\u003C/h2>\n\u003Cp>There’s definitely a lot of things that could be improved here. The planet from the “O” in the original logo is completely missing. A lot of materials don’t look metallic enough. The borders don’t come down at the right angle and have gaps. Colors are a bit desaturated. The middle letters aren’t stretched enough and the top letters are too big. The Cappy graphic appears to be out of date (?). However, for something that was honestly just intended as a joke, I think it came out alright.\u003C/p>\n\u003Ch2>Download\u003C/h2>\n\u003Cp>If you’d like to download the original project file, you’re in luck! Here it is:\u003C/p>\n\u003Cp>\u003Ca href=\"{fileBlend}\">Blender Project\u003C/a>\u003C/p>\n\u003Cp>If you want the render itself, you should be able to just right click and “Save As”. Same goes for the SVGs.\u003C/p>\n\u003Cp>\u003Ca\n href=\"https://creativecommons.org/licenses/by/4.0/\"\n rel=\"nofollow\"\n>This project is licensed under CC BY 4.0.\u003C/a>\u003C/p>\n\n",{"title":94,"date":95,"categories":96,"last_modified_at":97,"comments":28},"Graphic Design: \"Anime was a Mistake\" Shirt",["Date","2019-01-21T00:00:00.000Z"],"design ui",["Date","2022-11-07T00:00:00.000Z"],{"slug":99,"path":100,"code":101,"fm":102},"gd-mayolo","/articles/gd-mayolo","\u003Cscript context=\"module\">\n\texport const metadata = {\"title\":\"Graphic Design: \\\"Mayolo\\\" Shirt\",\"date\":\"2019-01-21T00:00:00.000Z\",\"categories\":\"design\",\"comments\":true};\n\tconst { title, date, categories, comments } = metadata;\n\u003C/script>\n\u003Cscript lang=\"ts\">\n import Image from \"$lib/Image.svelte\";\n import Figure from \"$lib/Figure.svelte\";\n \n import imgFinal from \"./final.png?as=run:\";\n import imgText from \"./text.png?as=run:\";\n import imgTextSVG from \"./text.svg\";\n import imgEyes from \"./eyes.jpg?as=run:\";\n import imgEyesSVG from \"./eyes.svg\";\n import imgNormal from \"./normal.jpg?as=run:\";\n import imgBottle from \"./bottle.png?as=run:\";\n import imgLighting from \"./lighting.png?as=run:\";\n import imgFirst from \"./first.png?as=run:\";\n import imgTireBlur from \"./tireblur.png?as=run:\";\n import imgTire from \"./tire.png?as=run:\";\n import imgTracks from \"./tracks.png?as=run:\";\n import imgSecond from \"./second.png?as=run:\";\n\u003C/script>\n\n\u003Cp>How I did my first commission!\u003C/p>\n\u003CFigure>\n \u003CImage src={imgFinal} />\n\u003C/Figure>\n\u003Cp>This design was a done as commission for someone. I was given the general idea of a cartoon mayo bottle writing out the words “Mayolo”, and the rest was up to my own judgement.\u003C/p>\n\u003Ch2>Making Of\u003C/h2>\n\u003Cp>I started by writing out the text. After doing some sketches, I settled on this text and translated it into an SVG. I was requested to change the “Y” uppercase and did so in the final SVG. The line was brought down to a middle point in order to place the mayo bottle more easily.\u003C/p>\n\u003CFigure frame=\"light\">\n \u003CImage src={imgText} />\n \u003CImage src={imgTextSVG} />\n\u003C/Figure>\n\u003Cp>I originally wanted to draw in the eyes in either GIMP or Inkscape, but eventually found it to look bad. I made the eye shape into an SVG as well, and then imported both into Blender.\u003C/p>\n\u003CFigure>\n \u003CImage src={imgEyes} />\n \u003CImage src={imgEyesSVG} />\n\u003C/Figure>\n\u003Cp>The mayo bottle was modeled by hand, and is essentially just a carefully extruded circle. The edge of the cap was given a bumpy normal map in order to simulate the look of the grip.\u003C/p>\n\u003CFigure>\n \u003CImage src={imgNormal} />\n \u003CImage src={imgBottle} />\n\u003C/Figure>\n\u003Cp>The text and eyes were extruded, beveled, and given materials. The text was also given an armature so it could be “posed” more easily. The pupils were made from squished down spheres. Objects were positioned and then given numerous light sources. The lighting is crucial in order to give the final render a less gritty appearance. The below image highlights all the different light sources in the final render: 11 in total.\u003C/p>\n\u003CFigure>\n \u003CImage src={imgLighting} />\n\u003C/Figure>\n\u003Cp>The scene was rendered and then given some soft shadows and a background color using GIMP.\u003C/p>\n\u003CFigure>\n \u003CImage src={imgFirst} />\n\u003C/Figure>\n\u003Cp>However, the client wanted a few more details added:\u003C/p>\n\u003Cul>\n\u003Cli>Eyebrows\u003C/li>\n\u003Cli>Smile\u003C/li>\n\u003Cli>Wheels\u003C/li>\n\u003Cli>Skid marks\u003C/li>\n\u003Cli>Peel-out smoke/sparks\u003C/li>\n\u003C/ul>\n\u003Cp>The eyebrows were fairly straightforwards. Give an armature to a long cylinder and bend it. Same goes for the smile. The wheels were a bit trickier. A circle was extruded into the shape of half of a wheel, and then the model was mirrored. A tire texture was made and then UV mapped onto the model. The texture was then blurred in one direction to give the appearance of motion. The inner rim was given a silver, metalic material.\u003C/p>\n\u003CFigure>\n \u003CImage src={imgTireBlur} scale=0.01 />\n \u003CImage src={imgTire} />\n\u003C/Figure>\n\u003Cp>The skid marks were made from two long extended planes and given a blurry, transparent, and lightened version of the tire texture. Despite what it looks like in the render, the skid marks stretch extremely far into the background.\u003C/p>\n\u003CFigure>\n \u003CImage src={imgTracks} />\n\u003C/Figure>\n\u003Cp>The peel-out smoke and sparks were created using particle emitters placed directly behind the tires. These were given “Smoke” properties with a flow type of “Fire + Smoke”. The raw render looked like this after compositing all layers:\u003C/p>\n\u003CFigure>\n \u003CImage src={imgSecond} />\n\u003C/Figure>\n\u003Cp>However, the flame effect was a bit lacking. Extra flame and smoke graphics were added to make the final render:\u003C/p>\n\u003CFigure>\n \u003CImage src={imgFinal} />\n\u003C/Figure>\n\n",{"title":103,"date":104,"categories":88,"comments":28},"Graphic Design: \"Mayolo\" Shirt",["Date","2019-01-21T00:00:00.000Z"]],"uses":{}}]} diff --git a/_app/immutable/assets/2.CiemoY37.css b/_app/immutable/assets/2.CiemoY37.css deleted file mode 100644 index ff34cd2..0000000 --- a/_app/immutable/assets/2.CiemoY37.css +++ /dev/null @@ -1 +0,0 @@ -#cusdis_thread.svelte-1b4u2h8>iframe{height:75vh}.post-meta-inline.svelte-xi2wws{color:#ddd;margin-top:8px;margin-bottom:16px}time.svelte-xi2wws{font-family:franklin_gothicregular,sans-serif;font-size:15px;margin-right:8px;vertical-align:middle} diff --git a/_app/immutable/assets/2.uYDo6Uun.css b/_app/immutable/assets/2.uYDo6Uun.css new file mode 100644 index 0000000..b68f304 --- /dev/null +++ b/_app/immutable/assets/2.uYDo6Uun.css @@ -0,0 +1 @@ +#cusdis_thread.svelte-1b4u2h8>iframe{height:75vh}.header-container.svelte-1b2s67e{display:flex;justify-content:space-between}.header-container.svelte-1b2s67e h1:where(.svelte-1b2s67e){margin-top:0}.times.svelte-1b2s67e{display:inline;text-align:right;flex-shrink:0}.dt.svelte-1b2s67e:not(:first-child){opacity:.8;font-size:12px}time.svelte-1b2s67e{font-family:franklin_gothicregular,sans-serif;font-size:15px;margin-right:8px;vertical-align:middle} diff --git a/_app/immutable/assets/_layout.CiemoY37.css b/_app/immutable/assets/_layout.CiemoY37.css deleted file mode 100644 index ff34cd2..0000000 --- a/_app/immutable/assets/_layout.CiemoY37.css +++ /dev/null @@ -1 +0,0 @@ -#cusdis_thread.svelte-1b4u2h8>iframe{height:75vh}.post-meta-inline.svelte-xi2wws{color:#ddd;margin-top:8px;margin-bottom:16px}time.svelte-xi2wws{font-family:franklin_gothicregular,sans-serif;font-size:15px;margin-right:8px;vertical-align:middle} diff --git a/_app/immutable/assets/_layout.uYDo6Uun.css b/_app/immutable/assets/_layout.uYDo6Uun.css new file mode 100644 index 0000000..b68f304 --- /dev/null +++ b/_app/immutable/assets/_layout.uYDo6Uun.css @@ -0,0 +1 @@ +#cusdis_thread.svelte-1b4u2h8>iframe{height:75vh}.header-container.svelte-1b2s67e{display:flex;justify-content:space-between}.header-container.svelte-1b2s67e h1:where(.svelte-1b2s67e){margin-top:0}.times.svelte-1b2s67e{display:inline;text-align:right;flex-shrink:0}.dt.svelte-1b2s67e:not(:first-child){opacity:.8;font-size:12px}time.svelte-1b2s67e{font-family:franklin_gothicregular,sans-serif;font-size:15px;margin-right:8px;vertical-align:middle} diff --git a/_app/immutable/chunks/entry.B8mbJhC8.js b/_app/immutable/chunks/entry.B8mbJhC8.js deleted file mode 100644 index a8bc0f1..0000000 --- a/_app/immutable/chunks/entry.B8mbJhC8.js +++ /dev/null @@ -1,3 +0,0 @@ -import{aH as lt}from"./runtime.Dfx-lVnK.js";import{w as pe}from"./index.CO8mGWLE.js";new URL("sveltekit-internal://");function ft(e,n){return e==="/"||n==="ignore"?e:n==="never"?e.endsWith("/")?e.slice(0,-1):e:n==="always"&&!e.endsWith("/")?e+"/":e}function ut(e){return e.split("%25").map(decodeURI).join("%25")}function dt(e){for(const n in e)e[n]=decodeURIComponent(e[n]);return e}function ce({href:e}){return e.split("#")[0]}const ht=["href","pathname","search","toString","toJSON"];function pt(e,n,t){const r=new URL(e);Object.defineProperty(r,"searchParams",{value:new Proxy(r.searchParams,{get(a,o){if(o==="get"||o==="getAll"||o==="has")return s=>(t(s),a[o](s));n();const i=Reflect.get(a,o);return typeof i=="function"?i.bind(a):i}}),enumerable:!0,configurable:!0});for(const a of ht)Object.defineProperty(r,a,{get(){return n(),e[a]},enumerable:!0,configurable:!0});return r}const gt="/__data.json",mt=".html__data.json";function yt(e){return e.endsWith(".html")?e.replace(/\.html$/,mt):e.replace(/\/$/,"")+gt}function _t(...e){let n=5381;for(const t of e)if(typeof t=="string"){let r=t.length;for(;r;)n=n*33^t.charCodeAt(--r)}else if(ArrayBuffer.isView(t)){const r=new Uint8Array(t.buffer,t.byteOffset,t.byteLength);let a=r.length;for(;a;)n=n*33^r[--a]}else throw new TypeError("value must be a string or TypedArray");return(n>>>0).toString(36)}function wt(e){const n=atob(e),t=new Uint8Array(n.length);for(let r=0;r((e instanceof Request?e.method:(n==null?void 0:n.method)||"GET")!=="GET"&&B.delete(ge(e)),$e(e,n));const B=new Map;function vt(e,n){const t=ge(e,n),r=document.querySelector(t);if(r!=null&&r.textContent){let{body:a,...o}=JSON.parse(r.textContent);const i=r.getAttribute("data-ttl");return i&&B.set(t,{body:a,init:o,ttl:1e3*Number(i)}),r.getAttribute("data-b64")!==null&&(a=wt(a)),Promise.resolve(new Response(a,o))}return window.fetch(e,n)}function bt(e,n,t){if(B.size>0){const r=ge(e,t),a=B.get(r);if(a){if(performance.now(){const a=/^\[\.\.\.(\w+)(?:=(\w+))?\]$/.exec(r);if(a)return n.push({name:a[1],matcher:a[2],optional:!1,rest:!0,chained:!0}),"(?:/(.*))?";const o=/^\[\[(\w+)(?:=(\w+))?\]\]$/.exec(r);if(o)return n.push({name:o[1],matcher:o[2],optional:!0,rest:!1,chained:!0}),"(?:/([^/]+))?";if(!r)return;const i=r.split(/\[(.+?)\](?!\])/);return"/"+i.map((c,f)=>{if(f%2){if(c.startsWith("x+"))return le(String.fromCharCode(parseInt(c.slice(2),16)));if(c.startsWith("u+"))return le(String.fromCharCode(...c.slice(2).split("-").map(l=>parseInt(l,16))));const d=At.exec(c),[,h,y,u,p]=d;return n.push({name:u,matcher:p,optional:!!h,rest:!!y,chained:y?f===1&&i[0]==="":!1}),y?"(.*?)":h?"([^/]*)?":"([^/]+?)"}return le(c)}).join("")}).join("")}/?$`),params:n}}function Et(e){return!/^\([^)]+\)$/.test(e)}function St(e){return e.slice(1).split("/").filter(Et)}function Rt(e,n,t){const r={},a=e.slice(1),o=a.filter(s=>s!==void 0);let i=0;for(let s=0;sd).join("/"),i=0),f===void 0){c.rest&&(r[c.name]="");continue}if(!c.matcher||t[c.matcher](f)){r[c.name]=f;const d=n[s+1],h=a[s+1];d&&!d.rest&&d.optional&&h&&c.chained&&(i=0),!d&&!h&&Object.keys(r).length===o.length&&(i=0);continue}if(c.optional&&c.chained){i++;continue}return}if(!i)return r}function le(e){return e.normalize().replace(/[[\]]/g,"\\$&").replace(/%/g,"%25").replace(/\//g,"%2[Ff]").replace(/\?/g,"%3[Ff]").replace(/#/g,"%23").replace(/[.*+?^${}()|\\]/g,"\\$&")}function It({nodes:e,server_loads:n,dictionary:t,matchers:r}){const a=new Set(n);return Object.entries(t).map(([s,[c,f,d]])=>{const{pattern:h,params:y}=kt(s),u={id:s,exec:p=>{const l=h.exec(p);if(l)return Rt(l,y,r)},errors:[1,...d||[]].map(p=>e[p]),layouts:[0,...f||[]].map(i),leaf:o(c)};return u.errors.length=u.layouts.length=Math.max(u.errors.length,u.layouts.length),u});function o(s){const c=s<0;return c&&(s=~s),[c,e[s]]}function i(s){return s===void 0?s:[a.has(s),e[s]]}}function Fe(e,n=JSON.parse){try{return n(sessionStorage[e])}catch{}}function Ie(e,n,t=JSON.stringify){const r=t(n);try{sessionStorage[e]=r}catch{}}var je;const U=((je=globalThis.__sveltekit_nohms1)==null?void 0:je.base)??"";var De;const Tt=((De=globalThis.__sveltekit_nohms1)==null?void 0:De.assets)??U,Ut="1733256088553",Ve="sveltekit:snapshot",Be="sveltekit:scroll",Ge="sveltekit:states",Lt="sveltekit:pageurl",j="sveltekit:history",q="sveltekit:navigation",Y={tap:1,hover:2,viewport:3,eager:4,off:-1,false:-1},K=location.origin;function qe(e){if(e instanceof URL)return e;let n=document.baseURI;if(!n){const t=document.getElementsByTagName("base");n=t.length?t[0].href:document.URL}return new URL(e,n)}function me(){return{x:pageXOffset,y:pageYOffset}}function N(e,n){return e.getAttribute(`data-sveltekit-${n}`)}const Te={...Y,"":Y.hover};function He(e){let n=e.assignedSlot??e.parentNode;return(n==null?void 0:n.nodeType)===11&&(n=n.host),n}function Me(e,n){for(;e&&e!==n;){if(e.nodeName.toUpperCase()==="A"&&e.hasAttribute("href"))return e;e=He(e)}}function ue(e,n){let t;try{t=new URL(e instanceof SVGAElement?e.href.baseVal:e.href,document.baseURI)}catch{}const r=e instanceof SVGAElement?e.target.baseVal:e.target,a=!t||!!r||ne(t,n)||(e.getAttribute("rel")||"").split(/\s+/).includes("external"),o=(t==null?void 0:t.origin)===K&&e.hasAttribute("download");return{url:t,external:a,target:r,download:o}}function J(e){let n=null,t=null,r=null,a=null,o=null,i=null,s=e;for(;s&&s!==document.documentElement;)r===null&&(r=N(s,"preload-code")),a===null&&(a=N(s,"preload-data")),n===null&&(n=N(s,"keepfocus")),t===null&&(t=N(s,"noscroll")),o===null&&(o=N(s,"reload")),i===null&&(i=N(s,"replacestate")),s=He(s);function c(f){switch(f){case"":case"true":return!0;case"off":case"false":return!1;default:return}}return{preload_code:Te[r??"off"],preload_data:Te[a??"off"],keepfocus:c(n),noscroll:c(t),reload:c(o),replace_state:c(i)}}function Ue(e){const n=pe(e);let t=!0;function r(){t=!0,n.update(i=>i)}function a(i){t=!1,n.set(i)}function o(i){let s;return n.subscribe(c=>{(s===void 0||t&&c!==s)&&i(s=c)})}return{notify:r,set:a,subscribe:o}}function Pt(){const{set:e,subscribe:n}=pe(!1);let t;async function r(){clearTimeout(t);try{const a=await fetch(`${Tt}/_app/version.json`,{headers:{pragma:"no-cache","cache-control":"no-cache"}});if(!a.ok)return!1;const i=(await a.json()).version!==Ut;return i&&(e(!0),clearTimeout(t)),i}catch{return!1}}return{subscribe:n,check:r}}function ne(e,n){return e.origin!==K||!e.pathname.startsWith(n)}function Le(e){const n=Ct(e),t=new ArrayBuffer(n.length),r=new DataView(t);for(let a=0;a>16),n+=String.fromCharCode((t&65280)>>8),n+=String.fromCharCode(t&255),t=r=0);return r===12?(t>>=4,n+=String.fromCharCode(t)):r===18&&(t>>=2,n+=String.fromCharCode((t&65280)>>8),n+=String.fromCharCode(t&255)),n}const Nt=-1,Ot=-2,jt=-3,Dt=-4,$t=-5,Ft=-6;function Vt(e,n){if(typeof e=="number")return a(e,!0);if(!Array.isArray(e)||e.length===0)throw new Error("Invalid input");const t=e,r=Array(t.length);function a(o,i=!1){if(o===Nt)return;if(o===jt)return NaN;if(o===Dt)return 1/0;if(o===$t)return-1/0;if(o===Ft)return-0;if(i)throw new Error("Invalid input");if(o in r)return r[o];const s=t[o];if(!s||typeof s!="object")r[o]=s;else if(Array.isArray(s))if(typeof s[0]=="string"){const c=s[0],f=n==null?void 0:n[c];if(f)return r[o]=f(a(s[1]));switch(c){case"Date":r[o]=new Date(s[1]);break;case"Set":const d=new Set;r[o]=d;for(let u=1;un!=null)}class re{constructor(n,t){this.status=n,typeof t=="string"?this.body={message:t}:t?this.body=t:this.body={message:`Error: ${n}`}}toString(){return JSON.stringify(this.body)}}class We{constructor(n,t){this.status=n,this.location=t}}class ye extends Error{constructor(n,t,r){super(r),this.status=n,this.text=t}}const qt="x-sveltekit-invalidated",Ht="x-sveltekit-trailing-slash";function z(e){return e instanceof re||e instanceof ye?e.status:500}function Mt(e){return e instanceof ye?e.text:"Internal Error"}const C=Fe(Be)??{},H=Fe(Ve)??{},P={url:Ue({}),page:Ue({}),navigating:pe(null),updated:Pt()};function _e(e){C[e]=me()}function Kt(e,n){let t=e+1;for(;C[t];)delete C[t],t+=1;for(t=n+1;H[t];)delete H[t],t+=1}function $(e){return location.href=e.href,new Promise(()=>{})}async function Ye(){if("serviceWorker"in navigator){const e=await navigator.serviceWorker.getRegistration(U||"/");e&&await e.update()}}function Pe(){}let ae,de,X,L,he,F;const Je=[],Z=[];let R=null;const ze=[],Wt=[];let O=[],_={branch:[],error:null,url:null},we=!1,Q=!1,xe=!0,M=!1,V=!1,Xe=!1,ve=!1,be,E,T,I,ee;const G=new Set;async function on(e,n,t){var a,o;document.URL!==location.href&&(location.href=location.href),F=e,ae=It(e),L=document.documentElement,he=n,de=e.nodes[0],X=e.nodes[1],de(),X(),E=(a=history.state)==null?void 0:a[j],T=(o=history.state)==null?void 0:o[q],E||(E=T=Date.now(),history.replaceState({...history.state,[j]:E,[q]:T},""));const r=C[E];r&&(history.scrollRestoration="manual",scrollTo(r.x,r.y)),t?await tn(he,t):Qt(location.href,{replaceState:!0}),en()}function Yt(){Je.length=0,ve=!1}function Ze(e){Z.some(n=>n==null?void 0:n.snapshot)&&(H[e]=Z.map(n=>{var t;return(t=n==null?void 0:n.snapshot)==null?void 0:t.capture()}))}function Qe(e){var n;(n=H[e])==null||n.forEach((t,r)=>{var a,o;(o=(a=Z[r])==null?void 0:a.snapshot)==null||o.restore(t)})}function Ce(){_e(E),Ie(Be,C),Ze(T),Ie(Ve,H)}async function et(e,n,t,r){return W({type:"goto",url:qe(e),keepfocus:n.keepFocus,noscroll:n.noScroll,replace_state:n.replaceState,state:n.state,redirect_count:t,nav_token:r,accept:()=>{n.invalidateAll&&(ve=!0)}})}async function Jt(e){if(e.id!==(R==null?void 0:R.id)){const n={};G.add(n),R={id:e.id,token:n,promise:nt({...e,preload:n}).then(t=>(G.delete(n),t.type==="loaded"&&t.state.error&&(R=null),t))}}return R.promise}async function fe(e){const n=ae.find(t=>t.exec(rt(e)));n&&await Promise.all([...n.layouts,n.leaf].map(t=>t==null?void 0:t[1]()))}function tt(e,n,t){var o;_=e.state;const r=document.querySelector("style[data-sveltekit]");r&&r.remove(),I=e.props.page,be=new F.root({target:n,props:{...e.props,stores:P,components:Z},hydrate:t,sync:!1}),Qe(T);const a={from:null,to:{params:_.params,route:{id:((o=_.route)==null?void 0:o.id)??null},url:new URL(location.href)},willUnload:!1,type:"enter",complete:Promise.resolve()};O.forEach(i=>i(a)),Q=!0}function te({url:e,params:n,branch:t,status:r,error:a,route:o,form:i}){let s="never";if(U&&(e.pathname===U||e.pathname===U+"/"))s="always";else for(const u of t)(u==null?void 0:u.slash)!==void 0&&(s=u.slash);e.pathname=ft(e.pathname,s),e.search=e.search;const c={type:"loaded",state:{url:e,params:n,branch:t,error:a,route:o},props:{constructors:Gt(t).map(u=>u.node.component),page:I}};i!==void 0&&(c.props.form=i);let f={},d=!I,h=0;for(let u=0;u(s&&(c.route=!0),l[m])}),params:new Proxy(r,{get:(l,m)=>(s&&c.params.add(m),l[m])}),data:(o==null?void 0:o.data)??null,url:pt(t,()=>{s&&(c.url=!0)},l=>{s&&c.search_params.add(l)}),async fetch(l,m){let b;l instanceof Request?(b=l.url,m={body:l.method==="GET"||l.method==="HEAD"?void 0:await l.blob(),cache:l.cache,credentials:l.credentials,headers:l.headers,integrity:l.integrity,keepalive:l.keepalive,method:l.method,mode:l.mode,redirect:l.redirect,referrer:l.referrer,referrerPolicy:l.referrerPolicy,signal:l.signal,...m}):b=l;const S=new URL(b,t);return s&&u(S.href),S.origin===t.origin&&(b=S.href.slice(t.origin.length)),Q?bt(b,S.href,m):vt(b,m)},setHeaders:()=>{},depends:u,parent(){return s&&(c.parent=!0),n()},untrack(l){s=!1;try{return l()}finally{s=!0}}};i=await f.universal.load.call(null,p)??null}return{node:f,loader:e,server:o,universal:(h=f.universal)!=null&&h.load?{type:"data",data:i,uses:c}:null,data:i??(o==null?void 0:o.data)??null,slash:((y=f.universal)==null?void 0:y.trailingSlash)??(o==null?void 0:o.slash)}}function Ne(e,n,t,r,a,o){if(ve)return!0;if(!a)return!1;if(a.parent&&e||a.route&&n||a.url&&t)return!0;for(const i of a.search_params)if(r.has(i))return!0;for(const i of a.params)if(o[i]!==_.params[i])return!0;for(const i of a.dependencies)if(Je.some(s=>s(new URL(i))))return!0;return!1}function ke(e,n){return(e==null?void 0:e.type)==="data"?e:(e==null?void 0:e.type)==="skip"?n??null:null}function zt(e,n){if(!e)return new Set(n.searchParams.keys());const t=new Set([...e.searchParams.keys(),...n.searchParams.keys()]);for(const r of t){const a=e.searchParams.getAll(r),o=n.searchParams.getAll(r);a.every(i=>o.includes(i))&&o.every(i=>a.includes(i))&&t.delete(r)}return t}function Oe({error:e,url:n,route:t,params:r}){return{type:"loaded",state:{error:e,url:n,route:t,params:r,branch:[]},props:{page:I,constructors:[]}}}async function nt({id:e,invalidating:n,url:t,params:r,route:a,preload:o}){if((R==null?void 0:R.id)===e)return G.delete(R.token),R.promise;const{errors:i,layouts:s,leaf:c}=a,f=[...s,c];i.forEach(g=>g==null?void 0:g().catch(()=>{})),f.forEach(g=>g==null?void 0:g[1]().catch(()=>{}));let d=null;const h=_.url?e!==_.url.pathname+_.url.search:!1,y=_.route?a.id!==_.route.id:!1,u=zt(_.url,t);let p=!1;const l=f.map((g,v)=>{var x;const A=_.branch[v],k=!!(g!=null&&g[0])&&((A==null?void 0:A.loader)!==g[1]||Ne(p,y,h,u,(x=A.server)==null?void 0:x.uses,r));return k&&(p=!0),k});if(l.some(Boolean)){try{d=await st(t,l)}catch(g){const v=await D(g,{url:t,params:r,route:{id:e}});return G.has(o)?Oe({error:v,url:t,params:r,route:a}):oe({status:z(g),error:v,url:t,route:a})}if(d.type==="redirect")return d}const m=d==null?void 0:d.nodes;let b=!1;const S=f.map(async(g,v)=>{var se;if(!g)return;const A=_.branch[v],k=m==null?void 0:m[v];if((!k||k.type==="skip")&&g[1]===(A==null?void 0:A.loader)&&!Ne(b,y,h,u,(se=A.universal)==null?void 0:se.uses,r))return A;if(b=!0,(k==null?void 0:k.type)==="error")throw k;return Ae({loader:g[1],url:t,params:r,route:a,parent:async()=>{var Re;const Se={};for(let ie=0;ie{});const w=[];for(let g=0;gPromise.resolve({}),server_data_node:ke(o)}),c={node:await X(),loader:X,universal:null,server:null,data:null};return te({url:t,params:a,branch:[s,c],status:e,error:n,route:null})}function Ee(e,n){if(!e||ne(e,U))return;let t;try{t=F.hooks.reroute({url:new URL(e)})??e.pathname}catch{return}const r=rt(t);for(const a of ae){const o=a.exec(r);if(o)return{id:e.pathname+e.search,invalidating:n,route:a,params:dt(o),url:e}}}function rt(e){return ut(e.slice(U.length)||"/")}function at({url:e,type:n,intent:t,delta:r}){let a=!1;const o=ct(_,t,e,n);r!==void 0&&(o.navigation.delta=r);const i={...o.navigation,cancel:()=>{a=!0,o.reject(new Error("navigation cancelled"))}};return M||ze.forEach(s=>s(i)),a?null:o}async function W({type:e,url:n,popped:t,keepfocus:r,noscroll:a,replace_state:o,state:i={},redirect_count:s=0,nav_token:c={},accept:f=Pe,block:d=Pe}){const h=Ee(n,!1),y=at({url:n,type:e,delta:t==null?void 0:t.delta,intent:h});if(!y){d();return}const u=E,p=T;f(),M=!0,Q&&P.navigating.set(y.navigation),ee=c;let l=h&&await nt(h);if(!l){if(ne(n,U))return await $(n);l=await ot(n,{id:null},await D(new ye(404,"Not Found",`Not found: ${n.pathname}`),{url:n,params:{},route:{id:null}}),404)}if(n=(h==null?void 0:h.url)||n,ee!==c)return y.reject(new Error("navigation aborted")),!1;if(l.type==="redirect")if(s>=20)l=await oe({status:500,error:await D(new Error("Redirect loop"),{url:n,params:{},route:{id:null}}),url:n,route:{id:null}});else return et(new URL(l.location,n).href,{},s+1,c),!1;else l.props.page.status>=400&&await P.updated.check()&&(await Ye(),await $(n));if(Yt(),_e(u),Ze(p),l.props.page.url.pathname!==n.pathname&&(n.pathname=l.props.page.url.pathname),i=t?t.state:i,!t){const w=o?0:1,g={[j]:E+=w,[q]:T+=w,[Ge]:i};(o?history.replaceState:history.pushState).call(history,g,"",n),o||Kt(E,T)}if(R=null,l.props.page.state=i,Q){_=l.state,l.props.page&&(l.props.page.url=n);const w=(await Promise.all(Wt.map(g=>g(y.navigation)))).filter(g=>typeof g=="function");if(w.length>0){let g=function(){O=O.filter(v=>!w.includes(v))};w.push(g),O.push(...w)}be.$set(l.props),Xe=!0}else tt(l,he,!1);const{activeElement:m}=document;await lt();const b=t?t.scroll:a?me():null;if(xe){const w=n.hash&&document.getElementById(decodeURIComponent(n.hash.slice(1)));b?scrollTo(b.x,b.y):w?w.scrollIntoView():scrollTo(0,0)}const S=document.activeElement!==m&&document.activeElement!==document.body;!r&&!S&&nn(),xe=!0,l.props.page&&(I=l.props.page),M=!1,e==="popstate"&&Qe(T),y.fulfil(void 0),O.forEach(w=>w(y.navigation)),P.navigating.set(null)}async function ot(e,n,t,r){return e.origin===K&&e.pathname===location.pathname&&!we?await oe({status:r,error:t,url:e,route:n}):await $(e)}function Zt(){let e;L.addEventListener("mousemove",o=>{const i=o.target;clearTimeout(e),e=setTimeout(()=>{r(i,2)},20)});function n(o){o.defaultPrevented||r(o.composedPath()[0],1)}L.addEventListener("mousedown",n),L.addEventListener("touchstart",n,{passive:!0});const t=new IntersectionObserver(o=>{for(const i of o)i.isIntersecting&&(fe(i.target.href),t.unobserve(i.target))},{threshold:0});function r(o,i){const s=Me(o,L);if(!s)return;const{url:c,external:f,download:d}=ue(s,U);if(f||d)return;const h=J(s),y=c&&_.url.pathname+_.url.search===c.pathname+c.search;if(!h.reload&&!y)if(i<=h.preload_data){const u=Ee(c,!1);u&&Jt(u)}else i<=h.preload_code&&fe(c.pathname)}function a(){t.disconnect();for(const o of L.querySelectorAll("a")){const{url:i,external:s,download:c}=ue(o,U);if(s||c)continue;const f=J(o);f.reload||(f.preload_code===Y.viewport&&t.observe(o),f.preload_code===Y.eager&&fe(i.pathname))}}O.push(a),a()}function D(e,n){if(e instanceof re)return e.body;const t=z(e),r=Mt(e);return F.hooks.handleError({error:e,event:n,status:t,message:r})??{message:r}}function Qt(e,n={}){return e=qe(e),e.origin!==K?Promise.reject(new Error("goto: invalid URL")):et(e,n,0)}function en(){var n;history.scrollRestoration="manual",addEventListener("beforeunload",t=>{let r=!1;if(Ce(),!M){const a=ct(_,void 0,null,"leave"),o={...a.navigation,cancel:()=>{r=!0,a.reject(new Error("navigation cancelled"))}};ze.forEach(i=>i(o))}r?(t.preventDefault(),t.returnValue=""):history.scrollRestoration="auto"}),addEventListener("visibilitychange",()=>{document.visibilityState==="hidden"&&Ce()}),(n=navigator.connection)!=null&&n.saveData||Zt(),L.addEventListener("click",async t=>{if(t.button||t.which!==1||t.metaKey||t.ctrlKey||t.shiftKey||t.altKey||t.defaultPrevented)return;const r=Me(t.composedPath()[0],L);if(!r)return;const{url:a,external:o,target:i,download:s}=ue(r,U);if(!a)return;if(i==="_parent"||i==="_top"){if(window.parent!==window)return}else if(i&&i!=="_self")return;const c=J(r);if(!(r instanceof SVGAElement)&&a.protocol!==location.protocol&&!(a.protocol==="https:"||a.protocol==="http:")||s)return;const[d,h]=a.href.split("#"),y=d===ce(location);if(o||c.reload&&(!y||!h)){at({url:a,type:"link"})?M=!0:t.preventDefault();return}if(h!==void 0&&y){const[,u]=_.url.href.split("#");if(u===h){if(t.preventDefault(),h===""||h==="top"&&r.ownerDocument.getElementById("top")===null)window.scrollTo({top:0});else{const p=r.ownerDocument.getElementById(decodeURIComponent(h));p&&(p.scrollIntoView(),p.focus())}return}if(V=!0,_e(E),e(a),!c.replace_state)return;V=!1}t.preventDefault(),await new Promise(u=>{requestAnimationFrame(()=>{setTimeout(u,0)}),setTimeout(u,100)}),W({type:"link",url:a,keepfocus:c.keepfocus,noscroll:c.noscroll,replace_state:c.replace_state??a.href===location.href})}),L.addEventListener("submit",t=>{if(t.defaultPrevented)return;const r=HTMLFormElement.prototype.cloneNode.call(t.target),a=t.submitter;if(((a==null?void 0:a.formTarget)||r.target)==="_blank"||((a==null?void 0:a.formMethod)||r.method)!=="get")return;const s=new URL((a==null?void 0:a.hasAttribute("formaction"))&&(a==null?void 0:a.formAction)||r.action);if(ne(s,U))return;const c=t.target,f=J(c);if(f.reload)return;t.preventDefault(),t.stopPropagation();const d=new FormData(c),h=a==null?void 0:a.getAttribute("name");h&&d.append(h,(a==null?void 0:a.getAttribute("value"))??""),s.search=new URLSearchParams(d).toString(),W({type:"form",url:s,keepfocus:f.keepfocus,noscroll:f.noscroll,replace_state:f.replace_state??s.href===location.href})}),addEventListener("popstate",async t=>{var r;if((r=t.state)!=null&&r[j]){const a=t.state[j];if(ee={},a===E)return;const o=C[a],i=t.state[Ge]??{},s=new URL(t.state[Lt]??location.href),c=t.state[q],f=ce(location)===ce(_.url);if(c===T&&(Xe||f)){e(s),C[E]=me(),o&&scrollTo(o.x,o.y),i!==I.state&&(I={...I,state:i},be.$set({page:I})),E=a;return}const h=a-E;await W({type:"popstate",url:s,popped:{state:i,scroll:o,delta:h},accept:()=>{E=a,T=c},block:()=>{history.go(-h)},nav_token:ee})}else if(!V){const a=new URL(location.href);e(a)}}),addEventListener("hashchange",()=>{V&&(V=!1,history.replaceState({...history.state,[j]:++E,[q]:T},"",location.href))});for(const t of document.querySelectorAll("link"))t.rel==="icon"&&(t.href=t.href);addEventListener("pageshow",t=>{t.persisted&&P.navigating.set(null)});function e(t){_.url=t,P.page.set({...I,url:t}),P.page.notify()}}async function tn(e,{status:n=200,error:t,node_ids:r,params:a,route:o,data:i,form:s}){we=!0;const c=new URL(location.href);({params:a={},route:o={id:null}}=Ee(c,!1)||{});let f;try{const d=r.map(async(u,p)=>{const l=i[p];return l!=null&&l.uses&&(l.uses=it(l.uses)),Ae({loader:F.nodes[u],url:c,params:a,route:o,parent:async()=>{const m={};for(let b=0;bu===o.id);if(y){const u=y.layouts;for(let p=0;po?"1":"0").join(""));const r=await $e(t.href);if(!r.ok){let o;throw(a=r.headers.get("content-type"))!=null&&a.includes("application/json")?o=await r.json():r.status===404?o="Not Found":r.status===500&&(o="Internal Error"),new re(r.status,o)}return new Promise(async o=>{var h;const i=new Map,s=r.body.getReader(),c=new TextDecoder;function f(y){return Vt(y,{Promise:u=>new Promise((p,l)=>{i.set(u,{fulfil:p,reject:l})})})}let d="";for(;;){const{done:y,value:u}=await s.read();if(y&&!d)break;for(d+=!u&&d?` -`:c.decode(u,{stream:!0});;){const p=d.indexOf(` -`);if(p===-1)break;const l=JSON.parse(d.slice(0,p));if(d=d.slice(p+1),l.type==="redirect")return o(l);if(l.type==="data")(h=l.nodes)==null||h.forEach(m=>{(m==null?void 0:m.type)==="data"&&(m.uses=it(m.uses),m.data=f(m.data))}),o(l);else if(l.type==="chunk"){const{id:m,data:b,error:S}=l,w=i.get(m);i.delete(m),S?w.reject(f(S)):w.fulfil(f(b))}}}})}function it(e){return{dependencies:new Set((e==null?void 0:e.dependencies)??[]),params:new Set((e==null?void 0:e.params)??[]),parent:!!(e!=null&&e.parent),route:!!(e!=null&&e.route),url:!!(e!=null&&e.url),search_params:new Set((e==null?void 0:e.search_params)??[])}}function nn(){const e=document.querySelector("[autofocus]");if(e)e.focus();else{const n=document.body,t=n.getAttribute("tabindex");n.tabIndex=-1,n.focus({preventScroll:!0,focusVisible:!1}),t!==null?n.setAttribute("tabindex",t):n.removeAttribute("tabindex");const r=getSelection();if(r&&r.type!=="None"){const a=[];for(let o=0;o{if(r.rangeCount===a.length){for(let o=0;o{a=d,o=h});return i.catch(()=>{}),{navigation:{from:{params:e.params,route:{id:((c=e.route)==null?void 0:c.id)??null},url:e.url},to:t&&{params:(n==null?void 0:n.params)??null,route:{id:((f=n==null?void 0:n.route)==null?void 0:f.id)??null},url:t},willUnload:!n,type:r,complete:i},fulfil:a,reject:o}}export{on as a,P as s}; diff --git a/_app/immutable/chunks/entry.BDNlJs-a.js b/_app/immutable/chunks/entry.BDNlJs-a.js new file mode 100644 index 0000000..fda85db --- /dev/null +++ b/_app/immutable/chunks/entry.BDNlJs-a.js @@ -0,0 +1,3 @@ +import{aH as lt}from"./runtime.Dfx-lVnK.js";import{w as pe}from"./index.CO8mGWLE.js";new URL("sveltekit-internal://");function ft(e,n){return e==="/"||n==="ignore"?e:n==="never"?e.endsWith("/")?e.slice(0,-1):e:n==="always"&&!e.endsWith("/")?e+"/":e}function ut(e){return e.split("%25").map(decodeURI).join("%25")}function dt(e){for(const n in e)e[n]=decodeURIComponent(e[n]);return e}function ce({href:e}){return e.split("#")[0]}const ht=["href","pathname","search","toString","toJSON"];function pt(e,n,t){const r=new URL(e);Object.defineProperty(r,"searchParams",{value:new Proxy(r.searchParams,{get(a,o){if(o==="get"||o==="getAll"||o==="has")return s=>(t(s),a[o](s));n();const i=Reflect.get(a,o);return typeof i=="function"?i.bind(a):i}}),enumerable:!0,configurable:!0});for(const a of ht)Object.defineProperty(r,a,{get(){return n(),e[a]},enumerable:!0,configurable:!0});return r}const gt="/__data.json",mt=".html__data.json";function yt(e){return e.endsWith(".html")?e.replace(/\.html$/,mt):e.replace(/\/$/,"")+gt}function _t(...e){let n=5381;for(const t of e)if(typeof t=="string"){let r=t.length;for(;r;)n=n*33^t.charCodeAt(--r)}else if(ArrayBuffer.isView(t)){const r=new Uint8Array(t.buffer,t.byteOffset,t.byteLength);let a=r.length;for(;a;)n=n*33^r[--a]}else throw new TypeError("value must be a string or TypedArray");return(n>>>0).toString(36)}function wt(e){const n=atob(e),t=new Uint8Array(n.length);for(let r=0;r((e instanceof Request?e.method:(n==null?void 0:n.method)||"GET")!=="GET"&&B.delete(ge(e)),$e(e,n));const B=new Map;function vt(e,n){const t=ge(e,n),r=document.querySelector(t);if(r!=null&&r.textContent){let{body:a,...o}=JSON.parse(r.textContent);const i=r.getAttribute("data-ttl");return i&&B.set(t,{body:a,init:o,ttl:1e3*Number(i)}),r.getAttribute("data-b64")!==null&&(a=wt(a)),Promise.resolve(new Response(a,o))}return window.fetch(e,n)}function bt(e,n,t){if(B.size>0){const r=ge(e,t),a=B.get(r);if(a){if(performance.now(){const a=/^\[\.\.\.(\w+)(?:=(\w+))?\]$/.exec(r);if(a)return n.push({name:a[1],matcher:a[2],optional:!1,rest:!0,chained:!0}),"(?:/(.*))?";const o=/^\[\[(\w+)(?:=(\w+))?\]\]$/.exec(r);if(o)return n.push({name:o[1],matcher:o[2],optional:!0,rest:!1,chained:!0}),"(?:/([^/]+))?";if(!r)return;const i=r.split(/\[(.+?)\](?!\])/);return"/"+i.map((c,f)=>{if(f%2){if(c.startsWith("x+"))return le(String.fromCharCode(parseInt(c.slice(2),16)));if(c.startsWith("u+"))return le(String.fromCharCode(...c.slice(2).split("-").map(l=>parseInt(l,16))));const d=At.exec(c),[,h,y,u,p]=d;return n.push({name:u,matcher:p,optional:!!h,rest:!!y,chained:y?f===1&&i[0]==="":!1}),y?"(.*?)":h?"([^/]*)?":"([^/]+?)"}return le(c)}).join("")}).join("")}/?$`),params:n}}function Et(e){return!/^\([^)]+\)$/.test(e)}function St(e){return e.slice(1).split("/").filter(Et)}function Rt(e,n,t){const r={},a=e.slice(1),o=a.filter(s=>s!==void 0);let i=0;for(let s=0;sd).join("/"),i=0),f===void 0){c.rest&&(r[c.name]="");continue}if(!c.matcher||t[c.matcher](f)){r[c.name]=f;const d=n[s+1],h=a[s+1];d&&!d.rest&&d.optional&&h&&c.chained&&(i=0),!d&&!h&&Object.keys(r).length===o.length&&(i=0);continue}if(c.optional&&c.chained){i++;continue}return}if(!i)return r}function le(e){return e.normalize().replace(/[[\]]/g,"\\$&").replace(/%/g,"%25").replace(/\//g,"%2[Ff]").replace(/\?/g,"%3[Ff]").replace(/#/g,"%23").replace(/[.*+?^${}()|\\]/g,"\\$&")}function It({nodes:e,server_loads:n,dictionary:t,matchers:r}){const a=new Set(n);return Object.entries(t).map(([s,[c,f,d]])=>{const{pattern:h,params:y}=kt(s),u={id:s,exec:p=>{const l=h.exec(p);if(l)return Rt(l,y,r)},errors:[1,...d||[]].map(p=>e[p]),layouts:[0,...f||[]].map(i),leaf:o(c)};return u.errors.length=u.layouts.length=Math.max(u.errors.length,u.layouts.length),u});function o(s){const c=s<0;return c&&(s=~s),[c,e[s]]}function i(s){return s===void 0?s:[a.has(s),e[s]]}}function Fe(e,n=JSON.parse){try{return n(sessionStorage[e])}catch{}}function Ie(e,n,t=JSON.stringify){const r=t(n);try{sessionStorage[e]=r}catch{}}var je;const T=((je=globalThis.__sveltekit_14xt6cx)==null?void 0:je.base)??"";var De;const xt=((De=globalThis.__sveltekit_14xt6cx)==null?void 0:De.assets)??T,Tt="1733342258019",Ve="sveltekit:snapshot",Be="sveltekit:scroll",Ge="sveltekit:states",Ut="sveltekit:pageurl",j="sveltekit:history",q="sveltekit:navigation",Y={tap:1,hover:2,viewport:3,eager:4,off:-1,false:-1},K=location.origin;function qe(e){if(e instanceof URL)return e;let n=document.baseURI;if(!n){const t=document.getElementsByTagName("base");n=t.length?t[0].href:document.URL}return new URL(e,n)}function me(){return{x:pageXOffset,y:pageYOffset}}function N(e,n){return e.getAttribute(`data-sveltekit-${n}`)}const xe={...Y,"":Y.hover};function He(e){let n=e.assignedSlot??e.parentNode;return(n==null?void 0:n.nodeType)===11&&(n=n.host),n}function Me(e,n){for(;e&&e!==n;){if(e.nodeName.toUpperCase()==="A"&&e.hasAttribute("href"))return e;e=He(e)}}function ue(e,n){let t;try{t=new URL(e instanceof SVGAElement?e.href.baseVal:e.href,document.baseURI)}catch{}const r=e instanceof SVGAElement?e.target.baseVal:e.target,a=!t||!!r||ne(t,n)||(e.getAttribute("rel")||"").split(/\s+/).includes("external"),o=(t==null?void 0:t.origin)===K&&e.hasAttribute("download");return{url:t,external:a,target:r,download:o}}function J(e){let n=null,t=null,r=null,a=null,o=null,i=null,s=e;for(;s&&s!==document.documentElement;)r===null&&(r=N(s,"preload-code")),a===null&&(a=N(s,"preload-data")),n===null&&(n=N(s,"keepfocus")),t===null&&(t=N(s,"noscroll")),o===null&&(o=N(s,"reload")),i===null&&(i=N(s,"replacestate")),s=He(s);function c(f){switch(f){case"":case"true":return!0;case"off":case"false":return!1;default:return}}return{preload_code:xe[r??"off"],preload_data:xe[a??"off"],keepfocus:c(n),noscroll:c(t),reload:c(o),replace_state:c(i)}}function Te(e){const n=pe(e);let t=!0;function r(){t=!0,n.update(i=>i)}function a(i){t=!1,n.set(i)}function o(i){let s;return n.subscribe(c=>{(s===void 0||t&&c!==s)&&i(s=c)})}return{notify:r,set:a,subscribe:o}}function Lt(){const{set:e,subscribe:n}=pe(!1);let t;async function r(){clearTimeout(t);try{const a=await fetch(`${xt}/_app/version.json`,{headers:{pragma:"no-cache","cache-control":"no-cache"}});if(!a.ok)return!1;const i=(await a.json()).version!==Tt;return i&&(e(!0),clearTimeout(t)),i}catch{return!1}}return{subscribe:n,check:r}}function ne(e,n){return e.origin!==K||!e.pathname.startsWith(n)}function Ue(e){const n=Ct(e),t=new ArrayBuffer(n.length),r=new DataView(t);for(let a=0;a>16),n+=String.fromCharCode((t&65280)>>8),n+=String.fromCharCode(t&255),t=r=0);return r===12?(t>>=4,n+=String.fromCharCode(t)):r===18&&(t>>=2,n+=String.fromCharCode((t&65280)>>8),n+=String.fromCharCode(t&255)),n}const Nt=-1,Ot=-2,jt=-3,Dt=-4,$t=-5,Ft=-6;function Vt(e,n){if(typeof e=="number")return a(e,!0);if(!Array.isArray(e)||e.length===0)throw new Error("Invalid input");const t=e,r=Array(t.length);function a(o,i=!1){if(o===Nt)return;if(o===jt)return NaN;if(o===Dt)return 1/0;if(o===$t)return-1/0;if(o===Ft)return-0;if(i)throw new Error("Invalid input");if(o in r)return r[o];const s=t[o];if(!s||typeof s!="object")r[o]=s;else if(Array.isArray(s))if(typeof s[0]=="string"){const c=s[0],f=n==null?void 0:n[c];if(f)return r[o]=f(a(s[1]));switch(c){case"Date":r[o]=new Date(s[1]);break;case"Set":const d=new Set;r[o]=d;for(let u=1;un!=null)}class re{constructor(n,t){this.status=n,typeof t=="string"?this.body={message:t}:t?this.body=t:this.body={message:`Error: ${n}`}}toString(){return JSON.stringify(this.body)}}class We{constructor(n,t){this.status=n,this.location=t}}class ye extends Error{constructor(n,t,r){super(r),this.status=n,this.text=t}}const qt="x-sveltekit-invalidated",Ht="x-sveltekit-trailing-slash";function z(e){return e instanceof re||e instanceof ye?e.status:500}function Mt(e){return e instanceof ye?e.text:"Internal Error"}const C=Fe(Be)??{},H=Fe(Ve)??{},L={url:Te({}),page:Te({}),navigating:pe(null),updated:Lt()};function _e(e){C[e]=me()}function Kt(e,n){let t=e+1;for(;C[t];)delete C[t],t+=1;for(t=n+1;H[t];)delete H[t],t+=1}function $(e){return location.href=e.href,new Promise(()=>{})}async function Ye(){if("serviceWorker"in navigator){const e=await navigator.serviceWorker.getRegistration(T||"/");e&&await e.update()}}function Le(){}let ae,de,X,U,he,F;const Je=[],Z=[];let R=null;const ze=[],Wt=[];let O=[],_={branch:[],error:null,url:null},we=!1,Q=!1,Pe=!0,M=!1,V=!1,Xe=!1,ve=!1,be,E,x,I,ee;const G=new Set;async function on(e,n,t){var a,o;document.URL!==location.href&&(location.href=location.href),F=e,ae=It(e),U=document.documentElement,he=n,de=e.nodes[0],X=e.nodes[1],de(),X(),E=(a=history.state)==null?void 0:a[j],x=(o=history.state)==null?void 0:o[q],E||(E=x=Date.now(),history.replaceState({...history.state,[j]:E,[q]:x},""));const r=C[E];r&&(history.scrollRestoration="manual",scrollTo(r.x,r.y)),t?await tn(he,t):Qt(location.href,{replaceState:!0}),en()}function Yt(){Je.length=0,ve=!1}function Ze(e){Z.some(n=>n==null?void 0:n.snapshot)&&(H[e]=Z.map(n=>{var t;return(t=n==null?void 0:n.snapshot)==null?void 0:t.capture()}))}function Qe(e){var n;(n=H[e])==null||n.forEach((t,r)=>{var a,o;(o=(a=Z[r])==null?void 0:a.snapshot)==null||o.restore(t)})}function Ce(){_e(E),Ie(Be,C),Ze(x),Ie(Ve,H)}async function et(e,n,t,r){return W({type:"goto",url:qe(e),keepfocus:n.keepFocus,noscroll:n.noScroll,replace_state:n.replaceState,state:n.state,redirect_count:t,nav_token:r,accept:()=>{n.invalidateAll&&(ve=!0)}})}async function Jt(e){if(e.id!==(R==null?void 0:R.id)){const n={};G.add(n),R={id:e.id,token:n,promise:nt({...e,preload:n}).then(t=>(G.delete(n),t.type==="loaded"&&t.state.error&&(R=null),t))}}return R.promise}async function fe(e){const n=ae.find(t=>t.exec(rt(e)));n&&await Promise.all([...n.layouts,n.leaf].map(t=>t==null?void 0:t[1]()))}function tt(e,n,t){var o;_=e.state;const r=document.querySelector("style[data-sveltekit]");r&&r.remove(),I=e.props.page,be=new F.root({target:n,props:{...e.props,stores:L,components:Z},hydrate:t,sync:!1}),Qe(x);const a={from:null,to:{params:_.params,route:{id:((o=_.route)==null?void 0:o.id)??null},url:new URL(location.href)},willUnload:!1,type:"enter",complete:Promise.resolve()};O.forEach(i=>i(a)),Q=!0}function te({url:e,params:n,branch:t,status:r,error:a,route:o,form:i}){let s="never";if(T&&(e.pathname===T||e.pathname===T+"/"))s="always";else for(const u of t)(u==null?void 0:u.slash)!==void 0&&(s=u.slash);e.pathname=ft(e.pathname,s),e.search=e.search;const c={type:"loaded",state:{url:e,params:n,branch:t,error:a,route:o},props:{constructors:Gt(t).map(u=>u.node.component),page:I}};i!==void 0&&(c.props.form=i);let f={},d=!I,h=0;for(let u=0;u(s&&(c.route=!0),l[m])}),params:new Proxy(r,{get:(l,m)=>(s&&c.params.add(m),l[m])}),data:(o==null?void 0:o.data)??null,url:pt(t,()=>{s&&(c.url=!0)},l=>{s&&c.search_params.add(l)}),async fetch(l,m){let b;l instanceof Request?(b=l.url,m={body:l.method==="GET"||l.method==="HEAD"?void 0:await l.blob(),cache:l.cache,credentials:l.credentials,headers:l.headers,integrity:l.integrity,keepalive:l.keepalive,method:l.method,mode:l.mode,redirect:l.redirect,referrer:l.referrer,referrerPolicy:l.referrerPolicy,signal:l.signal,...m}):b=l;const S=new URL(b,t);return s&&u(S.href),S.origin===t.origin&&(b=S.href.slice(t.origin.length)),Q?bt(b,S.href,m):vt(b,m)},setHeaders:()=>{},depends:u,parent(){return s&&(c.parent=!0),n()},untrack(l){s=!1;try{return l()}finally{s=!0}}};i=await f.universal.load.call(null,p)??null}return{node:f,loader:e,server:o,universal:(h=f.universal)!=null&&h.load?{type:"data",data:i,uses:c}:null,data:i??(o==null?void 0:o.data)??null,slash:((y=f.universal)==null?void 0:y.trailingSlash)??(o==null?void 0:o.slash)}}function Ne(e,n,t,r,a,o){if(ve)return!0;if(!a)return!1;if(a.parent&&e||a.route&&n||a.url&&t)return!0;for(const i of a.search_params)if(r.has(i))return!0;for(const i of a.params)if(o[i]!==_.params[i])return!0;for(const i of a.dependencies)if(Je.some(s=>s(new URL(i))))return!0;return!1}function ke(e,n){return(e==null?void 0:e.type)==="data"?e:(e==null?void 0:e.type)==="skip"?n??null:null}function zt(e,n){if(!e)return new Set(n.searchParams.keys());const t=new Set([...e.searchParams.keys(),...n.searchParams.keys()]);for(const r of t){const a=e.searchParams.getAll(r),o=n.searchParams.getAll(r);a.every(i=>o.includes(i))&&o.every(i=>a.includes(i))&&t.delete(r)}return t}function Oe({error:e,url:n,route:t,params:r}){return{type:"loaded",state:{error:e,url:n,route:t,params:r,branch:[]},props:{page:I,constructors:[]}}}async function nt({id:e,invalidating:n,url:t,params:r,route:a,preload:o}){if((R==null?void 0:R.id)===e)return G.delete(R.token),R.promise;const{errors:i,layouts:s,leaf:c}=a,f=[...s,c];i.forEach(g=>g==null?void 0:g().catch(()=>{})),f.forEach(g=>g==null?void 0:g[1]().catch(()=>{}));let d=null;const h=_.url?e!==_.url.pathname+_.url.search:!1,y=_.route?a.id!==_.route.id:!1,u=zt(_.url,t);let p=!1;const l=f.map((g,v)=>{var P;const A=_.branch[v],k=!!(g!=null&&g[0])&&((A==null?void 0:A.loader)!==g[1]||Ne(p,y,h,u,(P=A.server)==null?void 0:P.uses,r));return k&&(p=!0),k});if(l.some(Boolean)){try{d=await st(t,l)}catch(g){const v=await D(g,{url:t,params:r,route:{id:e}});return G.has(o)?Oe({error:v,url:t,params:r,route:a}):oe({status:z(g),error:v,url:t,route:a})}if(d.type==="redirect")return d}const m=d==null?void 0:d.nodes;let b=!1;const S=f.map(async(g,v)=>{var se;if(!g)return;const A=_.branch[v],k=m==null?void 0:m[v];if((!k||k.type==="skip")&&g[1]===(A==null?void 0:A.loader)&&!Ne(b,y,h,u,(se=A.universal)==null?void 0:se.uses,r))return A;if(b=!0,(k==null?void 0:k.type)==="error")throw k;return Ae({loader:g[1],url:t,params:r,route:a,parent:async()=>{var Re;const Se={};for(let ie=0;ie{});const w=[];for(let g=0;gPromise.resolve({}),server_data_node:ke(o)}),c={node:await X(),loader:X,universal:null,server:null,data:null};return te({url:t,params:a,branch:[s,c],status:e,error:n,route:null})}function Ee(e,n){if(!e||ne(e,T))return;let t;try{t=F.hooks.reroute({url:new URL(e)})??e.pathname}catch{return}const r=rt(t);for(const a of ae){const o=a.exec(r);if(o)return{id:e.pathname+e.search,invalidating:n,route:a,params:dt(o),url:e}}}function rt(e){return ut(e.slice(T.length)||"/")}function at({url:e,type:n,intent:t,delta:r}){let a=!1;const o=ct(_,t,e,n);r!==void 0&&(o.navigation.delta=r);const i={...o.navigation,cancel:()=>{a=!0,o.reject(new Error("navigation cancelled"))}};return M||ze.forEach(s=>s(i)),a?null:o}async function W({type:e,url:n,popped:t,keepfocus:r,noscroll:a,replace_state:o,state:i={},redirect_count:s=0,nav_token:c={},accept:f=Le,block:d=Le}){const h=Ee(n,!1),y=at({url:n,type:e,delta:t==null?void 0:t.delta,intent:h});if(!y){d();return}const u=E,p=x;f(),M=!0,Q&&L.navigating.set(y.navigation),ee=c;let l=h&&await nt(h);if(!l){if(ne(n,T))return await $(n);l=await ot(n,{id:null},await D(new ye(404,"Not Found",`Not found: ${n.pathname}`),{url:n,params:{},route:{id:null}}),404)}if(n=(h==null?void 0:h.url)||n,ee!==c)return y.reject(new Error("navigation aborted")),!1;if(l.type==="redirect")if(s>=20)l=await oe({status:500,error:await D(new Error("Redirect loop"),{url:n,params:{},route:{id:null}}),url:n,route:{id:null}});else return et(new URL(l.location,n).href,{},s+1,c),!1;else l.props.page.status>=400&&await L.updated.check()&&(await Ye(),await $(n));if(Yt(),_e(u),Ze(p),l.props.page.url.pathname!==n.pathname&&(n.pathname=l.props.page.url.pathname),i=t?t.state:i,!t){const w=o?0:1,g={[j]:E+=w,[q]:x+=w,[Ge]:i};(o?history.replaceState:history.pushState).call(history,g,"",n),o||Kt(E,x)}if(R=null,l.props.page.state=i,Q){_=l.state,l.props.page&&(l.props.page.url=n);const w=(await Promise.all(Wt.map(g=>g(y.navigation)))).filter(g=>typeof g=="function");if(w.length>0){let g=function(){O=O.filter(v=>!w.includes(v))};w.push(g),O.push(...w)}be.$set(l.props),Xe=!0}else tt(l,he,!1);const{activeElement:m}=document;await lt();const b=t?t.scroll:a?me():null;if(Pe){const w=n.hash&&document.getElementById(decodeURIComponent(n.hash.slice(1)));b?scrollTo(b.x,b.y):w?w.scrollIntoView():scrollTo(0,0)}const S=document.activeElement!==m&&document.activeElement!==document.body;!r&&!S&&nn(),Pe=!0,l.props.page&&(I=l.props.page),M=!1,e==="popstate"&&Qe(x),y.fulfil(void 0),O.forEach(w=>w(y.navigation)),L.navigating.set(null)}async function ot(e,n,t,r){return e.origin===K&&e.pathname===location.pathname&&!we?await oe({status:r,error:t,url:e,route:n}):await $(e)}function Zt(){let e;U.addEventListener("mousemove",o=>{const i=o.target;clearTimeout(e),e=setTimeout(()=>{r(i,2)},20)});function n(o){o.defaultPrevented||r(o.composedPath()[0],1)}U.addEventListener("mousedown",n),U.addEventListener("touchstart",n,{passive:!0});const t=new IntersectionObserver(o=>{for(const i of o)i.isIntersecting&&(fe(i.target.href),t.unobserve(i.target))},{threshold:0});function r(o,i){const s=Me(o,U);if(!s)return;const{url:c,external:f,download:d}=ue(s,T);if(f||d)return;const h=J(s),y=c&&_.url.pathname+_.url.search===c.pathname+c.search;if(!h.reload&&!y)if(i<=h.preload_data){const u=Ee(c,!1);u&&Jt(u)}else i<=h.preload_code&&fe(c.pathname)}function a(){t.disconnect();for(const o of U.querySelectorAll("a")){const{url:i,external:s,download:c}=ue(o,T);if(s||c)continue;const f=J(o);f.reload||(f.preload_code===Y.viewport&&t.observe(o),f.preload_code===Y.eager&&fe(i.pathname))}}O.push(a),a()}function D(e,n){if(e instanceof re)return e.body;const t=z(e),r=Mt(e);return F.hooks.handleError({error:e,event:n,status:t,message:r})??{message:r}}function Qt(e,n={}){return e=qe(e),e.origin!==K?Promise.reject(new Error("goto: invalid URL")):et(e,n,0)}function en(){var n;history.scrollRestoration="manual",addEventListener("beforeunload",t=>{let r=!1;if(Ce(),!M){const a=ct(_,void 0,null,"leave"),o={...a.navigation,cancel:()=>{r=!0,a.reject(new Error("navigation cancelled"))}};ze.forEach(i=>i(o))}r?(t.preventDefault(),t.returnValue=""):history.scrollRestoration="auto"}),addEventListener("visibilitychange",()=>{document.visibilityState==="hidden"&&Ce()}),(n=navigator.connection)!=null&&n.saveData||Zt(),U.addEventListener("click",async t=>{if(t.button||t.which!==1||t.metaKey||t.ctrlKey||t.shiftKey||t.altKey||t.defaultPrevented)return;const r=Me(t.composedPath()[0],U);if(!r)return;const{url:a,external:o,target:i,download:s}=ue(r,T);if(!a)return;if(i==="_parent"||i==="_top"){if(window.parent!==window)return}else if(i&&i!=="_self")return;const c=J(r);if(!(r instanceof SVGAElement)&&a.protocol!==location.protocol&&!(a.protocol==="https:"||a.protocol==="http:")||s)return;const[d,h]=a.href.split("#"),y=d===ce(location);if(o||c.reload&&(!y||!h)){at({url:a,type:"link"})?M=!0:t.preventDefault();return}if(h!==void 0&&y){const[,u]=_.url.href.split("#");if(u===h){if(t.preventDefault(),h===""||h==="top"&&r.ownerDocument.getElementById("top")===null)window.scrollTo({top:0});else{const p=r.ownerDocument.getElementById(decodeURIComponent(h));p&&(p.scrollIntoView(),p.focus())}return}if(V=!0,_e(E),e(a),!c.replace_state)return;V=!1}t.preventDefault(),await new Promise(u=>{requestAnimationFrame(()=>{setTimeout(u,0)}),setTimeout(u,100)}),W({type:"link",url:a,keepfocus:c.keepfocus,noscroll:c.noscroll,replace_state:c.replace_state??a.href===location.href})}),U.addEventListener("submit",t=>{if(t.defaultPrevented)return;const r=HTMLFormElement.prototype.cloneNode.call(t.target),a=t.submitter;if(((a==null?void 0:a.formTarget)||r.target)==="_blank"||((a==null?void 0:a.formMethod)||r.method)!=="get")return;const s=new URL((a==null?void 0:a.hasAttribute("formaction"))&&(a==null?void 0:a.formAction)||r.action);if(ne(s,T))return;const c=t.target,f=J(c);if(f.reload)return;t.preventDefault(),t.stopPropagation();const d=new FormData(c),h=a==null?void 0:a.getAttribute("name");h&&d.append(h,(a==null?void 0:a.getAttribute("value"))??""),s.search=new URLSearchParams(d).toString(),W({type:"form",url:s,keepfocus:f.keepfocus,noscroll:f.noscroll,replace_state:f.replace_state??s.href===location.href})}),addEventListener("popstate",async t=>{var r;if((r=t.state)!=null&&r[j]){const a=t.state[j];if(ee={},a===E)return;const o=C[a],i=t.state[Ge]??{},s=new URL(t.state[Ut]??location.href),c=t.state[q],f=ce(location)===ce(_.url);if(c===x&&(Xe||f)){e(s),C[E]=me(),o&&scrollTo(o.x,o.y),i!==I.state&&(I={...I,state:i},be.$set({page:I})),E=a;return}const h=a-E;await W({type:"popstate",url:s,popped:{state:i,scroll:o,delta:h},accept:()=>{E=a,x=c},block:()=>{history.go(-h)},nav_token:ee})}else if(!V){const a=new URL(location.href);e(a)}}),addEventListener("hashchange",()=>{V&&(V=!1,history.replaceState({...history.state,[j]:++E,[q]:x},"",location.href))});for(const t of document.querySelectorAll("link"))t.rel==="icon"&&(t.href=t.href);addEventListener("pageshow",t=>{t.persisted&&L.navigating.set(null)});function e(t){_.url=t,L.page.set({...I,url:t}),L.page.notify()}}async function tn(e,{status:n=200,error:t,node_ids:r,params:a,route:o,data:i,form:s}){we=!0;const c=new URL(location.href);({params:a={},route:o={id:null}}=Ee(c,!1)||{});let f;try{const d=r.map(async(u,p)=>{const l=i[p];return l!=null&&l.uses&&(l.uses=it(l.uses)),Ae({loader:F.nodes[u],url:c,params:a,route:o,parent:async()=>{const m={};for(let b=0;bu===o.id);if(y){const u=y.layouts;for(let p=0;po?"1":"0").join(""));const r=await $e(t.href);if(!r.ok){let o;throw(a=r.headers.get("content-type"))!=null&&a.includes("application/json")?o=await r.json():r.status===404?o="Not Found":r.status===500&&(o="Internal Error"),new re(r.status,o)}return new Promise(async o=>{var h;const i=new Map,s=r.body.getReader(),c=new TextDecoder;function f(y){return Vt(y,{Promise:u=>new Promise((p,l)=>{i.set(u,{fulfil:p,reject:l})})})}let d="";for(;;){const{done:y,value:u}=await s.read();if(y&&!d)break;for(d+=!u&&d?` +`:c.decode(u,{stream:!0});;){const p=d.indexOf(` +`);if(p===-1)break;const l=JSON.parse(d.slice(0,p));if(d=d.slice(p+1),l.type==="redirect")return o(l);if(l.type==="data")(h=l.nodes)==null||h.forEach(m=>{(m==null?void 0:m.type)==="data"&&(m.uses=it(m.uses),m.data=f(m.data))}),o(l);else if(l.type==="chunk"){const{id:m,data:b,error:S}=l,w=i.get(m);i.delete(m),S?w.reject(f(S)):w.fulfil(f(b))}}}})}function it(e){return{dependencies:new Set((e==null?void 0:e.dependencies)??[]),params:new Set((e==null?void 0:e.params)??[]),parent:!!(e!=null&&e.parent),route:!!(e!=null&&e.route),url:!!(e!=null&&e.url),search_params:new Set((e==null?void 0:e.search_params)??[])}}function nn(){const e=document.querySelector("[autofocus]");if(e)e.focus();else{const n=document.body,t=n.getAttribute("tabindex");n.tabIndex=-1,n.focus({preventScroll:!0,focusVisible:!1}),t!==null?n.setAttribute("tabindex",t):n.removeAttribute("tabindex");const r=getSelection();if(r&&r.type!=="None"){const a=[];for(let o=0;o{if(r.rangeCount===a.length){for(let o=0;o{a=d,o=h});return i.catch(()=>{}),{navigation:{from:{params:e.params,route:{id:((c=e.route)==null?void 0:c.id)??null},url:e.url},to:t&&{params:(n==null?void 0:n.params)??null,route:{id:((f=n==null?void 0:n.route)==null?void 0:f.id)??null},url:t},willUnload:!n,type:r,complete:i},fulfil:a,reject:o}}export{on as a,L as s}; diff --git a/_app/immutable/entry/app.DVk21-Gt.js b/_app/immutable/entry/app.ByzdQ3zT.js similarity index 74% rename from _app/immutable/entry/app.DVk21-Gt.js rename to _app/immutable/entry/app.ByzdQ3zT.js index e4c88fa..0a94526 100644 --- a/_app/immutable/entry/app.DVk21-Gt.js +++ b/_app/immutable/entry/app.ByzdQ3zT.js @@ -1,2 +1,2 @@ -const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["../nodes/0.-ewE0Ojq.js","../chunks/disclose-version.jbUlswzs.js","../chunks/runtime.Dfx-lVnK.js","../nodes/1.wGGJZu2n.js","../chunks/legacy.CRkFLlqN.js","../chunks/render.C1kgis0d.js","../chunks/store.CE1T5q5i.js","../chunks/lifecycle.kgqH09T4.js","../chunks/entry.B8mbJhC8.js","../chunks/index.CO8mGWLE.js","../nodes/2.Bxop8xDV.js","../chunks/if.BRDm7pt5.js","../chunks/style.BqiTLypu.js","../chunks/attributes.Dvuu1p9u.js","../chunks/props.C9XHSajK.js","../chunks/Layout.B6tzzdmS.js","../chunks/html.DJwC5DVL.js","../chunks/Frame.svelte_svelte_type_style_lang.CiKUY8ht.js","../assets/Frame.Ch5Xis14.css","../chunks/index-client.Ck4GHNqj.js","../chunks/each.psv4IetE.js","../assets/Layout.C0MFVVOY.css","../assets/2.CiemoY37.css","../nodes/3.BOJoUU7x.js","../chunks/ArticleFeed.BnZogVUb.js","../assets/ArticleFeed.COlA-k9G.css","../nodes/4.CFIMFROb.js","../chunks/Image.CorzdUlf.js","../chunks/this.l8DE5kMR.js","../assets/Image.DiB1YRod.css","../chunks/Figure.DrAK28J1.js","../assets/Figure.D7C6L14i.css","../nodes/5.OswBxykr.js","../nodes/6.uWhwuCeF.js","../nodes/7.gsEXZ4EK.js","../chunks/Video.DdNmxp5V.js","../assets/Video.C2BkDKz5.css","../nodes/8.HXC27mj2.js","../nodes/9.DkYBbSqf.js","../nodes/10.CmSU0U-5.js","../assets/10.jxrO33K0.css","../nodes/11.BvPbAE-y.js","../nodes/12.Ca8uRgGS.js","../nodes/13.CNpzwRjO.js","../chunks/Link.Ckijf6nQ.js","../nodes/14.BKJfGLGD.js","../nodes/15.HAXP-y8O.js","../nodes/16.BFe6OVRm.js"])))=>i.map(i=>d[i]); -var z=o=>{throw TypeError(o)};var J=(o,t,e)=>t.has(o)||z("Cannot "+e);var u=(o,t,e)=>(J(o,t,"read from private field"),e?e.call(o):t.get(o)),N=(o,t,e)=>t.has(o)?z("Cannot add the same private member more than once"):t instanceof WeakSet?t.add(o):t.set(o,e),U=(o,t,e,s)=>(J(o,t,"write to private field"),s?s.call(o,e):t.set(o,e),e);import{h as K,g as rt,d as at,a0 as ot,i as st,j as nt,N as it,a4 as g,as as ct,ah as S,aG as lt,aF as ut,X as mt,p as _t,a1 as dt,a2 as ft,aH as ht,f as p,s as vt,a as gt,aI as H,c as Et,t as yt,r as Pt,a7 as T}from"../chunks/runtime.Dfx-lVnK.js";import{h as Rt,m as pt,u as bt,s as At}from"../chunks/render.C1kgis0d.js";import{d as A,a as y,t as Z,e as Lt}from"../chunks/disclose-version.jbUlswzs.js";import{i as k}from"../chunks/if.BRDm7pt5.js";import{p as x,a as Ot}from"../chunks/props.C9XHSajK.js";import{b as w}from"../chunks/this.l8DE5kMR.js";import{o as Tt}from"../chunks/index-client.Ck4GHNqj.js";function I(o,t,e){K&&rt();var s=o,n,m;at(()=>{n!==(n=t())&&(m&&(it(m),m=null),n&&(m=st(()=>e(s,n))))},ot),K&&(s=nt)}function wt(o){return class extends It{constructor(t){super({component:o,...t})}}}var P,d;class It{constructor(t){N(this,P);N(this,d);var m;var e=new Map,s=(a,r)=>{var _=mt(r);return e.set(a,_),_};const n=new Proxy({...t.props||{},$$events:{}},{get(a,r){return g(e.get(r)??s(r,Reflect.get(a,r)))},has(a,r){return r===ct?!0:(g(e.get(r)??s(r,Reflect.get(a,r))),Reflect.has(a,r))},set(a,r,_){return S(e.get(r)??s(r,_),_),Reflect.set(a,r,_)}});U(this,d,(t.hydrate?Rt:pt)(t.component,{target:t.target,anchor:t.anchor,props:n,context:t.context,intro:t.intro??!1,recover:t.recover})),(!((m=t==null?void 0:t.props)!=null&&m.$$host)||t.sync===!1)&<(),U(this,P,n.$$events);for(const a of Object.keys(u(this,d)))a==="$set"||a==="$destroy"||a==="$on"||ut(this,a,{get(){return u(this,d)[a]},set(r){u(this,d)[a]=r},enumerable:!0});u(this,d).$set=a=>{Object.assign(n,a)},u(this,d).$destroy=()=>{bt(u(this,d))}}$set(t){u(this,d).$set(t)}$on(t,e){u(this,P)[t]=u(this,P)[t]||[];const s=(...n)=>e.call(this,...n);return u(this,P)[t].push(s),()=>{u(this,P)[t]=u(this,P)[t].filter(n=>n!==s)}}$destroy(){u(this,d).$destroy()}}P=new WeakMap,d=new WeakMap;const Dt="modulepreload",Vt=function(o,t){return new URL(o,t).href},Q={},i=function(t,e,s){let n=Promise.resolve();if(e&&e.length>0){const a=document.getElementsByTagName("link"),r=document.querySelector("meta[property=csp-nonce]"),_=(r==null?void 0:r.nonce)||(r==null?void 0:r.getAttribute("nonce"));n=Promise.allSettled(e.map(f=>{if(f=Vt(f,s),f in Q)return;Q[f]=!0;const R=f.endsWith(".css"),D=R?'[rel="stylesheet"]':"";if(!!s)for(let l=a.length-1;l>=0;l--){const E=a[l];if(E.href===f&&(!R||E.rel==="stylesheet"))return}else if(document.querySelector(`link[href="${f}"]${D}`))return;const c=document.createElement("link");if(c.rel=R?"stylesheet":Dt,R||(c.as="script"),c.crossOrigin="",c.href=f,_&&c.setAttribute("nonce",_),document.head.appendChild(c),R)return new Promise((l,E)=>{c.addEventListener("load",l),c.addEventListener("error",()=>E(new Error(`Unable to preload CSS for ${f}`)))})}))}function m(a){const r=new Event("vite:preloadError",{cancelable:!0});if(r.payload=a,window.dispatchEvent(r),!r.defaultPrevented)throw a}return n.then(a=>{for(const r of a||[])r.status==="rejected"&&m(r.reason);return t().catch(m)})},Xt={};var kt=Z('
'),xt=Z(" ",1);function St(o,t){_t(t,!0);let e=x(t,"components",23,()=>[]),s=x(t,"data_0",3,null),n=x(t,"data_1",3,null),m=x(t,"data_2",3,null);dt(()=>t.stores.page.set(t.page)),ft(()=>{t.stores,t.page,t.constructors,e(),t.form,s(),n(),m(),t.stores.page.notify()});let a=H(!1),r=H(!1),_=H(null);Tt(()=>{const c=t.stores.page.subscribe(()=>{g(a)&&(S(r,!0),ht().then(()=>{S(_,Ot(document.title||"untitled page"))}))});return S(a,!0),c});const f=T(()=>t.constructors[2]);var R=xt(),D=p(R);k(D,()=>t.constructors[1],c=>{var l=A();const E=T(()=>t.constructors[0]);var L=p(l);I(L,()=>g(E),(b,C)=>{w(C(b,{get data(){return s()},get form(){return t.form},children:(h,Ct)=>{var X=A(),M=p(X);k(M,()=>t.constructors[2],j=>{var O=A();const B=T(()=>t.constructors[1]);var F=p(O);I(F,()=>g(B),(q,G)=>{w(G(q,{get data(){return n()},get form(){return t.form},children:(v,jt)=>{var Y=A(),$=p(Y);I($,()=>g(f),(tt,et)=>{w(et(tt,{get data(){return m()},get form(){return t.form}}),V=>e()[2]=V,()=>{var V;return(V=e())==null?void 0:V[2]})}),y(v,Y)},$$slots:{default:!0}}),v=>e()[1]=v,()=>{var v;return(v=e())==null?void 0:v[1]})}),y(j,O)},j=>{var O=A();const B=T(()=>t.constructors[1]);var F=p(O);I(F,()=>g(B),(q,G)=>{w(G(q,{get data(){return n()},get form(){return t.form}}),v=>e()[1]=v,()=>{var v;return(v=e())==null?void 0:v[1]})}),y(j,O)}),y(h,X)},$$slots:{default:!0}}),h=>e()[0]=h,()=>{var h;return(h=e())==null?void 0:h[0]})}),y(c,l)},c=>{var l=A();const E=T(()=>t.constructors[0]);var L=p(l);I(L,()=>g(E),(b,C)=>{w(C(b,{get data(){return s()},get form(){return t.form}}),h=>e()[0]=h,()=>{var h;return(h=e())==null?void 0:h[0]})}),y(c,l)});var W=vt(D,2);k(W,()=>g(a),c=>{var l=kt(),E=Et(l);k(E,()=>g(r),L=>{var b=Lt();yt(()=>At(b,g(_))),y(L,b)}),Pt(l),y(c,l)}),y(o,R),gt()}const Yt=wt(St),zt=[()=>i(()=>import("../nodes/0.-ewE0Ojq.js"),__vite__mapDeps([0,1,2]),import.meta.url),()=>i(()=>import("../nodes/1.wGGJZu2n.js"),__vite__mapDeps([3,1,2,4,5,6,7,8,9]),import.meta.url),()=>i(()=>import("../nodes/2.Bxop8xDV.js"),__vite__mapDeps([10,1,2,4,5,6,11,12,13,7,14,15,16,17,9,18,19,20,21,22]),import.meta.url),()=>i(()=>import("../nodes/3.BOJoUU7x.js"),__vite__mapDeps([23,1,2,4,7,14,6,15,5,16,12,17,9,18,11,19,20,13,21,24,25]),import.meta.url),()=>i(()=>import("../nodes/4.CFIMFROb.js"),__vite__mapDeps([26,1,2,4,27,11,13,6,7,14,12,28,20,29,30,17,9,18,31]),import.meta.url),()=>i(()=>import("../nodes/5.OswBxykr.js"),__vite__mapDeps([32,1,2,4,27,11,13,6,7,14,12,28,20,29,30,17,9,18,31]),import.meta.url),()=>i(()=>import("../nodes/6.uWhwuCeF.js"),__vite__mapDeps([33,1,2,4,27,11,13,6,7,14,12,28,20,29,30,17,9,18,31]),import.meta.url),()=>i(()=>import("../nodes/7.gsEXZ4EK.js"),__vite__mapDeps([34,1,2,4,27,11,13,6,7,14,12,28,20,29,35,36,30,17,9,18,31]),import.meta.url),()=>i(()=>import("../nodes/8.HXC27mj2.js"),__vite__mapDeps([37,1,2,4,16,27,11,13,6,7,14,12,28,20,29,30,17,9,18,31]),import.meta.url),()=>i(()=>import("../nodes/9.DkYBbSqf.js"),__vite__mapDeps([38,1,2,4,27,11,13,6,7,14,12,28,20,29,30,17,9,18,31]),import.meta.url),()=>i(()=>import("../nodes/10.CmSU0U-5.js"),__vite__mapDeps([39,1,2,4,20,7,14,6,5,27,11,13,12,28,29,40]),import.meta.url),()=>i(()=>import("../nodes/11.BvPbAE-y.js"),__vite__mapDeps([41,1,2,4,27,11,13,6,7,14,12,28,20,29,30,17,9,18,31]),import.meta.url),()=>i(()=>import("../nodes/12.Ca8uRgGS.js"),__vite__mapDeps([42,1,2,4,35,6,13,12,7,14,36,30,11,17,9,18,31,29]),import.meta.url),()=>i(()=>import("../nodes/13.CNpzwRjO.js"),__vite__mapDeps([43,1,2,4,44,5,6,13,14]),import.meta.url),()=>i(()=>import("../nodes/14.BKJfGLGD.js"),__vite__mapDeps([45,1,2,4,44,5,6,13,14]),import.meta.url),()=>i(()=>import("../nodes/15.HAXP-y8O.js"),__vite__mapDeps([46,1,2,4,30,11,12,7,14,6,13,17,9,18,31,27,28,20,29]),import.meta.url),()=>i(()=>import("../nodes/16.BFe6OVRm.js"),__vite__mapDeps([47,1,2,4,5,6,7,14,15,16,12,17,9,18,11,19,20,13,21,24,25]),import.meta.url)],Jt=[0,2],Kt={"/":[-4],"/articles/gd-anime-was-a-mistake":[4,[2]],"/articles/gd-mayolo":[5,[2]],"/articles/gd-whiteface":[6,[2]],"/articles/lcatdb":[7,[2]],"/articles/mac-hidden-files":[8,[2]],"/articles/new-blog":[9,[2]],"/articles/photo-megapost":[-11,[2]],"/articles/portfolio":[11,[2]],"/articles/responsive-design":[12,[2]],"/articles/software-dump-mac":[13,[2]],"/articles/software-dump-multiplat":[14,[2]],"/articles/switch-ports":[15,[2]],"/category/[slug]":[-17]},Qt={handleError:({error:o})=>{console.error(o)},reroute:()=>{}};export{Kt as dictionary,Qt as hooks,Xt as matchers,zt as nodes,Yt as root,Jt as server_loads}; +const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["../nodes/0.-ewE0Ojq.js","../chunks/disclose-version.jbUlswzs.js","../chunks/runtime.Dfx-lVnK.js","../nodes/1.B-BTWY2M.js","../chunks/legacy.CRkFLlqN.js","../chunks/render.C1kgis0d.js","../chunks/store.CE1T5q5i.js","../chunks/lifecycle.kgqH09T4.js","../chunks/entry.BDNlJs-a.js","../chunks/index.CO8mGWLE.js","../nodes/2.JLOnKEIi.js","../chunks/if.BRDm7pt5.js","../chunks/each.psv4IetE.js","../chunks/style.BqiTLypu.js","../chunks/attributes.Dvuu1p9u.js","../chunks/props.C9XHSajK.js","../chunks/Layout.B6tzzdmS.js","../chunks/html.DJwC5DVL.js","../chunks/Frame.svelte_svelte_type_style_lang.CiKUY8ht.js","../assets/Frame.Ch5Xis14.css","../chunks/index-client.Ck4GHNqj.js","../assets/Layout.C0MFVVOY.css","../assets/2.uYDo6Uun.css","../nodes/3.BOJoUU7x.js","../chunks/ArticleFeed.BnZogVUb.js","../assets/ArticleFeed.COlA-k9G.css","../nodes/4.CFIMFROb.js","../chunks/Image.CorzdUlf.js","../chunks/this.l8DE5kMR.js","../assets/Image.DiB1YRod.css","../chunks/Figure.DrAK28J1.js","../assets/Figure.D7C6L14i.css","../nodes/5.OswBxykr.js","../nodes/6.uWhwuCeF.js","../nodes/7.BFR6zYzy.js","../chunks/Video.DdNmxp5V.js","../assets/Video.C2BkDKz5.css","../nodes/8.HXC27mj2.js","../nodes/9.DkYBbSqf.js","../nodes/10.CmSU0U-5.js","../assets/10.jxrO33K0.css","../nodes/11.HSC-p8U2.js","../nodes/12.D8NuWnIb.js","../nodes/13.CNpzwRjO.js","../chunks/Link.Ckijf6nQ.js","../nodes/14.BKJfGLGD.js","../nodes/15.HAXP-y8O.js","../nodes/16.BFe6OVRm.js"])))=>i.map(i=>d[i]); +var z=o=>{throw TypeError(o)};var J=(o,t,e)=>t.has(o)||z("Cannot "+e);var u=(o,t,e)=>(J(o,t,"read from private field"),e?e.call(o):t.get(o)),N=(o,t,e)=>t.has(o)?z("Cannot add the same private member more than once"):t instanceof WeakSet?t.add(o):t.set(o,e),U=(o,t,e,s)=>(J(o,t,"write to private field"),s?s.call(o,e):t.set(o,e),e);import{h as K,g as rt,d as at,a0 as ot,i as st,j as nt,N as it,a4 as g,as as ct,ah as S,aG as lt,aF as ut,X as mt,p as _t,a1 as dt,a2 as ft,aH as ht,f as p,s as vt,a as gt,aI as H,c as Et,t as yt,r as Pt,a7 as T}from"../chunks/runtime.Dfx-lVnK.js";import{h as Rt,m as pt,u as bt,s as At}from"../chunks/render.C1kgis0d.js";import{d as A,a as y,t as Z,e as Lt}from"../chunks/disclose-version.jbUlswzs.js";import{i as k}from"../chunks/if.BRDm7pt5.js";import{p as x,a as Ot}from"../chunks/props.C9XHSajK.js";import{b as w}from"../chunks/this.l8DE5kMR.js";import{o as Tt}from"../chunks/index-client.Ck4GHNqj.js";function I(o,t,e){K&&rt();var s=o,n,m;at(()=>{n!==(n=t())&&(m&&(it(m),m=null),n&&(m=st(()=>e(s,n))))},ot),K&&(s=nt)}function wt(o){return class extends It{constructor(t){super({component:o,...t})}}}var P,d;class It{constructor(t){N(this,P);N(this,d);var m;var e=new Map,s=(a,r)=>{var _=mt(r);return e.set(a,_),_};const n=new Proxy({...t.props||{},$$events:{}},{get(a,r){return g(e.get(r)??s(r,Reflect.get(a,r)))},has(a,r){return r===ct?!0:(g(e.get(r)??s(r,Reflect.get(a,r))),Reflect.has(a,r))},set(a,r,_){return S(e.get(r)??s(r,_),_),Reflect.set(a,r,_)}});U(this,d,(t.hydrate?Rt:pt)(t.component,{target:t.target,anchor:t.anchor,props:n,context:t.context,intro:t.intro??!1,recover:t.recover})),(!((m=t==null?void 0:t.props)!=null&&m.$$host)||t.sync===!1)&<(),U(this,P,n.$$events);for(const a of Object.keys(u(this,d)))a==="$set"||a==="$destroy"||a==="$on"||ut(this,a,{get(){return u(this,d)[a]},set(r){u(this,d)[a]=r},enumerable:!0});u(this,d).$set=a=>{Object.assign(n,a)},u(this,d).$destroy=()=>{bt(u(this,d))}}$set(t){u(this,d).$set(t)}$on(t,e){u(this,P)[t]=u(this,P)[t]||[];const s=(...n)=>e.call(this,...n);return u(this,P)[t].push(s),()=>{u(this,P)[t]=u(this,P)[t].filter(n=>n!==s)}}$destroy(){u(this,d).$destroy()}}P=new WeakMap,d=new WeakMap;const Dt="modulepreload",Vt=function(o,t){return new URL(o,t).href},Q={},i=function(t,e,s){let n=Promise.resolve();if(e&&e.length>0){const a=document.getElementsByTagName("link"),r=document.querySelector("meta[property=csp-nonce]"),_=(r==null?void 0:r.nonce)||(r==null?void 0:r.getAttribute("nonce"));n=Promise.allSettled(e.map(f=>{if(f=Vt(f,s),f in Q)return;Q[f]=!0;const R=f.endsWith(".css"),D=R?'[rel="stylesheet"]':"";if(!!s)for(let l=a.length-1;l>=0;l--){const E=a[l];if(E.href===f&&(!R||E.rel==="stylesheet"))return}else if(document.querySelector(`link[href="${f}"]${D}`))return;const c=document.createElement("link");if(c.rel=R?"stylesheet":Dt,R||(c.as="script"),c.crossOrigin="",c.href=f,_&&c.setAttribute("nonce",_),document.head.appendChild(c),R)return new Promise((l,E)=>{c.addEventListener("load",l),c.addEventListener("error",()=>E(new Error(`Unable to preload CSS for ${f}`)))})}))}function m(a){const r=new Event("vite:preloadError",{cancelable:!0});if(r.payload=a,window.dispatchEvent(r),!r.defaultPrevented)throw a}return n.then(a=>{for(const r of a||[])r.status==="rejected"&&m(r.reason);return t().catch(m)})},Xt={};var kt=Z('
'),xt=Z(" ",1);function St(o,t){_t(t,!0);let e=x(t,"components",23,()=>[]),s=x(t,"data_0",3,null),n=x(t,"data_1",3,null),m=x(t,"data_2",3,null);dt(()=>t.stores.page.set(t.page)),ft(()=>{t.stores,t.page,t.constructors,e(),t.form,s(),n(),m(),t.stores.page.notify()});let a=H(!1),r=H(!1),_=H(null);Tt(()=>{const c=t.stores.page.subscribe(()=>{g(a)&&(S(r,!0),ht().then(()=>{S(_,Ot(document.title||"untitled page"))}))});return S(a,!0),c});const f=T(()=>t.constructors[2]);var R=xt(),D=p(R);k(D,()=>t.constructors[1],c=>{var l=A();const E=T(()=>t.constructors[0]);var L=p(l);I(L,()=>g(E),(b,C)=>{w(C(b,{get data(){return s()},get form(){return t.form},children:(h,Ct)=>{var X=A(),M=p(X);k(M,()=>t.constructors[2],j=>{var O=A();const B=T(()=>t.constructors[1]);var F=p(O);I(F,()=>g(B),(q,G)=>{w(G(q,{get data(){return n()},get form(){return t.form},children:(v,jt)=>{var Y=A(),$=p(Y);I($,()=>g(f),(tt,et)=>{w(et(tt,{get data(){return m()},get form(){return t.form}}),V=>e()[2]=V,()=>{var V;return(V=e())==null?void 0:V[2]})}),y(v,Y)},$$slots:{default:!0}}),v=>e()[1]=v,()=>{var v;return(v=e())==null?void 0:v[1]})}),y(j,O)},j=>{var O=A();const B=T(()=>t.constructors[1]);var F=p(O);I(F,()=>g(B),(q,G)=>{w(G(q,{get data(){return n()},get form(){return t.form}}),v=>e()[1]=v,()=>{var v;return(v=e())==null?void 0:v[1]})}),y(j,O)}),y(h,X)},$$slots:{default:!0}}),h=>e()[0]=h,()=>{var h;return(h=e())==null?void 0:h[0]})}),y(c,l)},c=>{var l=A();const E=T(()=>t.constructors[0]);var L=p(l);I(L,()=>g(E),(b,C)=>{w(C(b,{get data(){return s()},get form(){return t.form}}),h=>e()[0]=h,()=>{var h;return(h=e())==null?void 0:h[0]})}),y(c,l)});var W=vt(D,2);k(W,()=>g(a),c=>{var l=kt(),E=Et(l);k(E,()=>g(r),L=>{var b=Lt();yt(()=>At(b,g(_))),y(L,b)}),Pt(l),y(c,l)}),y(o,R),gt()}const Yt=wt(St),zt=[()=>i(()=>import("../nodes/0.-ewE0Ojq.js"),__vite__mapDeps([0,1,2]),import.meta.url),()=>i(()=>import("../nodes/1.B-BTWY2M.js"),__vite__mapDeps([3,1,2,4,5,6,7,8,9]),import.meta.url),()=>i(()=>import("../nodes/2.JLOnKEIi.js"),__vite__mapDeps([10,1,2,4,5,6,11,12,13,14,7,15,16,17,18,9,19,20,21,22]),import.meta.url),()=>i(()=>import("../nodes/3.BOJoUU7x.js"),__vite__mapDeps([23,1,2,4,7,15,6,16,5,17,13,18,9,19,11,20,12,14,21,24,25]),import.meta.url),()=>i(()=>import("../nodes/4.CFIMFROb.js"),__vite__mapDeps([26,1,2,4,27,11,14,6,7,15,13,28,12,29,30,18,9,19,31]),import.meta.url),()=>i(()=>import("../nodes/5.OswBxykr.js"),__vite__mapDeps([32,1,2,4,27,11,14,6,7,15,13,28,12,29,30,18,9,19,31]),import.meta.url),()=>i(()=>import("../nodes/6.uWhwuCeF.js"),__vite__mapDeps([33,1,2,4,27,11,14,6,7,15,13,28,12,29,30,18,9,19,31]),import.meta.url),()=>i(()=>import("../nodes/7.BFR6zYzy.js"),__vite__mapDeps([34,1,2,4,27,11,14,6,7,15,13,28,12,29,35,36,30,18,9,19,31]),import.meta.url),()=>i(()=>import("../nodes/8.HXC27mj2.js"),__vite__mapDeps([37,1,2,4,17,27,11,14,6,7,15,13,28,12,29,30,18,9,19,31]),import.meta.url),()=>i(()=>import("../nodes/9.DkYBbSqf.js"),__vite__mapDeps([38,1,2,4,27,11,14,6,7,15,13,28,12,29,30,18,9,19,31]),import.meta.url),()=>i(()=>import("../nodes/10.CmSU0U-5.js"),__vite__mapDeps([39,1,2,4,12,7,15,6,5,27,11,14,13,28,29,40]),import.meta.url),()=>i(()=>import("../nodes/11.HSC-p8U2.js"),__vite__mapDeps([41,1,2,4,27,11,14,6,7,15,13,28,12,29,30,18,9,19,31]),import.meta.url),()=>i(()=>import("../nodes/12.D8NuWnIb.js"),__vite__mapDeps([42,1,2,4,35,6,14,13,7,15,36,30,11,18,9,19,31,29]),import.meta.url),()=>i(()=>import("../nodes/13.CNpzwRjO.js"),__vite__mapDeps([43,1,2,4,44,5,6,14,15]),import.meta.url),()=>i(()=>import("../nodes/14.BKJfGLGD.js"),__vite__mapDeps([45,1,2,4,44,5,6,14,15]),import.meta.url),()=>i(()=>import("../nodes/15.HAXP-y8O.js"),__vite__mapDeps([46,1,2,4,30,11,13,7,15,6,14,18,9,19,31,27,28,12,29]),import.meta.url),()=>i(()=>import("../nodes/16.BFe6OVRm.js"),__vite__mapDeps([47,1,2,4,5,6,7,15,16,17,13,18,9,19,11,20,12,14,21,24,25]),import.meta.url)],Jt=[0,2],Kt={"/":[-4],"/articles/gd-anime-was-a-mistake":[4,[2]],"/articles/gd-mayolo":[5,[2]],"/articles/gd-whiteface":[6,[2]],"/articles/lcatdb":[7,[2]],"/articles/mac-hidden-files":[8,[2]],"/articles/new-blog":[9,[2]],"/articles/photo-megapost":[-11,[2]],"/articles/portfolio":[11,[2]],"/articles/responsive-design":[12,[2]],"/articles/software-dump-mac":[13,[2]],"/articles/software-dump-multiplat":[14,[2]],"/articles/switch-ports":[15,[2]],"/category/[slug]":[-17]},Qt={handleError:({error:o})=>{console.error(o)},reroute:()=>{}};export{Kt as dictionary,Qt as hooks,Xt as matchers,zt as nodes,Yt as root,Jt as server_loads}; diff --git a/_app/immutable/entry/start.B8xNVxTP.js b/_app/immutable/entry/start.B8xNVxTP.js deleted file mode 100644 index 9ee99a1..0000000 --- a/_app/immutable/entry/start.B8xNVxTP.js +++ /dev/null @@ -1 +0,0 @@ -import{a as t}from"../chunks/entry.B8mbJhC8.js";export{t as start}; diff --git a/_app/immutable/entry/start.BGhs6bZM.js b/_app/immutable/entry/start.BGhs6bZM.js new file mode 100644 index 0000000..b4cf7bb --- /dev/null +++ b/_app/immutable/entry/start.BGhs6bZM.js @@ -0,0 +1 @@ +import{a as t}from"../chunks/entry.BDNlJs-a.js";export{t as start}; diff --git a/_app/immutable/nodes/1.wGGJZu2n.js b/_app/immutable/nodes/1.B-BTWY2M.js similarity index 92% rename from _app/immutable/nodes/1.wGGJZu2n.js rename to _app/immutable/nodes/1.B-BTWY2M.js index cbaac87..5ddb865 100644 --- a/_app/immutable/nodes/1.wGGJZu2n.js +++ b/_app/immutable/nodes/1.B-BTWY2M.js @@ -1 +1 @@ -import{a as b,t as f}from"../chunks/disclose-version.jbUlswzs.js";import"../chunks/legacy.CRkFLlqN.js";import{p as v,f as d,t as h,a as l,s as _,c as i,r as n}from"../chunks/runtime.Dfx-lVnK.js";import{s as c}from"../chunks/render.C1kgis0d.js";import{i as $}from"../chunks/lifecycle.kgqH09T4.js";import{s as x,b as E}from"../chunks/store.CE1T5q5i.js";import{s as S}from"../chunks/entry.B8mbJhC8.js";const j=()=>{const s=S;return{page:{subscribe:s.page.subscribe},navigating:{subscribe:s.navigating.subscribe},updated:s.updated}},k={subscribe(s){return j().page.subscribe(s)}};var q=f("

",1);function F(s,r){v(r,!1);const m=x(),e=()=>E(k,"$page",m);$();var a=q(),t=d(a),u=i(t,!0);n(t);var o=_(t,2),g=i(o,!0);n(o),h(()=>{var p;c(u,e().status),c(g,(p=e().error)==null?void 0:p.message)}),b(s,a),l()}export{F as component}; +import{a as b,t as f}from"../chunks/disclose-version.jbUlswzs.js";import"../chunks/legacy.CRkFLlqN.js";import{p as v,f as d,t as h,a as l,s as _,c as i,r as n}from"../chunks/runtime.Dfx-lVnK.js";import{s as c}from"../chunks/render.C1kgis0d.js";import{i as $}from"../chunks/lifecycle.kgqH09T4.js";import{s as x,b as E}from"../chunks/store.CE1T5q5i.js";import{s as S}from"../chunks/entry.BDNlJs-a.js";const j=()=>{const s=S;return{page:{subscribe:s.page.subscribe},navigating:{subscribe:s.navigating.subscribe},updated:s.updated}},k={subscribe(s){return j().page.subscribe(s)}};var q=f("

",1);function F(s,r){v(r,!1);const m=x(),e=()=>E(k,"$page",m);$();var a=q(),t=d(a),u=i(t,!0);n(t);var o=_(t,2),g=i(o,!0);n(o),h(()=>{var p;c(u,e().status),c(g,(p=e().error)==null?void 0:p.message)}),b(s,a),l()}export{F as component}; diff --git a/_app/immutable/nodes/11.BvPbAE-y.js b/_app/immutable/nodes/11.BvPbAE-y.js deleted file mode 100644 index ef6ec25..0000000 --- a/_app/immutable/nodes/11.BvPbAE-y.js +++ /dev/null @@ -1 +0,0 @@ -import{a as p,t as m}from"../chunks/disclose-version.jbUlswzs.js";import"../chunks/legacy.CRkFLlqN.js";import{s as t,f as c,n as d}from"../chunks/runtime.Dfx-lVnK.js";import{I as o}from"../chunks/Image.CorzdUlf.js";import{F as a}from"../chunks/Figure.DrAK28J1.js";const g={avif:""+new URL("../assets/IMG_AA04362AF84B-1.DsnyqCpp.avif",import.meta.url).href+" 480w, "+new URL("../assets/IMG_AA04362AF84B-1.Z1HIv9WD.avif",import.meta.url).href+" 920w",webp:""+new URL("../assets/IMG_AA04362AF84B-1.D4ixPQJd.webp",import.meta.url).href+" 480w, "+new URL("../assets/IMG_AA04362AF84B-1.C9kEy5nY.webp",import.meta.url).href+" 920w",jpeg:""+new URL("../assets/IMG_AA04362AF84B-1.CnGKgfj0.jpg",import.meta.url).href+" 480w, "+new URL("../assets/IMG_AA04362AF84B-1.D9WN_J4n.jpg",import.meta.url).href+" 920w"},u={src:""+new URL("../assets/IMG_AA04362AF84B-1.D9WN_J4n.jpg",import.meta.url).href,w:920,h:1713,lqip:"UklGRqgAAABXRUJQVlA4IJwAAAAQAwCdASoQAB4ABUB8JbACdMoAFuicDYxGoA7VnWRO4AAA+PVHKuzY8HLo1hal/F7iYG/bIl6ZZmpg/mL3NY2lRUzgVMQNNI0GyoNlkXH0UcNf+LEstqUXqNvHfdL1wLB8ameeQgSWr8beVaEwItsnaz70zDs06CnWdzYJk4E6HbW5sD2TpaD+jnhj6S+y1mx0qJboZtqZIVicAAA="},f={sources:g,img:u},w={avif:""+new URL("../assets/SCR-20230412-r9e.DaXy5q9B.avif",import.meta.url).href+" 480w, "+new URL("../assets/SCR-20230412-r9e.DKqXF5ka.avif",import.meta.url).href+" 892w",webp:""+new URL("../assets/SCR-20230412-r9e.uHCQKsu4.webp",import.meta.url).href+" 480w, "+new URL("../assets/SCR-20230412-r9e.DG462qTW.webp",import.meta.url).href+" 892w",jpeg:""+new URL("../assets/SCR-20230412-r9e.CZj6S4S6.jpg",import.meta.url).href+" 480w, "+new URL("../assets/SCR-20230412-r9e.Dt916dPb.jpg",import.meta.url).href+" 892w"},y={src:""+new URL("../assets/SCR-20230412-r9e.Dt916dPb.jpg",import.meta.url).href,w:892,h:1454,lqip:"UklGRmwAAABXRUJQVlA4IGAAAACwAwCdASoQABoAPzmGu1QvKSYjMAgB4CcJQBb3BDzfHTEl5gmCIAD+3oAEiCBO26tpgEWFfQPNQeIzItFcDN6Y0NQEtT+xTxUrwRe09FdM6htYxu601TLLY+jmoXPTAAA="},b={sources:w,img:y},A={avif:""+new URL("../assets/localhost_3000_login.html.BQWwZbvR.avif",import.meta.url).href+" 428w",webp:""+new URL("../assets/localhost_3000_login.html.DF5I5gd7.webp",import.meta.url).href+" 428w"},I={src:""+new URL("../assets/localhost_3000_login.html.BtjM_qXW.jpg",import.meta.url).href,w:428,h:723,lqip:"UklGRp4AAABXRUJQVlA4IJIAAABQBACdASoQABsAPzmGulOvKSWisAgB4CcJZACdMoADUBOr5002QSz1FgAAAPedbniXbvC8O2Ixj1eyaMg2RWbQcD7dtL7iQQwTDMs5lY1n7pp68EBdzFxh95VD6vo6x90k3kfFjg9Sbz9pAKGMjIWhnBClctlRYPdFqGFlXLxgUDqGdxoUakFLW9cuGqX4XwAAAA=="},v={sources:A,img:I};var k=m('

Welcome to the site! If you’re on this post, you’re probably wondering who I am and what I do! I’ve recently graduated from Clarkson University with a Master’s in Computer Science. However, I’d like to use this post to show what I am outside of just this degree!


Professional Projects

Clarkson University Virtual Reality Coaster

In February of 2021, I began a refresh of Clarkson University’s MaxFlight VR2004 motion simulator, dubbed the Virtual Reality Coaster (VRC)! This machine was purchased from a local arcade sometime in the mid-2000s, and it shows. The software is quite dated, and so I’ve produced several resources to bring it up towards modern standards:

  • pyMaxFlight: Exposes all capabilities of the MaxFlight Motion Client programmatically through Python on a the Control PC. This module only allows for local operation. MaxFlight did not provide their own public API for interfacing with the simulator, so this module works by directly accessing the Motion Client window using the Win32 API, simulating button presses and slider movements. (GitHub, Docs)

  • pyWSConsole: Provides network communication via WebSockets, with a focus on stability and persistent connections. Uses a console-like interface and provides automatic help documentation. (GitHub)

  • VRC-Apps: A collection of Python applications to extend the functionality of the VRC using the aforementioned libraries. (GitHub)

Whiteface Tour

Link: https://whitefacetour.app

In the summer of 2018, I had the opportunity to work as an intern at the Whiteface Mountain Atmospheric Sciences Research Center (ASRC). During my time there, I worked on several projects related to outreach. The most substantial of these is the Whiteface Tour: a 360-degree virtual tour of the mountain summit. This uses the library three.js for rendering, and a custom JSON-based system for scene management. This does not use a pre-existing game engine. All resources are packaged using webpack, deployed through GitHub Actions, and hosted using GitHub Pages.


lcatDB (Lake Champlain Anglers’ Temperature Database)

lcatDB (Lake Champlain Anglers’ Temperature Database) was an online database and single page application (SPA) meant to provide a centralized means of recording, accessing, and analyzing vertical water temperature profiles to citizens of the Lake Champlain region. It was supported by the SUNY Plattsburgh Center for Earth & Environmental Science and the Lake Champlain Sea Grant. lcatDB was (what I consider to be) my first attempt at full stack software development. It primarily uses Bootstrap, jQuery, and Grunt for the frontend, while the backend was built using Node.JS, Express, and MongoDB. Read the full retrospective here.

Personal Projects

Switch Ports

Page: Switch Ports

The Nintendo Switch has a thriving homebrew scene that I took part in between 2019 and 2022. I developed ports of several games and maintained them through the 3 years that I was active. These ports were generally maintained as forks so as to not pollute the upstream repositories with platform specific material. This required working with upstream maintainers to merge in changes to these fork and produce installable packages, which was eventually automated using GitHub Actions.

ICBINS1

Link: http://jojudge.com/ICBINS1/

As a test to learn the ins-and-outs of Unity, I tried recreating the original Sonic the Hedgehog from observation! This was a great learning experience and taught me a lot about the game engine and C#. I later moved away from this project due to it being superseded in its goal by the decompilation of the 2013 remake, the exploration of other approaches, and shifting priorities in my personal life.

S2CX/Genesis Plus GX Wide

News Article: https://www.libretro.com/index.php/genesis-plus-gx-wide-now-available-for-libretroretroarch/

Another project launched before the 2013 remake decompilations that took the Genesis version of Sonic 2 and added widescreen capabilities. When first developed, my modification of Genesis Plus GX was only meant to add widescreen to that one game. After people began experimenting with other games, it was found that this approach worked for many other games, and so the project now lives on as an upstream RetroArch core (you can find it in the core downloader)! I no longer maintain this fork, but it has been picked up by other passionate community members!

Other Stuff

Here’s a miscellaneous list of other things I written/done you should check out!

Other Hobbies

Believe it or not, I do get sick of programming! When I do, I like dipping my toes into other domains to pick up new skills! Some of my “side gigs” include:

Photography

Check out my photography megapost!

Music

Since 2022 I’ve been dabbling in mashups using Logic Pro X and AI tools such as demucs! You can find tracks on my SoundCloud. Be warned that my taste is… inexplicable? I often pull tracks from my mid-2000s childhood for material. Some of main inspirations include Neil Cicierega, Triple-Q, Galactic Hole, and SiivaGunner. I’m hoping to produce some original pieces in the future!

Graphic Design

You can see my graphic design skills reflected in many of my projects, as I found it to be necessary to pair with programming for many projects. I tend to follow more minimalist standards such as Material Design and Metro while retaining some personalized flair.

3D Modeling

I’m familiar with the basics of modeling in Blender and SketchUp and can produce simple models/renders for both graphic design and CAD. (Some examples: “Anime Was a Mistake”, “Mayolo”) I have 2 3D printers (one SLA, one FDM) and experiment with them from time to time to produce both figurines and more utilitarian parts.s


Philosophy

I’ve spent a lot of time programming in a lot of different languages, working with many different technologies, and aiming for several different goals. It’s easy to get lost in programming as an art in-and-of-itself, but as a goal-oriented individual, I see programming as a tool I use to accomplish said goals. That being said, I personally recognize it as one of the most important tools in modern society, and thus I have spent a lot of time refining it as a craft.

I always consider the long-term viability of projects and weigh all possible approaches. There are many great ideas for products, but without a efficient plan, many of them lie on the table or the cutting-room floor. I often look to automation to avoid these scenarios, even for tasks in my personal life. I always consider all available options and tools: sometimes the answer is to deploy pre-existing services or generally change my workflow. Very often, however, an idea will come along that is too specific to execute with anything other than my own code.

Side note: As AI becomes more adept, I am constantly searching for ways to use it in optimizing my workflow. However, I am more interested in using AI tools than developing them, as I do not currently have a great interest in AI training and data science. I am experimenting with tools such as OpenAI Playground and GitHub Copilot and, while not perfect, I am very impressed with their results and have begun using them to increase my own efficiency.

When writing my code, I make sure that it is readable, modular, and generally reusable. Sometimes this may be as simple as providing reasoning through comments. Other times it may be more complex, such as producing documentation (generally using a pre-existing documentation system like mkdocs), segmenting code into more readable chunks, or even developing libraries to use in multiple projects. I prioritize compatibility with IntelliSense, which makes any codebase significantly more navigable. This is generally accomplished by following documentation standards, such as type annotations (ala Python 3) or JSDoc.

My goal is to write software and produce products that will stand the test of time, that I’ll be able to look on years down the line and be proud of.

',1);function j(l){var i=k(),s=t(c(i),6);a(s,{align:"right",children:(e,n)=>{o(e,{src:f})},$$slots:{default:!0}});var r=t(s,8);a(r,{align:"right",children:(e,n)=>{o(e,{src:b,maxHeight:"270px"})},$$slots:{default:!0}});var h=t(r,10);a(h,{align:"right",children:(e,n)=>{o(e,{src:v,maxHeight:"330px"})},$$slots:{default:!0}}),d(64),p(l,i)}export{j as component}; diff --git a/_app/immutable/nodes/11.HSC-p8U2.js b/_app/immutable/nodes/11.HSC-p8U2.js new file mode 100644 index 0000000..e32de19 --- /dev/null +++ b/_app/immutable/nodes/11.HSC-p8U2.js @@ -0,0 +1 @@ +import{a as p,t as m}from"../chunks/disclose-version.jbUlswzs.js";import"../chunks/legacy.CRkFLlqN.js";import{s as t,f as c,n as d}from"../chunks/runtime.Dfx-lVnK.js";import{I as o}from"../chunks/Image.CorzdUlf.js";import{F as a}from"../chunks/Figure.DrAK28J1.js";const g={avif:""+new URL("../assets/IMG_AA04362AF84B-1.DsnyqCpp.avif",import.meta.url).href+" 480w, "+new URL("../assets/IMG_AA04362AF84B-1.Z1HIv9WD.avif",import.meta.url).href+" 920w",webp:""+new URL("../assets/IMG_AA04362AF84B-1.D4ixPQJd.webp",import.meta.url).href+" 480w, "+new URL("../assets/IMG_AA04362AF84B-1.C9kEy5nY.webp",import.meta.url).href+" 920w",jpeg:""+new URL("../assets/IMG_AA04362AF84B-1.CnGKgfj0.jpg",import.meta.url).href+" 480w, "+new URL("../assets/IMG_AA04362AF84B-1.D9WN_J4n.jpg",import.meta.url).href+" 920w"},u={src:""+new URL("../assets/IMG_AA04362AF84B-1.D9WN_J4n.jpg",import.meta.url).href,w:920,h:1713,lqip:"UklGRqgAAABXRUJQVlA4IJwAAAAQAwCdASoQAB4ABUB8JbACdMoAFuicDYxGoA7VnWRO4AAA+PVHKuzY8HLo1hal/F7iYG/bIl6ZZmpg/mL3NY2lRUzgVMQNNI0GyoNlkXH0UcNf+LEstqUXqNvHfdL1wLB8ameeQgSWr8beVaEwItsnaz70zDs06CnWdzYJk4E6HbW5sD2TpaD+jnhj6S+y1mx0qJboZtqZIVicAAA="},f={sources:g,img:u},w={avif:""+new URL("../assets/SCR-20230412-r9e.DaXy5q9B.avif",import.meta.url).href+" 480w, "+new URL("../assets/SCR-20230412-r9e.DKqXF5ka.avif",import.meta.url).href+" 892w",webp:""+new URL("../assets/SCR-20230412-r9e.uHCQKsu4.webp",import.meta.url).href+" 480w, "+new URL("../assets/SCR-20230412-r9e.DG462qTW.webp",import.meta.url).href+" 892w",jpeg:""+new URL("../assets/SCR-20230412-r9e.CZj6S4S6.jpg",import.meta.url).href+" 480w, "+new URL("../assets/SCR-20230412-r9e.Dt916dPb.jpg",import.meta.url).href+" 892w"},y={src:""+new URL("../assets/SCR-20230412-r9e.Dt916dPb.jpg",import.meta.url).href,w:892,h:1454,lqip:"UklGRmwAAABXRUJQVlA4IGAAAACwAwCdASoQABoAPzmGu1QvKSYjMAgB4CcJQBb3BDzfHTEl5gmCIAD+3oAEiCBO26tpgEWFfQPNQeIzItFcDN6Y0NQEtT+xTxUrwRe09FdM6htYxu601TLLY+jmoXPTAAA="},b={sources:w,img:y},A={avif:""+new URL("../assets/localhost_3000_login.html.BQWwZbvR.avif",import.meta.url).href+" 428w",webp:""+new URL("../assets/localhost_3000_login.html.DF5I5gd7.webp",import.meta.url).href+" 428w"},I={src:""+new URL("../assets/localhost_3000_login.html.BtjM_qXW.jpg",import.meta.url).href,w:428,h:723,lqip:"UklGRp4AAABXRUJQVlA4IJIAAABQBACdASoQABsAPzmGulOvKSWisAgB4CcJZACdMoADUBOr5002QSz1FgAAAPedbniXbvC8O2Ixj1eyaMg2RWbQcD7dtL7iQQwTDMs5lY1n7pp68EBdzFxh95VD6vo6x90k3kfFjg9Sbz9pAKGMjIWhnBClctlRYPdFqGFlXLxgUDqGdxoUakFLW9cuGqX4XwAAAA=="},v={sources:A,img:I};var k=m('

Welcome to the site! If you’re on this post, you’re probably wondering who I am and what I do! I’ve recently graduated from Clarkson University with a Master’s in Computer Science. However, I’d like to use this post to show what I am outside of just this degree!


Professional Projects

Clarkson University Virtual Reality Coaster

In February of 2021, I began a refresh of Clarkson University’s MaxFlight VR2004 motion simulator, dubbed the Virtual Reality Coaster (VRC)! This machine was purchased from a local arcade sometime in the mid-2000s, and it shows. The software is quite dated, and so I’ve produced several resources to bring it up towards modern standards:

  • pyMaxFlight: Exposes all capabilities of the MaxFlight Motion Client programmatically through Python on a the Control PC. This module only allows for local operation. MaxFlight did not provide their own public API for interfacing with the simulator, so this module works by directly accessing the Motion Client window using the Win32 API, simulating button presses and slider movements. (GitHub, Docs)

  • pyWSConsole: Provides network communication via WebSockets, with a focus on stability and persistent connections. Uses a console-like interface and provides automatic help documentation. (GitHub)

  • VRC-Apps: A collection of Python applications to extend the functionality of the VRC using the aforementioned libraries. (GitHub)

Whiteface Tour

Link: https://jojudge.com/whitefacetour

In the summer of 2018, I had the opportunity to work as an intern at the Whiteface Mountain Atmospheric Sciences Research Center (ASRC). During my time there, I worked on several projects related to outreach. The most substantial of these is the Whiteface Tour: a 360-degree virtual tour of the mountain summit. This uses the library three.js for rendering, and a custom JSON-based system for scene management. This does not use a pre-existing game engine. All resources are packaged using webpack, deployed through GitHub Actions, and hosted using GitHub Pages.


lcatDB (Lake Champlain Anglers’ Temperature Database)

lcatDB (Lake Champlain Anglers’ Temperature Database) was an online database and single page application (SPA) meant to provide a centralized means of recording, accessing, and analyzing vertical water temperature profiles to citizens of the Lake Champlain region. It was supported by the SUNY Plattsburgh Center for Earth & Environmental Science and the Lake Champlain Sea Grant. lcatDB was (what I consider to be) my first attempt at full stack software development. It primarily uses Bootstrap, jQuery, and Grunt for the frontend, while the backend was built using Node.JS, Express, and MongoDB. Read the full retrospective here.

Personal Projects

Switch Ports

Page: Switch Ports

The Nintendo Switch has a thriving homebrew scene that I took part in between 2019 and 2022. I developed ports of several games and maintained them through the 3 years that I was active. These ports were generally maintained as forks so as to not pollute the upstream repositories with platform specific material. This required working with upstream maintainers to merge in changes to these fork and produce installable packages, which was eventually automated using GitHub Actions.

ICBINS1

Link: http://jojudge.com/ICBINS1/

As a test to learn the ins-and-outs of Unity, I tried recreating the original Sonic the Hedgehog from observation! This was a great learning experience and taught me a lot about the game engine and C#. I later moved away from this project due to it being superseded in its goal by the decompilation of the 2013 remake, the exploration of other approaches, and shifting priorities in my personal life.

S2CX/Genesis Plus GX Wide

News Article: https://www.libretro.com/index.php/genesis-plus-gx-wide-now-available-for-libretroretroarch/

Another project launched before the 2013 remake decompilations that took the Genesis version of Sonic 2 and added widescreen capabilities. When first developed, my modification of Genesis Plus GX was only meant to add widescreen to that one game. After people began experimenting with other games, it was found that this approach worked for many other games, and so the project now lives on as an upstream RetroArch core (you can find it in the core downloader)! I no longer maintain this fork, but it has been picked up by other passionate community members!

Other Stuff

Here’s a miscellaneous list of other things I written/done you should check out!

Other Hobbies

Believe it or not, I do get sick of programming! When I do, I like dipping my toes into other domains to pick up new skills! Some of my “side gigs” include:

Photography

Check out my photography megapost!

Music

Since 2022 I’ve been dabbling in mashups using Logic Pro X and AI tools such as demucs! You can find tracks on my SoundCloud. Be warned that my taste is… inexplicable? I often pull tracks from my mid-2000s childhood for material. Some of main inspirations include Neil Cicierega, Triple-Q, Galactic Hole, and SiivaGunner. I’m hoping to produce some original pieces in the future!

Graphic Design

You can see my graphic design skills reflected in many of my projects, as I found it to be necessary to pair with programming for many projects. I tend to follow more minimalist standards such as Material Design and Metro while retaining some personalized flair.

3D Modeling

I’m familiar with the basics of modeling in Blender and SketchUp and can produce simple models/renders for both graphic design and CAD. (Some examples: “Anime Was a Mistake”, “Mayolo”) I have 2 3D printers (one SLA, one FDM) and experiment with them from time to time to produce both figurines and more utilitarian parts.s


Philosophy

I’ve spent a lot of time programming in a lot of different languages, working with many different technologies, and aiming for several different goals. It’s easy to get lost in programming as an art in-and-of-itself, but as a goal-oriented individual, I see programming as a tool I use to accomplish said goals. That being said, I personally recognize it as one of the most important tools in modern society, and thus I have spent a lot of time refining it as a craft.

I always consider the long-term viability of projects and weigh all possible approaches. There are many great ideas for products, but without a efficient plan, many of them lie on the table or the cutting-room floor. I often look to automation to avoid these scenarios, even for tasks in my personal life. I always consider all available options and tools: sometimes the answer is to deploy pre-existing services or generally change my workflow. Very often, however, an idea will come along that is too specific to execute with anything other than my own code.

Side note: As AI becomes more adept, I am constantly searching for ways to use it in optimizing my workflow. However, I am more interested in using AI tools than developing them, as I do not currently have a great interest in AI training and data science. I am experimenting with tools such as OpenAI Playground and GitHub Copilot and, while not perfect, I am very impressed with their results and have begun using them to increase my own efficiency.

When writing my code, I make sure that it is readable, modular, and generally reusable. Sometimes this may be as simple as providing reasoning through comments. Other times it may be more complex, such as producing documentation (generally using a pre-existing documentation system like mkdocs), segmenting code into more readable chunks, or even developing libraries to use in multiple projects. I prioritize compatibility with IntelliSense, which makes any codebase significantly more navigable. This is generally accomplished by following documentation standards, such as type annotations (ala Python 3) or JSDoc.

My goal is to write software and produce products that will stand the test of time, that I’ll be able to look on years down the line and be proud of.

',1);function B(l){var i=k(),s=t(c(i),6);a(s,{align:"right",children:(e,n)=>{o(e,{src:f})},$$slots:{default:!0}});var r=t(s,8);a(r,{align:"right",children:(e,n)=>{o(e,{src:b,maxHeight:"270px"})},$$slots:{default:!0}});var h=t(r,10);a(h,{align:"right",children:(e,n)=>{o(e,{src:v,maxHeight:"330px"})},$$slots:{default:!0}}),d(64),p(l,i)}export{B as component}; diff --git a/_app/immutable/nodes/12.Ca8uRgGS.js b/_app/immutable/nodes/12.Ca8uRgGS.js deleted file mode 100644 index 403cda1..0000000 --- a/_app/immutable/nodes/12.Ca8uRgGS.js +++ /dev/null @@ -1 +0,0 @@ -import{a,t as r}from"../chunks/disclose-version.jbUlswzs.js";import"../chunks/legacy.CRkFLlqN.js";import{s,f as h}from"../chunks/runtime.Dfx-lVnK.js";/* empty css */import{V as i}from"../chunks/Video.DdNmxp5V.js";import{F as p}from"../chunks/Figure.DrAK28J1.js";const u=""+new URL("../assets/Whiteface Responsive Demo.C9GpO-gE.mp4",import.meta.url).href,d=""+new URL("../assets/lcatDB Responsive Demo.CH1cWjrv.mp4",import.meta.url).href,v=""+new URL("../assets/heyjoeway Responsive Demo.DIPvgU25.mp4",import.meta.url).href;var g=r('
The Whiteface Tour is a 360-degree virtual tour of the Whiteface Mountain summit I developed using three.js. Try it for yourself here!
'),$=r('
lcatDB was a full-stack single-page application which I developed. You can read more about it here.
'),y=r(`
And last but not least, the site you're reading this on! Try it for yourself!
`),w=r("

Here’s a mainly visual showcase of some of my work in responsive design!

",1);function j(f){var n=w(),c=s(h(n),2);p(c,{children:(o,t)=>{i(o,{src:u})},$$slots:{default:!0,caption:(o,t)=>{var e=g();a(o,e)}}});var l=s(c,2);p(l,{children:(o,t)=>{i(o,{src:d})},$$slots:{default:!0,caption:(o,t)=>{var e=$();a(o,e)}}});var m=s(l,2);p(m,{children:(o,t)=>{i(o,{src:v})},$$slots:{default:!0,caption:(o,t)=>{var e=y();a(o,e)}}}),a(f,n)}export{j as component}; diff --git a/_app/immutable/nodes/12.D8NuWnIb.js b/_app/immutable/nodes/12.D8NuWnIb.js new file mode 100644 index 0000000..abfc9fc --- /dev/null +++ b/_app/immutable/nodes/12.D8NuWnIb.js @@ -0,0 +1 @@ +import{a,t as r}from"../chunks/disclose-version.jbUlswzs.js";import"../chunks/legacy.CRkFLlqN.js";import{s,f as u}from"../chunks/runtime.Dfx-lVnK.js";/* empty css */import{V as i}from"../chunks/Video.DdNmxp5V.js";import{F as p}from"../chunks/Figure.DrAK28J1.js";const d=""+new URL("../assets/Whiteface Responsive Demo.C9GpO-gE.mp4",import.meta.url).href,h=""+new URL("../assets/lcatDB Responsive Demo.CH1cWjrv.mp4",import.meta.url).href,v=""+new URL("../assets/heyjoeway Responsive Demo.DIPvgU25.mp4",import.meta.url).href;var g=r('
The Whiteface Tour is a 360-degree virtual tour of the Whiteface Mountain summit I developed using three.js. Try it for yourself here!
'),$=r('
lcatDB was a full-stack single-page application which I developed. You can read more about it here.
'),y=r(`
And last but not least, the site you're reading this on! Try it for yourself!
`),w=r("

Here’s a mainly visual showcase of some of my work in responsive design!

",1);function b(f){var n=w(),c=s(u(n),2);p(c,{children:(o,e)=>{i(o,{src:d})},$$slots:{default:!0,caption:(o,e)=>{var t=g();a(o,t)}}});var l=s(c,2);p(l,{children:(o,e)=>{i(o,{src:h})},$$slots:{default:!0,caption:(o,e)=>{var t=$();a(o,t)}}});var m=s(l,2);p(m,{children:(o,e)=>{i(o,{src:v})},$$slots:{default:!0,caption:(o,e)=>{var t=y();a(o,t)}}}),a(f,n)}export{b as component}; diff --git a/_app/immutable/nodes/2.Bxop8xDV.js b/_app/immutable/nodes/2.Bxop8xDV.js deleted file mode 100644 index 043a05a..0000000 --- a/_app/immutable/nodes/2.Bxop8xDV.js +++ /dev/null @@ -1 +0,0 @@ -import{d as E,a as n,t as p,e as G,f as H}from"../chunks/disclose-version.jbUlswzs.js";import"../chunks/legacy.CRkFLlqN.js";import{p as D,c as b,f as C,r as x,t as h,a as I,s as f,n as J,a4 as K,a9 as N}from"../chunks/runtime.Dfx-lVnK.js";import{s as S}from"../chunks/render.C1kgis0d.js";import{i as M}from"../chunks/if.BRDm7pt5.js";import{a as w,s as P}from"../chunks/style.BqiTLypu.js";import{s as y}from"../chunks/attributes.Dvuu1p9u.js";import{i as j}from"../chunks/lifecycle.kgqH09T4.js";import{p as l}from"../chunks/props.C9XHSajK.js";import{C as O,L as Q,T as R,f as U,s as V}from"../chunks/Layout.B6tzzdmS.js";import{e as q,s as z,b as A,l as W}from"../chunks/store.CE1T5q5i.js";import{c as X,p as Y}from"../chunks/Frame.svelte_svelte_type_style_lang.CiKUY8ht.js";var Z=p(''),$=p('
');function tt(u,t){D(t,!1);let a=l(t,"width",8,"default"),g=l(t,"height",8,"default"),d=l(t,"onClick",24,()=>{}),v=l(t,"onContextMenu",24,()=>{}),m=l(t,"style",8,"");function c(s){typeof d()=="function"&&d()(s)}function _(s){typeof v()=="function"&&(s.preventDefault(),v()(s))}j();var e=$(),r=b(e);M(r,()=>typeof d()=="string",s=>{var o=Z(),i=b(o);w(i,t,"default",{}),x(o),h(()=>y(o,"href",d())),n(s,o)},s=>{var o=E(),i=C(o);w(i,t,"default",{}),n(s,o)}),x(e),h(()=>{y(e,"style",m()),P(e,"width",a()),P(e,"height",g())}),q("click",e,c),q("contextmenu",e,_),n(u,e),I()}var et=p(''),at=p('
');function st(u,t){D(t,!1);const a=z(),g=()=>A(X,"$currentTheme",a);let d=l(t,"onClick",24,()=>{}),v=l(t,"iconSrc",8,""),m=l(t,"color",8,null);j(),tt(u,{height:"32px",get onClick(){return d()},children:(c,_)=>{var e=at(),r=b(e);M(r,v,o=>{var i=et();h(()=>y(i,"src",v())),n(o,i)});var s=f(r,2);w(s,t,"default",{}),x(e),h(()=>P(e,"color",m()||g().text.primary.color)),n(c,e)},$$slots:{default:!0}}),I()}var rt=H('