aboutsummaryrefslogtreecommitdiffstats
path: root/src/pages
diff options
context:
space:
mode:
authorPinapelz <yukais@pinapelz.com>2026-04-27 16:45:49 -0700
committerPinapelz <yukais@pinapelz.com>2026-04-27 16:58:00 -0700
commitc3acd89b02680cd3c75674ef713875953d84b738 (patch)
tree9aa3fad4a3164f8170ee9adc39210ae6cefb22b4 /src/pages
parent2cdbdc1d9443578822482515fde6e91e9d2fc655 (diff)
update projects
Diffstat (limited to 'src/pages')
-rw-r--r--src/pages/projects.astro854
1 files changed, 448 insertions, 406 deletions
diff --git a/src/pages/projects.astro b/src/pages/projects.astro
index 7373603..1cb84ba 100644
--- a/src/pages/projects.astro
+++ b/src/pages/projects.astro
@@ -5,437 +5,479 @@ import Card from "../components/Card.astro";
---
<Layout title="Projects">
- <main>
- <div class="header-container">
- <h1 class="text-4xl font-semibold text-center py-6">Projects</h1>
- <p class="text-center mb-8">
- Here are some of my projects. I love tinkering with stuff so for a more complete list, visit my <a class="font-bold hover:underline hover:text-blue-300 transition-colors" href="https://github.com/pinapelz">Github</a>
- <br />
- <a href="https://github.com/pulls/search?q=is%3Apr+is%3Amerged+author%3Apinapelz+-repo%3Apinapelz%2Fupptime+-repo%3Apinapelz%2Fpinapelz+-user%3Apinapelz+-org%3AAntAvionics" class="mt-6 inline-block hover:underline text-center font-bold text-2xl animate-pulse hover:text-blue-300 transition-colors">Open Source Contributions</a>
- </p>
- </div>
- <ul role="list" class="project-grid">
- <Card
- href="https://github.com/pinapelz/Mirage"
- title="Mirage"
- body={`• Rhythm game score tracker that preserves scores across games - even niche ones
-• No reliance on predefined seeds or chart metadata
-• Import & track scores to keep a safe backup of your game progress
-• Support for any rhythm game, even without official metadata
-• Self-host for group tracking or use locally
-• Multi-user system with customizable permissions
-• Pre-loaded tracking for DANCERUSH, DANCE aROUND, Project DIVA, MUSIC DIVER, Nostalgia, and REFLEC BEAT`}
- language="TypeScript"
- languageColor="#3178c6"
- year="2024"
- image="/mirage.png"
- imageAlt="Mirage score tracker interface screenshot"
- tags={["React", "TypeScript", "Express", "Prisma ORM", "PostgreSQL"]}
- />
- <Card
- href="https://patchwork.moekyun.me"
- title="Patchwork Archive"
- body={`• Comprehensive archival system for YouTube videos at scale
-• React-based frontend for managing requests and viewing archived content
-• Python Flask backend API for authentication and database operations
-• Distributed worker system for processing archival jobs
-• Automatic metadata extraction and thumbnail preservation
-• Efficient storage using S3-compatible backends
-• Scalable architecture handling concurrent archival of thousands of videos`}
- language="Javascript"
- languageColor="#f1e05a"
- year="2023"
- image="/patchwork.png"
- imageAlt="Screenshot of Patchwork Archive site"
- tags={["React", "Python", "MySQL" , "S3 Storage", "TailwindCSS"]}
- />
- <Card
- href="https://github.com/pinapelz/tiny-time-tracker"
- title="tiny-time-tracker"
- body={`• Lightweight game time tracking application written in Rust
-• Designed specifically for Windows systems
-• Uses native process monitoring APIs instead of resource-intensive polling
-• Event-based detection of when applications start and stop
-• Clean web-based dashboard built with Askama templating
-• Styled with Tailwind CSS for responsive design
-• Detailed statistics about gaming habits and playtime`}
- language="Rust"
- languageColor="#dea584"
- image="/ttt.png"
- imagAlt="Tiny Time Tracker Web UI"
- year="2025"
- tags={["Rust", "Askama", "Tailwind", "Windows API", "SQLite"]}
- />
- <Card
- href="https://github.com/pinapelz/NijiTrack"
- title="Nijitrack"
- body={`• Data analytics platform for YouTube channel statistics
-• Focus on VTuber and content creator metrics tracking
-• Python backend for continuous data collection via YouTube API
-• Tracks subscriber counts, view metrics, and other channel data
-• Next.js frontend with interactive charts and historical analysis
-• Responsive dashboard for comparing multiple channels
-• Tools to identify growth trends and performance patterns`}
- language="Python"
- languageColor="#3572A5"
- year="2023"
- image="/phase-tracker.png"
- tags={["Python", "Next.js", "Tailwind", "SSR", "Chart.js", "PostgreSQL", "YouTube API"]}
- />
- <Card
- href="https://github.com/pinapelz/brokenithm-evolved-ios-umi"
- title="brokenithm-evolved-ios-umi"
- body={`• Low-level bridge application for iOS rhythm game controllers
-• Interfaces with the Brokenithm protocol for game input
-• Direct integration with chuniio hardware input systems
-• Translates UMIGURI LED lighting signals for iOS compatibility
+ <main>
+ <div class="header-container">
+ <h1 class="text-4xl font-semibold text-center py-6">Projects</h1>
+ <p class="text-center mb-8">
+ Here are some of my projects. I love tinkering with stuff so for
+ a more complete list, visit my <a
+ class="font-bold hover:underline hover:text-blue-300 transition-colors"
+ href="https://github.com/pinapelz">Github</a
+ >
+ <br />
+ <a
+ href="https://github.com/pulls/search?q=is%3Apr+is%3Amerged+author%3Apinapelz+-repo%3Apinapelz%2Fupptime+-repo%3Apinapelz%2Fpinapelz+-user%3Apinapelz+-org%3AAntAvionics"
+ class="mt-6 inline-block hover:underline text-center font-bold text-2xl animate-pulse hover:text-blue-300 transition-colors"
+ >Open Source Contributions</a
+ >
+ </p>
+ </div>
+
+ <ul role="list" class="project-grid">
+ <!-- 2026 -->
+ <Card
+ href="https://github.com/pinapelz/auto-live-tl"
+ title="auto-live-tl"
+ body={`• Local real-time subtitle translation for a PCM audio input using faster-whisper ASR
+• Optional LLM post-processing for cleanup and de-duplication
+• Live transcription with server-sent-events streaming to clients
+• Algorithmic filter for removing potential hallucinations and repetitions`}
+ language="Python"
+ languageColor="#3572A5"
+ image="/auto-livetl.png"
+ imagAlt="Auto Live TL subtitle demo using a YouTube Userscript client (French -> English)"
+ year="2026"
+ tags={[
+ "Python",
+ "OpenAI-Whisper",
+ "ASR",
+ "LLM"
+ ]}
+ />
+ <Card
+ href="https://github.com/pinapelz/lrc-karaoke-player"
+ title="lrc-karaoke-player"
+ body={`• Client-side web app for LRC-based karaoke playback and tooling
+• LRC-Player: synchronized scrolling lyrics with karaoke-style highlighting, with dual audio mixing
+• LRC-Type: typing game inspired by Typing-Tube using song lyrics as input
+• Shareable, codes, Fully client-side, no user data stored`}
+ language="TypeScript"
+ languageColor="#3178C6"
+ image="/lrc.png"
+ imagAlt="LRC Karaoke Player interface"
+ year="2026"
+ tags={[
+ "Next.js",
+ "TypeScript",
+ "LRC",
+ "Karaoke",
+ "Web Audio",
+ "Client-side"
+ ]}
+ />
+ <!-- 2025 -->
+ <Card
+ href="https://github.com/pinapelz/tiny-time-tracker"
+ title="tiny-time-tracker"
+ body={`• Lightweight game time tracker for Windows
+• Uses native APIs for event-based detection
+• Web-based dashboard with responsive design
+• Tracks detailed gaming statistics and playtime
+• Built with Rust and styled with Tailwind CSS`}
+ language="Rust"
+ languageColor="#dea584"
+ image="/ttt.png"
+ imagAlt="Tiny Time Tracker Web UI"
+ year="2025"
+ tags={["Rust", "Askama", "Tailwind", "Windows API", "SQLite"]}
+ />
+
+ <Card
+ href="https://github.com/pinapelz/brokenithm-evolved-ios-umi"
+ title="brokenithm-evolved-ios-umi"
+ body={`• Bridge app for iOS rhythm game controllers
• Real-time input translation with minimal latency
-• Accurate transmission of touch inputs to the game system
-• Custom protocol implementations for input capture and LED synchronization
-• Enables arcade-style gaming experiences using mobile devices`}
- language="Python"
- image="/brokenithm.png"
- imageAlt="Brokenithm-SwiftUI bridged via USB MUX into UMIGURI"
- languageColor="#3572A5"
- year="2025"
- tags={["Python", "C#"]}
- />
- <Card
- href="https://github.com/pinapelz/JHolodex"
- title="JHolodex"
- body={`• Object-oriented Java wrapper library for the Holodex API
-• Provides easy access to VTuber and content creator data
-• Built with Retrofit2 for efficient HTTP operations
-• Clean, intuitive interface for querying channel information
-• Access to video metadata and live stream data
-• Extensive documentation and comprehensive unit tests
-• Published on Maven Central for easy integration
-• Follows modern Java development practices and API design patterns`}
- language="Java"
- languageColor="#b07219"
- image="/jholodex.png"
- imageAlt="JHolodex Central Repository"
- year="2023"
- tags={["Java", "Retrofit2", "Maven Central", "REST API"]}
- />
- <Card
- href="https://github.com/pinapelz/573-updates"
- title="573-UPDATES"
- body={`• Modular web scraping and news aggregation system for arcade gaming
-• Python-based scraper monitoring multiple news sources
-• Tracks game updates, patch notes, and community announcements
-• Automatic parsing and standardization to JSON format
-• React single-page application built with TypeScript
-• Intuitive, filterable interface for browsing aggregated news
-• Extensible architecture for adding new data sources
+• Interfaces with chuniio hardware and UMIGURI LEDs
+• Translates LED signals for iOS compatibility
+• Enables arcade-style gaming on mobile devices`}
+ language="Python"
+ languageColor="#3572A5"
+ image="/brokenithm.png"
+ imageAlt="Brokenithm-SwiftUI bridged via USB MUX into UMIGURI"
+ year="2025"
+ tags={["Python", "C#"]}
+ />
+
+ <Card
+ href="https://github.com/pinapelz/573-updates"
+ title="573-UPDATES"
+ body={`• Web scraper for arcade gaming news and updates
+• Tracks patch notes, announcements, and community posts
+• Python backend with React frontend for browsing
+• Extensible architecture for adding new sources
• Automated deployment pipelines for content updates`}
- language="Typescript"
- languageColor="#3178c6"
- year="2025"
- image="/573.png"
- imageAlt="573-UPDATES site screenshot"
- tags={["React", "TypeScript", "Python", "Web Scraping"]}
- />
- <Card
- href="https://github.com/pinapelz/ffxiv-chronowatcher"
- title="ffxiv-chronowatcher"
- body={`• Precision-engineered Rust library for FFXIV calculations
-• Accurate implementation of Eorzean Time system
-• Weather forecasting for all game zones with perfect accuracy
-• Complex time conversion algorithms and weather generation systems
+ language="Typescript"
+ languageColor="#3178c6"
+ image="/573.png"
+ imageAlt="573-UPDATES site screenshot"
+ year="2025"
+ tags={["React", "TypeScript", "Python", "Web Scraping"]}
+ />
+
+ <!-- 2024 -->
+ <Card
+ href="https://github.com/pinapelz/Mirage"
+ title="Mirage"
+ body={`• Tracks rhythm game scores across multiple titles
+• Supports niche games without official metadata
+• Import and backup game progress from different games
+• Multi-user system with customizable permissions
+• Pre-loaded trackers for several popular rhythm games`}
+ language="TypeScript"
+ languageColor="#3178c6"
+ image="/mirage.png"
+ imageAlt="Mirage score tracker interface screenshot"
+ year="2024"
+ tags={[
+ "React",
+ "TypeScript",
+ "Express",
+ "Prisma ORM",
+ "PostgreSQL",
+ ]}
+ />
+
+ <Card
+ href="https://github.com/pinapelz/ffxiv-chronowatcher"
+ title="ffxiv-chronowatcher"
+ body={`• Rust library for FFXIV time and weather calculations
+• Implements Eorzean Time system with perfect accuracy
+• Weather forecasting for all game zones
• Comprehensive unit testing for reliability
-• Extensive documentation with practical examples
-• Published on crates.io for easy integration
-• Valuable for developers building FFXIV-related tools`}
- language="Rust"
- languageColor="#dea584"
- year="2024"
- image="/chronowatcher.png"
- imageAlt="Crates.io FFXIV-Chronowatcher"
- tags={["Rust", "Crates.io"]}
- />
- <Card
- href="https://blog.pinapelz.com"
- title="Personal Blog"
- body={`• Modern, performance-focused personal blog built with Astro
-• Platform for technical insights and project updates
-• MDX integration combining Markdown with React components
-• Rich content with embedded demos and interactive elements
-• Clean, responsive design with accessibility compliance
-• Excellent SEO optimization and fast loading times
-• Content spanning from technical tutorials to industry observations
-• Project breakdowns and software development experiences`}
- language="Astro"
- image="/blog.png"
- imageAlt="Personal Blog Site Screenshot"
- languageColor="#ff5a03"
- year="2023"
- tags={["Astro", "MDX", "React"]}
- />
- <Card
- href="https://github.com/pinapelz/ytmp3AutoTag"
- title="ytID3AutoTag"
- body={`• Java Swing desktop application for YouTube to MP3 conversion
-• Automatic ID3 metadata tagging from video information
-• Intelligent analysis of video titles and descriptions
-• Auto-population of artist names, song titles, and album info
-• User-friendly GUI designed for ease of use
-• Batch processing capabilities for multiple downloads
-• High audio quality preservation during conversion
-• Streamlined workflow for building organized music libraries`}
- language="Java"
- languageColor="#b07219"
- image="/yt.png"
- imageAlt="ytId3AutoTag Swing Metadata Editing GUI"
- year="2022"
- tags={["Java", "Swing"]}
- />
- <Card
- href="https://github.com/pinapelz/yet-another-lavaplayer-bot"
- title="Yet Another Lavaplayer Bot"
- body={`• Feature-rich, self-hosted Discord music bot
-• Built with Java Discord API (JDA) and Lavaplayer
-• Multi-source playback: YouTube, SoundCloud, Bandcamp, etc.
-• Advanced queue management and playlist support
-• Audio filtering and sound customization features
-• Comprehensive command handling system
-• User permission controls and moderation features
-• High-quality audio streaming for Discord servers`}
- language="Java"
- languageColor="#b07219"
- image="/lavaplayer.png"
- imageAlt="Example usage of Discord bot. Able to search + play songs on YouTube"
- year="2022"
- tags={["Java", "JDA", "Discord Bot", "Lavaplayer", "Audio Streaming", "Async Programming"]}
- />
- <Card
- href="https://github.com/pinapelz/moekyun-me-link-shortener"
- title="Moekyun Link Shortener"
- body={`• Self-hosted URL shortening service built with Flask
-• Designed for easy deployment on serverless platforms
-• Clean, minimalist interface with intuitive controls
-• Custom short URL generation and management
-• PostgreSQL for reliable data persistence
-• Redis caching for frequently accessed URLs
-• Fast response times even under heavy load
-• 1-click deployment through Vercel for easy setup`}
- language="Python"
- languageColor="#3572A5"
- year="2023"
- image="/link.png"
- imageAlt="Moekyun Link Shortener Screenshot"
- tags={["Python", "Flask", "PostgreSQL", "Redis"]}
- />
- <Card
- href="https://github.com/pinapelz/ffxiv-malmstone"
- title="Malmstone Calculator"
- body={`• Specialized FFXIV Dalamud plugin for PvP progression tracking
-• Real-time goal-setting functionality within the game interface
-• Built with ImGui for seamless UI integration
-• Hooks into game data to fetch necessary information
-• Calculates matches needed to reach player-defined goals
-• Customizable target tracking for different rewards
-• Match history analysis and performance metrics
-• Time-to-completion estimates based on win rate probabilities`}
- language="C#"
- languageColor="#178600"
- year="2023"
- image="/malmstone.png"
- imageAlt="FFXIV Malmstone Plugin Screenshot"
- tags={["C#", "ImGui"]}
- />
- </ul>
- <div class="text-center my-12">
- <a href="https://knowledge.pinapelz.com/personal/tools" class="inline-block hover:underline text-2xl animate-pulse transition-all hover:text-blue-300">and also a few smaller tools...</a>
- </div>
- </main>
- <SocialNavbar />
+• Published on crates.io for easy integration`}
+ language="Rust"
+ languageColor="#dea584"
+ image="/chronowatcher.png"
+ imageAlt="Crates.io FFXIV-Chronowatcher"
+ year="2024"
+ tags={["Rust", "Crates.io"]}
+ />
- <!-- Image Modal -->
- <div id="imageModal" class="modal">
- <span class="close-modal">&times;</span>
- <img class="modal-content" id="modalImage">
- <div id="modalCaption"></div>
- </div>
- <style>
- main {
- margin: auto;
- padding: 2rem 1.5rem;
- max-width: 1300px;
- color: white;
- font-size: 18px;
- line-height: 1.6;
- }
- a {
- color: white;
- transition: all 0.2s ease;
- }
- .header-container {
- margin-bottom: 2.5rem;
- border-bottom: 1px solid rgba(255, 255, 255, 0.1);
- padding-bottom: 1rem;
- }
- .project-grid {
- display: grid;
- grid-template-columns: repeat(1, 1fr);
- gap: 2.5rem;
- padding: 0;
- list-style: none;
- margin-bottom: 2rem;
- }
+ <!-- 2023 -->
+ <Card
+ href="https://patchwork.moekyun.me"
+ title="Patchwork Archive"
+ body={`• Scalable system for archiving YouTube videos
+• React frontend for managing requests and archives
+• Distributed workers for processing archival jobs
+• Automatic metadata extraction and thumbnail storage
+• Efficient storage using S3-compatible backends`}
+ language="Javascript"
+ languageColor="#f1e05a"
+ image="/patchwork.png"
+ imageAlt="Screenshot of Patchwork Archive site"
+ year="2023"
+ tags={["React", "Python", "MySQL", "S3 Storage", "TailwindCSS"]}
+ />
- /* Bullet point styling */
- .bullet-style {
- position: relative;
- padding-left: 1.25rem;
- margin-bottom: 0.5rem;
- line-height: 1.5;
- }
+ <Card
+ href="https://github.com/pinapelz/NijiTrack"
+ title="Nijitrack"
+ body={`• Analytics platform for YouTube channel metrics
+• Tracks subscribers, views, and historical trends
+• Python backend with Next.js frontend and interactive charts
+• Dashboard for comparing multiple channels
+• Tools for growth analysis and performance insights`}
+ language="Python"
+ languageColor="#3572A5"
+ image="/phase-tracker.png"
+ year="2023"
+ tags={[
+ "Python",
+ "Next.js",
+ "Tailwind",
+ "SSR",
+ "Chart.js",
+ "PostgreSQL",
+ "YouTube API",
+ ]}
+ />
- .bullet-style::before {
- content: "•";
- position: absolute;
- left: 0;
- color: rgb(139, 92, 246);
- font-weight: bold;
- }
+ <Card
+ href="https://github.com/pinapelz/JHolodex"
+ title="JHolodex"
+ body={`• Java wrapper for the Holodex API
+• Simplifies access to VTuber and creator data
+• Built with Retrofit2 for efficient HTTP calls
+• Exposes video metadata and live stream info
+• Published on Maven Central with docs and tests`}
+ language="Java"
+ languageColor="#b07219"
+ image="/jholodex.png"
+ imageAlt="JHolodex Central Repository"
+ year="2023"
+ tags={["Java", "Retrofit2", "Maven Central", "REST API"]}
+ />
- @media (min-width: 768px) {
- .project-grid {
- grid-template-columns: repeat(2, 1fr);
- gap: 2rem 2.5rem;
- }
- }
+ <Card
+ href="https://blog.pinapelz.com"
+ title="Personal Blog"
+ body={`• Performance-focused blog built with Astro
+• Combines MDX with React components for richer posts
+• Technical tutorials, project write-ups, and demos
+• Optimized for SEO and fast loading times`}
+ language="Astro"
+ languageColor="#ff5a03"
+ image="/blog.png"
+ imageAlt="Personal Blog Site Screenshot"
+ year="2023"
+ tags={["Astro", "MDX", "React"]}
+ />
- @media (min-width: 1200px) {
- .project-grid {
- grid-template-columns: repeat(2, 1fr);
- gap: 3rem;
- }
- }
+ <Card
+ href="https://github.com/pinapelz/ffxiv-malmstone"
+ title="Malmstone Calculator"
+ body={`• FFXIV Dalamud plugin for PvP progression tracking
+• Calculates matches needed to reach player goals
+• Real-time goal tracking and match history analysis
+• Built with ImGui for in-game UI integration
+• Provides time-to-completion estimates based on win rate`}
+ language="C#"
+ languageColor="#178600"
+ image="/malmstone.png"
+ imageAlt="FFXIV Malmstone Plugin Screenshot"
+ year="2023"
+ tags={["C#", "ImGui"]}
+ />
- /* Modal Styles */
- .modal {
- display: none;
- position: fixed;
- z-index: 1500;
- padding-top: 50px;
- left: 0;
- top: 0;
- width: 100%;
- height: 100%;
- overflow: auto;
- background-color: rgba(0, 0, 0, 0.9);
- opacity: 0;
- transition: opacity 0.3s ease;
- }
+ <!-- 2022 -->
+ <Card
+ href="https://github.com/pinapelz/ytmp3AutoTag"
+ title="ytID3AutoTag"
+ body={`• Java Swing app for YouTube-to-MP3 conversion
+• Automatically tags ID3 metadata from video info
+• Batch processing for many files at once
+• Preserves audio quality during conversion
+• GUI optimized for quick metadata edits`}
+ language="Java"
+ languageColor="#b07219"
+ image="/yt.png"
+ imageAlt="ytId3AutoTag Swing Metadata Editing GUI"
+ year="2022"
+ tags={["Java", "Swing"]}
+ />
- .modal-visible {
- opacity: 1;
- display: block;
- }
+ <Card
+ href="https://github.com/pinapelz/yet-another-lavaplayer-bot"
+ title="Yet Another Lavaplayer Bot"
+ body={`• Self-hosted Discord music bot with multi-source playback
+• Supports YouTube, SoundCloud, Bandcamp, and more
+• Advanced queue management and audio filtering
+• Built with JDA and Lavaplayer for reliable streaming
+• Includes permission controls and moderation features`}
+ language="Java"
+ languageColor="#b07219"
+ image="/lavaplayer.png"
+ imageAlt="Example usage of Discord bot. Able to search + play songs on YouTube"
+ year="2022"
+ tags={[
+ "Java",
+ "JDA",
+ "Discord Bot",
+ "Lavaplayer",
+ "Audio Streaming",
+ "Async Programming",
+ ]}
+ />
+ </ul>
- .modal-content {
- margin: auto;
- display: block;
- max-width: 90%;
- max-height: 80vh;
- opacity: 0;
- transition: opacity 0.3s ease;
- }
+ <div class="text-center my-12">
+ <a
+ href="https://knowledge.pinapelz.com/personal/tools"
+ class="inline-block hover:underline text-2xl animate-pulse transition-all hover:text-blue-300"
+ >and also a few smaller tools...</a
+ >
+ </div>
+ </main>
- .modal-content-visible {
- opacity: 1;
- }
+ <SocialNavbar />
- .close-modal {
- position: absolute;
- top: 15px;
- right: 35px;
- color: #f1f1f1;
- font-size: 40px;
- font-weight: bold;
- transition: 0.3s;
- cursor: pointer;
- z-index: 1001;
- }
+ <!-- Image Modal -->
+ <div id="imageModal" class="modal">
+ <span class="close-modal">&times;</span>
+ <img class="modal-content" id="modalImage" />
+ <div id="modalCaption"></div>
+ </div>
- .close-modal:hover,
- .close-modal:focus {
- color: #bbb;
- text-decoration: none;
- cursor: pointer;
- }
+ <style>
+ main {
+ margin: auto;
+ padding: 2rem 1.5rem;
+ max-width: 1300px;
+ color: white;
+ font-size: 18px;
+ line-height: 1.6;
+ }
+ a {
+ color: white;
+ transition: all 0.2s ease;
+ }
+ .header-container {
+ margin-bottom: 2.5rem;
+ border-bottom: 1px solid rgba(255, 255, 255, 0.1);
+ padding-bottom: 1rem;
+ }
+ .project-grid {
+ display: grid;
+ grid-template-columns: repeat(1, 1fr);
+ gap: 2.5rem;
+ padding: 0;
+ list-style: none;
+ margin-bottom: 2rem;
+ }
- #modalCaption {
- margin: auto;
- display: block;
- width: 80%;
- max-width: 700px;
- text-align: center;
- color: #ccc;
- padding: 10px 0;
- height: 150px;
- }
- </style>
+ /* Bullet point styling */
+ .bullet-style {
+ position: relative;
+ padding-left: 1.25rem;
+ margin-bottom: 0.5rem;
+ line-height: 1.5;
+ }
- <script>
- document.addEventListener('DOMContentLoaded', function() {
- // Get all project images
- const projectImages = document.querySelectorAll('.image-container');
- const modal = document.getElementById('imageModal');
- const modalImg = document.getElementById('modalImage');
- const modalCaption = document.getElementById('modalCaption');
- const closeBtn = document.querySelector('.close-modal');
+ .bullet-style::before {
+ content: "•";
+ position: absolute;
+ left: 0;
+ color: rgb(139, 92, 246);
+ font-weight: bold;
+ }
- // Add click event to each project image
- projectImages.forEach(imgContainer => {
- imgContainer.addEventListener('click', function(e) {
- e.preventDefault();
- e.stopPropagation();
+ @media (min-width: 768px) {
+ .project-grid {
+ grid-template-columns: repeat(2, 1fr);
+ gap: 2rem 2.5rem;
+ }
+ }
- const img = this.querySelector('img');
- modal.style.display = "block";
- setTimeout(() => {
- modal.classList.add('modal-visible');
- }, 10);
+ @media (min-width: 1200px) {
+ .project-grid {
+ grid-template-columns: repeat(2, 1fr);
+ gap: 3rem;
+ }
+ }
- modalImg.src = img.src;
- modalImg.alt = img.alt;
- setTimeout(() => {
- modalImg.classList.add('modal-content-visible');
- }, 50);
+ /* Modal Styles */
+ .modal {
+ display: none;
+ position: fixed;
+ z-index: 1500;
+ padding-top: 50px;
+ left: 0;
+ top: 0;
+ width: 100%;
+ height: 100%;
+ overflow: auto;
+ background-color: rgba(0, 0, 0, 0.9);
+ opacity: 0;
+ transition: opacity 0.3s ease;
+ }
- modalCaption.textContent = img.alt;
+ .modal-visible {
+ opacity: 1;
+ display: block;
+ }
- return false;
- });
- });
+ .modal-content {
+ margin: auto;
+ display: block;
+ max-width: 90%;
+ max-height: 80vh;
+ opacity: 0;
+ transition: opacity 0.3s ease;
+ }
- // Close modal when clicking the X
- closeBtn.addEventListener('click', closeModal);
+ .modal-content-visible {
+ opacity: 1;
+ }
- // Close modal when clicking outside the image
- modal.addEventListener('click', function(event) {
- if (event.target === modal) {
- closeModal();
+ .close-modal {
+ position: absolute;
+ top: 15px;
+ right: 35px;
+ color: #f1f1f1;
+ font-size: 40px;
+ font-weight: bold;
+ transition: 0.3s;
+ cursor: pointer;
+ z-index: 1001;
}
- });
- // Close modal with Escape key
- document.addEventListener('keydown', function(event) {
- if (event.key === 'Escape' && modal.style.display === 'block') {
- closeModal();
+ .close-modal:hover,
+ .close-modal:focus {
+ color: #bbb;
+ text-decoration: none;
+ cursor: pointer;
}
- });
- function closeModal() {
- modal.classList.remove('modal-visible');
- modalImg.classList.remove('modal-content-visible');
- setTimeout(() => {
- modal.style.display = "none";
- }, 300);
- }
- });
- </script>
+ #modalCaption {
+ margin: auto;
+ display: block;
+ width: 80%;
+ max-width: 700px;
+ text-align: center;
+ color: #ccc;
+ padding: 10px 0;
+ height: 150px;
+ }
+ </style>
+
+ <script>
+ document.addEventListener("DOMContentLoaded", function () {
+ // Get all project images
+ const projectImages = document.querySelectorAll(".image-container");
+ const modal = document.getElementById("imageModal");
+ const modalImg = document.getElementById("modalImage");
+ const modalCaption = document.getElementById("modalCaption");
+ const closeBtn = document.querySelector(".close-modal");
+
+ // Add click event to each project image
+ projectImages.forEach((imgContainer) => {
+ imgContainer.addEventListener("click", function (e) {
+ e.preventDefault();
+ e.stopPropagation();
+
+ const img = this.querySelector("img");
+ modal.style.display = "block";
+ setTimeout(() => {
+ modal.classList.add("modal-visible");
+ }, 10);
+
+ modalImg.src = img.src;
+ modalImg.alt = img.alt;
+ setTimeout(() => {
+ modalImg.classList.add("modal-content-visible");
+ }, 50);
+
+ modalCaption.textContent = img.alt;
+
+ return false;
+ });
+ });
+
+ // Close modal when clicking the X
+ closeBtn.addEventListener("click", closeModal);
+
+ // Close modal when clicking outside the image
+ modal.addEventListener("click", function (event) {
+ if (event.target === modal) {
+ closeModal();
+ }
+ });
+
+ // Close modal with Escape key
+ document.addEventListener("keydown", function (event) {
+ if (event.key === "Escape" && modal.style.display === "block") {
+ closeModal();
+ }
+ });
+
+ function closeModal() {
+ modal.classList.remove("modal-visible");
+ modalImg.classList.remove("modal-content-visible");
+ setTimeout(() => {
+ modal.style.display = "none";
+ }, 300);
+ }
+ });
+ </script>
</Layout>
send patches to the email below
yukais@pinapelz.com
include the subject [PATCH repo_name]
pinapelz.com
homepage