Mercurial
changeset 109:1c446ab6f945
[MrJuneJune] New Blog about writing Seobeo library.
line wrap: on
line diff
--- a/hg-web/main.c Sat Jan 03 17:29:12 2026 -0800 +++ b/hg-web/main.c Sat Jan 03 19:37:51 2026 -0800 @@ -286,7 +286,8 @@ snprintf(command, sizeof(command), "hg -R %s serve --stdio 2>&1", REPO_ROOT); FILE *hg_pipe = popen(command, "r+"); - if (!hg_pipe) { + if (!hg_pipe) + { Seobeo_Log(SEOBEO_DEBUG, "Failed to open pipe\n"); return resp; }
--- a/mrjunejune/src/blog/index.html Sat Jan 03 17:29:12 2026 -0800 +++ b/mrjunejune/src/blog/index.html Sat Jan 03 19:37:51 2026 -0800 @@ -9,6 +9,10 @@ <h1 class="title" style="margin-bottom: 20px;"> Blogs </h1> <ul style="list-style: none;"> <li style="margin-bottom: 20px;"> + <span> January 02 2026 </span> + <p><h4><a href="/blog/my-seobeo-journey">Creating Network Library in C</a></h4></p> + </li> + <li style="margin-bottom: 20px;"> <span> Apr 12 2025 </span> <p><h4><a href="/blog/wsl2-ssh">WSL2 Cloudtop Setup</a></h4></p> </li>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mrjunejune/src/blog/my-seobeo-journey/index.html Sat Jan 03 19:37:51 2026 -0800 @@ -0,0 +1,20 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <title>Creating Network Library in C</title> + {{/parts/base_head.html}} + <link rel="stylesheet" href="/tools/markdown_to_html/index.css" /> +</head> +<body> + {{/parts/header.html}} + <main> + <div id="content"></div> + </main> + {{/parts/footer.html}} + <script src="/markdown_to_html.js"></script> + <script> + fetch('/blog/my-seobeo-journey/index.md').then(res => res.text()).then(text => renderMarkdown(content, text)); + </script> +</body>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mrjunejune/src/blog/my-seobeo-journey/index.md Sat Jan 03 19:37:51 2026 -0800 @@ -0,0 +1,127 @@ +# Creating Network Library in C + +I’m so tired of modern web development. You want to spin up a simple web server, and suddenly you’re wrestling with a mountain of dependencies. Express, Fastify, whatever the flavor of the week is. They all promise simplicity, but dump a black hole of `node_modules` onto your machine. Have you ever actually looked inside? It’s insane. And all of them use React, Svelte, or whatever the current trend is for modern development. One time, I created a single React file using Bun and it had over 18k lines of JavaScript code lmao. And for what? To handle a simple HTTP request for a text file? + +<img src="/public/23k.png" /> + +** It was actually 23K lines long lol.. you can test it [here](https://zenbu.babocoder.com/file/tip/playground). Just clone the repo and run `bazel build playground:hello` ** + + +## So, I decided to build my own network library from scratch, in C. + +I call it `seobeo` because that is how Korean people say "server" with an accent. The goal is simple: create something fast, lightweight, and completely transparent. No magic, no hidden layers. Just a pure, unadulterated networking layer, with a web server interface built on top of it. + +## Where to begin? + +I’d never really dealt with network libraries, nor had I written much C outside of a few contract jobs, so starting was a learning curve. I did a quick Google search and found an [amazing guide to network programming](https://beej.us/guide/bgnet/html/split/). In it, Beej talks about networking from scratch and explains many socket library functions in detail. He provides examples and explains the "why" behind the setup, so I highly recommend people check that out first. + +After that, I looked at a few examples from [Eskil Steenberg’s](https://www.google.com/search?q=https://www.quelsolaar.com/about/index.html) [GitHub repo](https://github.com/valiet/quel_solaar/tree/main/Testify), which was super useful for seeing how he approached API design. + +## The Core Concept + +The whole library is built around a central `Seobeo_Handle` struct. This struct handles all server-related information, holding everything from the socket file descriptor to the read/write buffers, as well as SSL-related data. This allows API callers to decide how they want to serialize the bytes and write/read from them. + +```c +typedef struct { + int32 socket; + Seobeo_SocketType type; + boolean connected; + + char *host; + char *port; + + uint8 *read_buffer; + uint32 read_buffer_capacity; + uint32 read_buffer_len; + uint32 read_buffer_used; + + SSL_CTX_TYPE *ssl_ctx; + SSL_TYPE *ssl; + + uint8 *write_buffer; + uint32 write_buffer_capacity; + uint32 write_buffer_len; + + void *file; + void *text_copy; + char *file_name; + + atomic_bool destroyed; +} Seobeo_Handle; + +``` + +## Creating the Web Server Library + +From here, it was incredibly simple to create the web client and server logic, since all I had to do was send a string of bytes to request information. It was refreshing to be able to set exact header values without going through a library; receiving information byte-by-byte was actually fun. + +```c +// Manually craft the raw HTTP request string +char request[4096]; +int request_len = sprintf( + request, + "GET / HTTP/1.1\r\n" + "Host: example.com\r\n" + "Connection: close\r\n" + "\r\n" +); + +// Queue the raw text... +Seobeo_Handle_Queue(h, (uint8*)request, (uint32)request_len); +// ...and flush it to the socket. +int flush_result = Seobeo_Handle_Flush(h); + +``` + +**While the logic is simple, I made plenty of mistakes along the way. I eventually had to create my own utility library (using Arenas, dynamic arrays, and hashmaps) just to make implementing the basic logic easier to manage.** + +Things only got complicated when I had to deal with SSL, as I wanted to be able to make encrypted calls, but I just had to hunker down and learn how SSL worked. + +On the server side, I kept the API dead simple: the server is granted access to a folder, and you can add your own router logic before initializing the server. As you can see below, your custom routing is always checked first, followed by static paths. I had to write a lot of code to handle different data types, but the logic remained straightforward. If you want to add a custom header, you just add it to the response hashmap. + +```c +#include "seobeo/seobeo.h" + +// Handler for GET /api/v1/hello +Seobeo_Request_Entry* GetHello(Seobeo_Request_Entry *req, Dowa_Arena *arena) { + Seobeo_Request_Entry *resp = NULL; + const char *body = "{\"message\": \"Hello from Seobeo!\"}"; + + // Build the response map + Dowa_HashMap_Push_Arena(resp, "status", "200", arena); + Dowa_HashMap_Push_Arena(resp, "content-type", "application/json", arena); + Dowa_HashMap_Push_Arena(resp, "body", body, arena); + + return resp; +} + +int main() { + Seobeo_Router_Init(); + // Register the route + Seobeo_Router_Register("GET", "/api/v1/hello", GetHello); + // Start the server + Seobeo_Web_Server_Start("./public", "8080", SEOBEO_MODE_EDGE, 4); + return 0; +} + +``` + +No dependency injection, no middleware chains, no magic. Just a function that takes a request and returns a response. I updated the API so it can run as a multi-threaded, asynchronous process using system-level libraries like `epoll` or `kqueue`. I tested it with `Locust` at over 2,000 requests per second (my internet’s limit, lol) without Cloudflare, and it worked perfectly. + +## The Payoff and Trade-offs + +But here’s the best part: the final executable for my entire website, running on `seobeo`, is just **81 kB** (excluding static assets) and uses only **1.1 MB** of memory before server-side caching. + +That’s smaller than the `package.json` file of most modern web projects. It’s a rounding error in the world of Express or Python web frameworks. I understand that it doesn't have every feature, and people might worry about security (though I’m not too concerned since the server runs as a limited user with just static files), but it’s still a massive victory in my book. + +I know what you’re thinking. "But you have to write so much code" or "This code sucks" or whatever. And you're probably right. I have to manually craft HTTP headers. I have to manage my own memory with arena allocators. I even wrote my own integration test framework just to verify the HTTP client. + +But I’ll take that trade-off any day of the week. + +Why? Because I actually know what’s going on. The whole stack is right there in front of me. Plus, in the age of cloud infrastructure, most of the complex stuff is handled upstream anyway. My typical setup is **Cloudflare -> Nginx -> my server**. Cloudflare handles HTTPS; Nginx handles proxying and load balancing. Why should my application server bother with SSL handshakes when it’s sitting deep inside a trusted network? It’s redundant and just adds another layer of complexity. + +FYI, I am running this website on seobeo library and everything you see in this website from start to finish is written by me from now on. + +## TODOs? + +I’m planning to build out a WebSocket layer and an MCP (Message Control Protocol) implementation for some other crazy projects I have in mind, just to see how complicated they are to write because I like writing code. [Link to Library](https://zenbu.babocoder.com/file/tip/seobeo);
--- a/mrjunejune/test/README.md Sat Jan 03 17:29:12 2026 -0800 +++ b/mrjunejune/test/README.md Sat Jan 03 19:37:51 2026 -0800 @@ -99,17 +99,3 @@ # Run tests to verify bazel test //mrjunejune:integration_test ``` - -### When Adding New Endpoints -1. Update `main.c` with new route -2. Add test case to `integration_test.c` -3. Add snapshot config to `create_snapshots.c` (for GET) -4. Regenerate snapshots -5. Run tests - -## Notes - -- Tests require FFmpeg to be installed for video/image conversion tests -- Server runs on port 6969 during tests -- Test files are cleaned up automatically after download -- Converted files are stored in `/tmp/` during tests
--- a/mrjunejune/test/snapshots/index.html.snapshot Sat Jan 03 17:29:12 2026 -0800 +++ b/mrjunejune/test/snapshots/index.html.snapshot Sat Jan 03 19:37:51 2026 -0800 @@ -4,7 +4,10 @@ Connection: close Body: -t/otf" crossorigin> +"font/woff" crossorigin> +<link rel="preload" href="/public/fonts/atkinson-bold.woff" as="font" type="font/woff" crossorigin> + +<link rel="preload" href="/public/fonts/more-sugar.extras.otf" as="font" type="font/otf" crossorigin> <link rel="preload" href="/public/fonts/more-sugar.regular.otf" as="font" type="font/otf" crossorigin> <link rel="preload" href="/public/fonts/more-sugar.thin.otf" as="font" type="font/otf" crossorigin> @@ -12,16 +15,10 @@ <link rel="stylesheet" href="/base.css" /> + <link rel="stylesheet" href="markdown_to_html/index.css" /> +</head> +<body> <style> - .epi-photo { - display: flex; - justify-content: center; - margin-bottom: 10px; - } - </style> - </head> - <body> - <style> :root { --header-background: var(--white); --header-color: rgb(var(--black)); @@ -159,44 +156,290 @@ <script src="/index.js"></script> - <main> - <p>Hi, my name is Juntae, but most people call me June or MrJuneJune.</p> + <div class="header"> + <h1>Markdown to HTML Converter</h1> + </div> + + <div class="container"> + <div class="panel"> + <div class="label">Markdown Input</div> + <textarea id="input" placeholder="Type your markdown here..."># Welcome to Markdown Converter + +## Features + +This converter supports: - <p>I am a software engineer with experience spanning a wide range of companies, from small startups to FAANGs....</p> - <p>I know it is lame to work for them, but I have a dog so I need to put foods on my table.</p> +- **Bold text** and *italic text* +- [Links](https://example.com) +- `inline code` +- ~~strikethrough~~ + +### Lists - <div class="epi-photo"> - <img id="currentPhoto" style="opacity: 0; transition: opacity 0.2s;" /> - </div> +1. Ordered lists +2. Like this one +3. With numbers + +- Unordered lists +- Use dashes +- Or asterisks + +### Code Blocks - <p>During my free time, I like to write codes mostly in C, Python, and Typescript. All in mono repo styles using mercurial and bazel. (I know that is mentally ill...)</p> - <p>Feel free to check it out. My bad code..</p> +``` +function example() { + return "Hello World"; +} +``` + +### Blockquotes + +> This is a blockquote +> It can span multiple lines + +--- + +### Images - <h2>Links</h2> - <ul> - <li><a href="https://zenbu.babocoder.com/file/tip">Repository</a> - Check out my monorepo code</li> - <li><a href="/resume">Resume</a> - My professional experiences </li> - <li><a href="/tools">Tools</a> - Tools </li> - <!-- <li><a href="/blogs">Blogs</a> - Personal Blogs where I rant </li> --> - </ul> - </main> - <div style="display: flex; align-items: center; justify-content: center; margin: 30px 0px;"> + + +**Try editing this text!**</textarea> + </div> + + <div class="panel"> + <div class="title"> + <div class="label">HTML Output</div> + <button id="copy"> Copy </button> + </div> + <div id="output"></div> + </div> + </div> + <div style="display: flex; align-items: center; justify-content: center; margin: 30px 0px;"> <small>© 2026 June Park</small> </div> - </body> - <script> - let arr = Array.from({ length: 18 }, (_, i) => i+1); - function setRandomImages() { - const randomIndex = Math.floor(Math.random() * arr.length); - const pos = arr[randomIndex]; - currentPhoto.src = `/public/epi-photos/webp/${pos}.webp`; - currentPhoto.onload = () => { - currentPhoto.style.opacity = "1"; - }; - setTimeout(() => setRandomImages(), 1000); - } - setRandomImages(); - </script> + <script src="/markdown_to_html.js"></script> + <script> + function convert() { + output.innerHTML = ''; + const markdown = input.value; + renderMarkdown(output, markdown); + } + input.addEventListener('input', convert); + + convert(); + + copy.addEventListener('click', () => { + const htmlBlob = new Blob([output.innerHTML], { type: 'text/html'}); + const textBlob = new Blob([output.innerText], { type: 'text/plain'}); + const data = [new ClipboardItem({ + 'text/html': htmlBlob, + 'text/plain': textBlob + })]; + navigator.clipboard.write(data).then(() => { + copy.textContent = "Copied!"; + setTimeout(() => { + copy.textContent = "Copy"; + copy.classList.remove('success'); + }, 1000); + }).catch(err => { + console.error('Failed to copy: ', err); + }); + }); + </script> +</body> </htmlLocation: / + <p> + <span class="entry-title-style">Tools & Platforms:</span> + <span class="skill-type-style">Bazel, PostgresSQL, Mercurial, Git, Pands, Raylib, XCode</span> + </p> + <p> + <span class="entry-title-style"> Web Frameworks: </span> + <span class="skill-type-style"> Django, Rails, React, Flask</span> + </p> + <p> + <span class="entry-title-style"> DevOp:</span> + <span class="skill-type-style"> Plummi, Heroku, DigitalOcean, AWS, Google Cloud </span> + </p> + <p> + <span class="entry-title-style"> Language:</span> + <span class="skill-type-style"> English, Korean, Japanese </span> + </p> + </div> + + <!-- Experiences --> + <div class="sub-header"> + <h2 class="section-style"> Experience </h2> + <div class="line"></div> + </div> + <div class="flex-box"> + <p class="entry-title-style"> + <a href="https://www.meta.com/">Meta</a> + </p> + <p class="entry-location-style">San Francisco, CA, USA</p> + </div> + <div class="flex-box"> + <p class="entry-position-style">SOFTWARE ENGINEER</p> + <p class="entry-date-style">Oct, 2024 - Present</p> + </div> + <ul class="description-style"> + <li> + Took initiative on Channel Value Rule, targeting the 16% of ad traffic with both app and web destinations to improve value attribution and ROI. + </li> + <li> + Built full-stack features using React and Hack/GraphQL, contributing to scalable, production-ready systems. + </li> + <li> + Partnered with data science to design A/B tests and analyze revenue impact of ads destination. + </li> + <li> + Proposed and implemented alpha improvements to internal testing infrastructure, reducing test time by 50% and enhancing developer velocity. + </li> + </ul> + <div class="flex-box"> + <p class="entry-title-style"> + <a href="https://www.wmg.com/">Warner Music Group</a> + </p> + <p class="entry-location-style">Toronto, ON, Canada</p> + </div> + <div class="flex-box"> + <p class="entry-position-style">TECHNICAL LEAD ENGINEER</p> + <p class="entry-date-style">July, 2023 - Sept, 2024</p> + </div> + <ul class="description-style"> + <li> + Implements <a href="https://bazel.build/">bazel </a>structure for the company for TypeScript and JavaScript code base for hermiticity and stablishing standards for JavaScript and + </li> + <li> + TypeScript testing and code structures. + </li> + <li> + Led a team of five engineers in building GraphQL endpoints for client-facing applications using Apollo and AppSync, supporting over 2000 RPS and auto scaling depending on request values. + </li> + <li> + Improved application response times by up to 85% for graphQL response by updating database schema and SQL queries, eliminating N+1 queries and lack of indexes. + </li> + <li> + Developed CI/CD pipelines for backend structures. + </li> + <li> + Designed infrastructure for pub/sub, caching, and media processing logic. + </li> + </ul> + + <div class="flex-box"> + <p class="entry-title-style"> + <a href="https://www.google.com/">Google</a> + </p> + <p class="entry-location-style">Toronto, ON, Canada</p> + </div> + <div class="flex-box"> + <p class="entry-position-style">SOFTWARE ENGINEER</p> + <p class="entry-date-style">Feb, 2022 - July 2023</p> + </div> + <ul class="description-style"> + <li> + Implements and maintained new features relating to App Script across google workspace platform including Gmail, sheets, and Docs.</li> + <li> + Improved a response time and render time of App Script hover card components.</li> + <li> + Collaborated with a team of developers to ensure timely and accurate delivery of features.</li> + <li> + Conducted user testing and gathered feedback to iterate on features for optimal user experience.</li> + </ul> + + <div class="flex-box"> + <p class="entry-title-style"> + <a href="https://www.everlywell.com/">Everlywell</a> + </p> + <p class="entry-location-style">Toronto, ON, Canada</p> + </div> + <div class="flex-box"> + <p class="entry-position-style">SOFTWARE ENGINEER</p> + <p class="entry-date-style">December, 2020 - Jan, 2022</p> + </div> + <ul class="description-style"> + <li> + Maintained Amazon amplify apps to create and deploy React web applications for companies such as <a href="https://brooklynnets.everlywell.com/">NBA</a>, <a href="https://tinder.everlywell.com/">Tinder</a>, and other companies for COVID-19 at-home test kits.</li> + <li> + Implemented a script that helps accurately access and refund unused covid test kits; helping company save up to 200,000 USD.</li> + <li> + Created several Rails controllers for internal purposes; mocking end to end user experience for QA, mass refund features for CX department, and more, ultimately reducing support tickets amount by 50 percent.</li> + <li> + Implemented an audit table to help debug problems and logged which process was responsible for the change of the record using PaperTrail gems</li> + </ul> + + <div class="flex-box"> + <p class="entry-title-style"> + <a href="https://www.spiria.com/">Spiria</a> + </p> + <p class="entry-location-style">Oakville, ON, Canada</p> + </div> + <div class="flex-box"> + <p class="entry-position-style">SOFTWARE ENGINEER</p> + <p class="entry-date-style">October, 2018 - October, 2020</p> + </div> + <ul class="description-style"> + <li> + Constructed RESTful API endpoints in multiple different frameworks such as Django, Ruby on Rails, and Flask and automated API documentation process using swagger. + </li> + <li> + Designed custom rake tasks for importing production data into newly updated data structure to meet client's needs. + </li> + <li> + Maintained or updated staging/productions servers. Debugged problems in production postgres database using ssh and postgres console on Heroku or AWS servers + </li> + <li> + Collaborated in creating automation python scripts for websites and application using selenium covering for QA eliminating 80% of QA's manual work + </li> + </ul> + + <div class="flex-box"> + <p class="entry-title-style"> + <a href="https://www.apexscore.ai/">Apex Score</a> + </p> + <p class="entry-location-style">Oakville, ON, Canada</p> + </div> + <div class="flex-box"> + <p class="entry-position-style">SOFTWARE ENGINEER</p> + <p class="entry-date-style">September, 2019 - October, 2020</p> + </div> + <ul class="description-style"> + <li> + Developed custom Shapley value regression model to calculate importance of independent variables of data sets using sklearn, pandas, and numpy. + </li> + <li> + Created custom image uploader to Amazon s3 bucket using boto3 library. + </li> + <li> + Built RESTful API application using Flask framework and automated extensive API documentation pages using flask-restplus, pytest, and swagger, covering 95% of the code base. + </li> + <li> + Created an interactive graph using D3.js in Vue.js with data from Flask backend API. + </li> + </ul> + + <div class="sub-header"> + <h2 class="section-style"> Education </h2> + <div class="line"></div> + </div> + <div class="flex-box"> + <p class="entry-title-style"> + University of British Columbia + </p> + <p class="entry-location-style">Kelowna, British Columbia</p> + </div> + <div class="flex-box"> + <p class="entry-position-style">BACHELOR OF SCIENCE IN PHYSICS</p> + <p class="entry-date-style">2014 - 2018</p> + </div> + <div id="footer"></div> + </main> + <div style="display: flex; align-items: center; justify-content: center; margin: 30px 0px;"> + <small>© 2026 June Park</small> +</div> + + <script href="index.js"></script> + </body> +</html>
--- a/mrjunejune/test/snapshots/resume.snapshot Sat Jan 03 17:29:12 2026 -0800 +++ b/mrjunejune/test/snapshots/resume.snapshot Sat Jan 03 19:37:51 2026 -0800 @@ -1,12 +1,14 @@ HTTP/1.1 200 OK Content-Type: text/html -Content-Length: 15108 +Content-Length: 14484 Connection: close <!doctype html> <html lang="en"> <head> - <link rel="icon" type="image/svg+xml" href="/public/epi_all_colors.svg"> + <meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<link rel="icon" type="image/svg+xml" href="/public/epi_all_colors.svg"> <link rel="preload" href="/public/fonts/Roboto-Regular.ttf" as="font" crossorigin> <link rel="preload" href="/public/fonts/Roboto-Thin.ttf"as="font" crossorigin> @@ -174,20 +176,21 @@ Bay Area, CA, USA </div> <div class="header-social-style"> - <p> <a href="tel:+016505311728">(US) 650-531-1728</a> | <a href="tel:+014375808026">(CA) 437-580-8026</a> | <a href="mailto:[email protected]"> [email protected] </a>| <a href="https://github.com/mrjunejune"> - <svg viewBox="0 0 16 16" aria-hidden="true" width="12" height="12"> - <path - fill="currentColor" - d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.012 8.012 0 0 0 16 8c0-4.42-3.58-8-8-8z" - ></path> - </svg> mrjunejune - </a>| <a href="https://www.linkedin.com/in/junepark"> - <svg width="12" height="12" fill="currentColor" class="bi bi-linkedin" viewBox="0 0 16 16"> + <div> + <a href="tel:+016505311728">(US) 650-531-1728</a> + </div> + <div> + <a href="tel:+014375808026">(CA) 437-580-8026</a> + </div> + <div> + <a href="mailto:[email protected]">[email protected]</a> + </div> + <div> + <a href="https://www.linkedin.com/in/junepark"><svg width="12" height="12" fill="currentColor" class="bi bi-linkedin" viewBox="0 0 16 16"> <path d="M0 1.146C0 .513.526 0 1.175 0h13.65C15.474 0 16 .513 16 1.146v13.708c0 .633-.526 1.146-1.175 1.146H1.175C.526 16 0 15.487 0 14.854zm4.943 12.248V6.169H2.542v7.225zm-1.2-8.212c.837 0 1.358-.554 1.358-1.248-.015-.709-.52-1.248-1.342-1.248S2.4 3.226 2.4 3.934c0 .694.521 1.248 1.327 1.248zm4.908 8.212V9.359c0-.216.016-.432.08-.586.173-.431.568-.878 1.232-.878.869 0 1.216.662 1.216 1.634v3.865h2.401V9.25c0-2.22-1.184-3.252-2.764-3.252-1.274 0-1.845.7-2.165 1.193v.025h-.016l.016-.025V6.169h-2.4c.03.678 0 7.225 0 7.225z"/> - </svg> - junepark + </svg> June Park </a> - </p> + </div> </div> </div>
--- a/mrjunejune/test/snapshots/resume_index.html.snapshot Sat Jan 03 17:29:12 2026 -0800 +++ b/mrjunejune/test/snapshots/resume_index.html.snapshot Sat Jan 03 19:37:51 2026 -0800 @@ -4,7 +4,10 @@ Connection: close Body: -t/otf" crossorigin> +"font/woff" crossorigin> +<link rel="preload" href="/public/fonts/atkinson-bold.woff" as="font" type="font/woff" crossorigin> + +<link rel="preload" href="/public/fonts/more-sugar.extras.otf" as="font" type="font/otf" crossorigin> <link rel="preload" href="/public/fonts/more-sugar.regular.otf" as="font" type="font/otf" crossorigin> <link rel="preload" href="/public/fonts/more-sugar.thin.otf" as="font" type="font/otf" crossorigin> @@ -12,16 +15,10 @@ <link rel="stylesheet" href="/base.css" /> + <link rel="stylesheet" href="markdown_to_html/index.css" /> +</head> +<body> <style> - .epi-photo { - display: flex; - justify-content: center; - margin-bottom: 10px; - } - </style> - </head> - <body> - <style> :root { --header-background: var(--white); --header-color: rgb(var(--black)); @@ -159,45 +156,291 @@ <script src="/index.js"></script> - <main> - <p>Hi, my name is Juntae, but most people call me June or MrJuneJune.</p> + <div class="header"> + <h1>Markdown to HTML Converter</h1> + </div> + + <div class="container"> + <div class="panel"> + <div class="label">Markdown Input</div> + <textarea id="input" placeholder="Type your markdown here..."># Welcome to Markdown Converter + +## Features + +This converter supports: - <p>I am a software engineer with experience spanning a wide range of companies, from small startups to FAANGs....</p> - <p>I know it is lame to work for them, but I have a dog so I need to put foods on my table.</p> +- **Bold text** and *italic text* +- [Links](https://example.com) +- `inline code` +- ~~strikethrough~~ + +### Lists - <div class="epi-photo"> - <img id="currentPhoto" style="opacity: 0; transition: opacity 0.2s;" /> - </div> +1. Ordered lists +2. Like this one +3. With numbers + +- Unordered lists +- Use dashes +- Or asterisks + +### Code Blocks - <p>During my free time, I like to write codes mostly in C, Python, and Typescript. All in mono repo styles using mercurial and bazel. (I know that is mentally ill...)</p> - <p>Feel free to check it out. My bad code..</p> +``` +function example() { + return "Hello World"; +} +``` + +### Blockquotes + +> This is a blockquote +> It can span multiple lines + +--- + +### Images - <h2>Links</h2> - <ul> - <li><a href="https://zenbu.babocoder.com/file/tip">Repository</a> - Check out my monorepo code</li> - <li><a href="/resume">Resume</a> - My professional experiences </li> - <li><a href="/tools">Tools</a> - Tools </li> - <!-- <li><a href="/blogs">Blogs</a> - Personal Blogs where I rant </li> --> - </ul> - </main> - <div style="display: flex; align-items: center; justify-content: center; margin: 30px 0px;"> + + +**Try editing this text!**</textarea> + </div> + + <div class="panel"> + <div class="title"> + <div class="label">HTML Output</div> + <button id="copy"> Copy </button> + </div> + <div id="output"></div> + </div> + </div> + <div style="display: flex; align-items: center; justify-content: center; margin: 30px 0px;"> <small>© 2026 June Park</small> </div> + <script src="/markdown_to_html.js"></script> + <script> + function convert() { + output.innerHTML = ''; + const markdown = input.value; + renderMarkdown(output, markdown); + } + input.addEventListener('input', convert); + + convert(); + + copy.addEventListener('click', () => { + const htmlBlob = new Blob([output.innerHTML], { type: 'text/html'}); + const textBlob = new Blob([output.innerText], { type: 'text/plain'}); + const data = [new ClipboardItem({ + 'text/html': htmlBlob, + 'text/plain': textBlob + })]; + navigator.clipboard.write(data).then(() => { + copy.textContent = "Copied!"; + setTimeout(() => { + copy.textContent = "Copy"; + copy.classList.remove('success'); + }, 1000); + }).catch(err => { + console.error('Failed to copy: ', err); + }); + }); + </script> +</body> +</htmlLocation: / + + <p> + <span class="entry-title-style">Tools & Platforms:</span> + <span class="skill-type-style">Bazel, PostgresSQL, Mercurial, Git, Pands, Raylib, XCode</span> + </p> + <p> + <span class="entry-title-style"> Web Frameworks: </span> + <span class="skill-type-style"> Django, Rails, React, Flask</span> + </p> + <p> + <span class="entry-title-style"> DevOp:</span> + <span class="skill-type-style"> Plummi, Heroku, DigitalOcean, AWS, Google Cloud </span> + </p> + <p> + <span class="entry-title-style"> Language:</span> + <span class="skill-type-style"> English, Korean, Japanese </span> + </p> + </div> + + <!-- Experiences --> + <div class="sub-header"> + <h2 class="section-style"> Experience </h2> + <div class="line"></div> + </div> + <div class="flex-box"> + <p class="entry-title-style"> + <a href="https://www.meta.com/">Meta</a> + </p> + <p class="entry-location-style">San Francisco, CA, USA</p> + </div> + <div class="flex-box"> + <p class="entry-position-style">SOFTWARE ENGINEER</p> + <p class="entry-date-style">Oct, 2024 - Present</p> + </div> + <ul class="description-style"> + <li> + Took initiative on Channel Value Rule, targeting the 16% of ad traffic with both app and web destinations to improve value attribution and ROI. + </li> + <li> + Built full-stack features using React and Hack/GraphQL, contributing to scalable, production-ready systems. + </li> + <li> + Partnered with data science to design A/B tests and analyze revenue impact of ads destination. + </li> + <li> + Proposed and implemented alpha improvements to internal testing infrastructure, reducing test time by 50% and enhancing developer velocity. + </li> + </ul> + <div class="flex-box"> + <p class="entry-title-style"> + <a href="https://www.wmg.com/">Warner Music Group</a> + </p> + <p class="entry-location-style">Toronto, ON, Canada</p> + </div> + <div class="flex-box"> + <p class="entry-position-style">TECHNICAL LEAD ENGINEER</p> + <p class="entry-date-style">July, 2023 - Sept, 2024</p> + </div> + <ul class="description-style"> + <li> + Implements <a href="https://bazel.build/">bazel </a>structure for the company for TypeScript and JavaScript code base for hermiticity and stablishing standards for JavaScript and + </li> + <li> + TypeScript testing and code structures. + </li> + <li> + Led a team of five engineers in building GraphQL endpoints for client-facing applications using Apollo and AppSync, supporting over 2000 RPS and auto scaling depending on request values. + </li> + <li> + Improved application response times by up to 85% for graphQL response by updating database schema and SQL queries, eliminating N+1 queries and lack of indexes. + </li> + <li> + Developed CI/CD pipelines for backend structures. + </li> + <li> + Designed infrastructure for pub/sub, caching, and media processing logic. + </li> + </ul> + + <div class="flex-box"> + <p class="entry-title-style"> + <a href="https://www.google.com/">Google</a> + </p> + <p class="entry-location-style">Toronto, ON, Canada</p> + </div> + <div class="flex-box"> + <p class="entry-position-style">SOFTWARE ENGINEER</p> + <p class="entry-date-style">Feb, 2022 - July 2023</p> + </div> + <ul class="description-style"> + <li> + Implements and maintained new features relating to App Script across google workspace platform including Gmail, sheets, and Docs.</li> + <li> + Improved a response time and render time of App Script hover card components.</li> + <li> + Collaborated with a team of developers to ensure timely and accurate delivery of features.</li> + <li> + Conducted user testing and gathered feedback to iterate on features for optimal user experience.</li> + </ul> + + <div class="flex-box"> + <p class="entry-title-style"> + <a href="https://www.everlywell.com/">Everlywell</a> + </p> + <p class="entry-location-style">Toronto, ON, Canada</p> + </div> + <div class="flex-box"> + <p class="entry-position-style">SOFTWARE ENGINEER</p> + <p class="entry-date-style">December, 2020 - Jan, 2022</p> + </div> + <ul class="description-style"> + <li> + Maintained Amazon amplify apps to create and deploy React web applications for companies such as <a href="https://brooklynnets.everlywell.com/">NBA</a>, <a href="https://tinder.everlywell.com/">Tinder</a>, and other companies for COVID-19 at-home test kits.</li> + <li> + Implemented a script that helps accurately access and refund unused covid test kits; helping company save up to 200,000 USD.</li> + <li> + Created several Rails controllers for internal purposes; mocking end to end user experience for QA, mass refund features for CX department, and more, ultimately reducing support tickets amount by 50 percent.</li> + <li> + Implemented an audit table to help debug problems and logged which process was responsible for the change of the record using PaperTrail gems</li> + </ul> + + <div class="flex-box"> + <p class="entry-title-style"> + <a href="https://www.spiria.com/">Spiria</a> + </p> + <p class="entry-location-style">Oakville, ON, Canada</p> + </div> + <div class="flex-box"> + <p class="entry-position-style">SOFTWARE ENGINEER</p> + <p class="entry-date-style">October, 2018 - October, 2020</p> + </div> + <ul class="description-style"> + <li> + Constructed RESTful API endpoints in multiple different frameworks such as Django, Ruby on Rails, and Flask and automated API documentation process using swagger. + </li> + <li> + Designed custom rake tasks for importing production data into newly updated data structure to meet client's needs. + </li> + <li> + Maintained or updated staging/productions servers. Debugged problems in production postgres database using ssh and postgres console on Heroku or AWS servers + </li> + <li> + Collaborated in creating automation python scripts for websites and application using selenium covering for QA eliminating 80% of QA's manual work + </li> + </ul> + + <div class="flex-box"> + <p class="entry-title-style"> + <a href="https://www.apexscore.ai/">Apex Score</a> + </p> + <p class="entry-location-style">Oakville, ON, Canada</p> + </div> + <div class="flex-box"> + <p class="entry-position-style">SOFTWARE ENGINEER</p> + <p class="entry-date-style">September, 2019 - October, 2020</p> + </div> + <ul class="description-style"> + <li> + Developed custom Shapley value regression model to calculate importance of independent variables of data sets using sklearn, pandas, and numpy. + </li> + <li> + Created custom image uploader to Amazon s3 bucket using boto3 library. + </li> + <li> + Built RESTful API application using Flask framework and automated extensive API documentation pages using flask-restplus, pytest, and swagger, covering 95% of the code base. + </li> + <li> + Created an interactive graph using D3.js in Vue.js with data from Flask backend API. + </li> + </ul> + + <div class="sub-header"> + <h2 class="section-style"> Education </h2> + <div class="line"></div> + </div> + <div class="flex-box"> + <p class="entry-title-style"> + University of British Columbia + </p> + <p class="entry-location-style">Kelowna, British Columbia</p> + </div> + <div class="flex-box"> + <p class="entry-position-style">BACHELOR OF SCIENCE IN PHYSICS</p> + <p class="entry-date-style">2014 - 2018</p> + </div> + <div id="footer"></div> + </main> + <div style="display: flex; align-items: center; justify-content: center; margin: 30px 0px;"> + <small>© 2026 June Park</small> +</div> + + <script href="index.js"></script> </body> - <script> - let arr = Array.from({ length: 18 }, (_, i) => i+1); - function setRandomImages() { - const randomIndex = Math.floor(Math.random() * arr.length); - const pos = arr[randomIndex]; - currentPhoto.src = `/public/epi-photos/webp/${pos}.webp`; - currentPhoto.onload = () => { - currentPhoto.style.opacity = "1"; - }; - setTimeout(() => setRandomImages(), 1000); - } - setRandomImages(); - </script> -</htmlLocation: / -Location: /resume +</htmlLocation: /resume
--- a/mrjunejune/test/snapshots/root.snapshot Sat Jan 03 17:29:12 2026 -0800 +++ b/mrjunejune/test/snapshots/root.snapshot Sat Jan 03 19:37:51 2026 -0800 @@ -1,13 +1,15 @@ HTTP/1.1 200 OK Content-Type: text/html -Content-Length: 5322 +Content-Length: 5723 Connection: close <!doctype html> <html lang="en"> <head> <title> MrJuneJune </title> - <link rel="icon" type="image/svg+xml" href="/public/epi_all_colors.svg"> + <meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<link rel="icon" type="image/svg+xml" href="/public/epi_all_colors.svg"> <link rel="preload" href="/public/fonts/Roboto-Regular.ttf" as="font" crossorigin> <link rel="preload" href="/public/fonts/Roboto-Thin.ttf"as="font" crossorigin> @@ -29,6 +31,26 @@ justify-content: center; margin-bottom: 10px; } + + .epi-photo img { + max-width: 100%; + height: auto; + border-radius: 8px; + } + + @media (max-width: 720px) { + .epi-photo { + margin-bottom: 1.5em; + } + + ul { + padding-left: 1.5em; + } + + li { + margin-bottom: 0.75em; + } + } </style> </head> <body> @@ -180,15 +202,15 @@ <img id="currentPhoto" style="opacity: 0; transition: opacity 0.2s;" /> </div> - <p>During my free time, I like to write codes mostly in C, Python, and Typescript. All in mono repo styles using mercurial and bazel. (I know that is mentally ill...)</p> - <p>Feel free to check it out. My bad code..</p> + <p>During my free time, I like to write codes mostly in C, Python, and Typescript; all in mono repo styles using bazel. (I know that is mentally ill...)</p> + <p>Feel free to check it out my bad code!</p> <h2>Links</h2> <ul> - <li><a href="https://zenbu.babocoder.com/file/tip">Repository</a> - Check out my monorepo code</li> + <li><a href="https://zenbu.babocoder.com/file/tip">Repository</a> - Check out my code</li> + <li><a href="/blog">Blogs</a> - My thoughts / Experiments </li> <li><a href="/resume">Resume</a> - My professional experiences </li> - <li><a href="/tools">Tools</a> - Tools </li> - <!-- <li><a href="/blogs">Blogs</a> - Personal Blogs where I rant </li> --> + <li><a href="/tools">Tools</a> - Things I made for myself </li> </ul> </main> <div style="display: flex; align-items: center; justify-content: center; margin: 30px 0px;">
--- a/mrjunejune/test/snapshots/tools.snapshot Sat Jan 03 17:29:12 2026 -0800 +++ b/mrjunejune/test/snapshots/tools.snapshot Sat Jan 03 19:37:51 2026 -0800 @@ -1,12 +1,14 @@ HTTP/1.1 200 OK Content-Type: text/html -Content-Length: 4152 +Content-Length: 4246 Connection: close <!doctype html> <html lang="en"> <head> - <link rel="icon" type="image/svg+xml" href="/public/epi_all_colors.svg"> + <meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<link rel="icon" type="image/svg+xml" href="/public/epi_all_colors.svg"> <link rel="preload" href="/public/fonts/Roboto-Regular.ttf" as="font" crossorigin> <link rel="preload" href="/public/fonts/Roboto-Thin.ttf"as="font" crossorigin>
--- a/mrjunejune/test/snapshots/tools_file_converter.snapshot Sat Jan 03 17:29:12 2026 -0800 +++ b/mrjunejune/test/snapshots/tools_file_converter.snapshot Sat Jan 03 19:37:51 2026 -0800 @@ -1,6 +1,6 @@ HTTP/1.1 200 OK Content-Type: text/html -Content-Length: 7742 +Content-Length: 8526 Connection: close <!DOCTYPE html> @@ -9,7 +9,9 @@ <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>File Format Converter</title> - <link rel="icon" type="image/svg+xml" href="/public/epi_all_colors.svg"> + <meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<link rel="icon" type="image/svg+xml" href="/public/epi_all_colors.svg"> <link rel="preload" href="/public/fonts/Roboto-Regular.ttf" as="font" crossorigin> <link rel="preload" href="/public/fonts/Roboto-Thin.ttf"as="font" crossorigin> @@ -55,11 +57,11 @@ } input[type="file"] { - width: 50%; - padding: 0.5rem; + padding: 0.75rem; border: 2px dashed #ccc; border-radius: 4px; background: white; + font-size: 16px; } button { @@ -72,6 +74,7 @@ cursor: pointer; font-size: 1rem; margin-top: 1rem; + min-height: 44px; } button:hover { @@ -122,6 +125,36 @@ .loading.show { display: block; } + + /* Mobile responsive */ + @media (max-width: 720px) { + .container { + padding: 1rem; + margin: 1rem auto; + } + + .converter-section { + padding: 1.5rem 1rem; + } + + .converter-section h2 { + font-size: 1.5rem; + } + + .file-input-wrapper label { + font-size: 1rem; + } + + button { + font-size: 1.1rem; + padding: 1rem 1.5rem; + } + + .result { + padding: 1rem; + font-size: 1rem; + } + } </style> </head> <body>
--- a/mrjunejune/test/snapshots/tools_file_converter_index.html.snapshot Sat Jan 03 17:29:12 2026 -0800 +++ b/mrjunejune/test/snapshots/tools_file_converter_index.html.snapshot Sat Jan 03 19:37:51 2026 -0800 @@ -4,17 +4,149 @@ Connection: close Body: -reload" href="/public/fonts/more-sugar.regular.otf" as="font" type="font/otf" crossorigin> +/woff" crossorigin> +<link rel="preload" href="/public/fonts/atkinson-bold.woff" as="font" type="font/woff" crossorigin> + +<link rel="preload" href="/public/fonts/more-sugar.extras.otf" as="font" type="font/otf" crossorigin> +<link rel="preload" href="/public/fonts/more-sugar.regular.otf" as="font" type="font/otf" crossorigin> <link rel="preload" href="/public/fonts/more-sugar.thin.otf" as="font" type="font/otf" crossorigin> <link rel="preload" href="/base.css" as="style" /> <link rel="stylesheet" href="/base.css" /> - <link rel="stylesheet" href="/tools/index.css" /> - </head> - <body> - <style> + <style> + .container { + max-width: 800px; + margin: 2rem auto; + padding: 2rem; + } + + .converter-section { + border-radius: 8px; + border: 2px solid var(--lightgray); + padding: 2rem; + margin-bottom: 2rem; + } + + .converter-section h2 { + margin-top: 0; + color: var(--black); + } + + .file-input-wrapper { + margin: 1rem 0; + } + + .file-input-wrapper label { + display: block; + margin-bottom: 0.5rem; + font-weight: 600; + } + + input[type="file"] { + padding: 0.75rem; + border: 2px dashed #ccc; + border-radius: 4px; + background: white; + font-size: 16px; + } + + button { + background: var(--purple); + font-family: "More Thin", sans-serif; + color: var(--gray-gradient); + border: none; + padding: 0.75rem 1.5rem; + border-radius: 4px; + cursor: pointer; + font-size: 1rem; + margin-top: 1rem; + min-height: 44px; + } + + button:hover { + background: var(--orange); + } + + button:disabled { + background: var(--gray); + cursor: not-allowed; + } + + .result { + margin-top: 1rem; + padding: 1rem; + background: white; + border-radius: 4px; + display: none; + } + + .result.show { + display: block; + } + + .result.success { + border-left: 4px solid var(--blue); + } + + .result.error { + border-left: 4px solid var(--red); + } + + .download-link { + display: inline-block; + margin-top: 0.5rem; + color: var(--awesome); + text-decoration: none; + } + + .download-link:hover { + text-decoration: underline; + } + + .loading { + display: none; + margin-top: 1rem; + } + + .loading.show { + display: block; + } + + /* Mobile responsive */ + @media (max-width: 720px) { + .container { + padding: 1rem; + margin: 1rem auto; + } + + .converter-section { + padding: 1.5rem 1rem; + } + + .converter-section h2 { + font-size: 1.5rem; + } + + .file-input-wrapper label { + font-size: 1rem; + } + + button { + font-size: 1.1rem; + padding: 1rem 1.5rem; + } + + .result { + padding: 1rem; + font-size: 1rem; + } + } + </style> +</head> +<body> + <style> :root { --header-background: var(--white); --header-color: rgb(var(--black)); @@ -152,23 +284,55 @@ <script src="/index.js"></script> - <main> - <h1 class="title"> Tools </h1> - <ul class="nav-list"> - <li><a href="/tools/markdown_to_html">MarkDown to HTML</a></li> - <li><a href="/tools/file_converter">Images to Webp / Video to Mp4</a></li> - </ul> - <h3> TODOs </h3> - <p> Probably should add this... </p> - <ul class="nav-list"> - <li>- Simple online LaTex editor.</li> - <li>- Online HLS player.</li> - </ul> - </main> - <div style="display: flex; align-items: center; justify-content: center; margin: 30px 0px;"> + + <div class="container"> + <h1>File Format Converter</h1> + <p>Convert your images and videos to different formats using FFmpeg</p> + + <div class="converter-section"> + <h2>Image to WebP Converter</h2> + <p>Upload an image file (PNG, JPG, GIF, etc.) to convert it to WebP format</p> + + <div class="file-input-wrapper"> + <label for="imageInput">Choose an image file:</label> + <input type="file" id="imageInput" accept="image/*"> + </div> + + <button id="convertImageBtn" onclick="convertImageToWebP()">Convert to WebP</button> + + <div class="loading" id="imageLoading">Converting... Please wait.</div> + + <div class="result" id="imageResult"> + <p id="imageMessage"></p> + <a id="imageDownload" class="download-link" style="display: none;">Download WebP Image</a> + </div> + </div> + + <div class="converter-section"> + <h2>Video to MP4 Converter</h2> + <p>Upload a video file (AVI, MOV, MKV, etc.) to convert it to MP4 format</p> + + <div class="file-input-wrapper"> + <label for="videoInput">Choose a video file:</label> + <input type="file" id="videoInput" accept="video/*"> + </div> + + <button id="convertVideoBtn" onclick="convertVideoToMP4()">Convert to MP4</button> + + <div class="loading" id="videoLoading">Converting... Please wait.</div> + + <div class="result" id="videoResult"> + <p id="videoMessage"></p> + <a id="videoDownload" class="download-link" style="display: none;">Download MP4 Video</a> + </div> + </div> + <div style="display: flex; align-items: center; justify-content: center; margin: 30px 0px;"> <small>© 2026 June Park</small> </div> - </body> -</htmlLocation: /tools/file_converter + </div> + <script src="/tools/file_converter/index.js"></script> +</body> +</htmlLocation: /tools/markdown_to_html +Location: /tools/file_converter
--- a/mrjunejune/test/snapshots/tools_index.html.snapshot Sat Jan 03 17:29:12 2026 -0800 +++ b/mrjunejune/test/snapshots/tools_index.html.snapshot Sat Jan 03 19:37:51 2026 -0800 @@ -4,7 +4,8 @@ Connection: close Body: -="font/woff" crossorigin> +"font/woff" crossorigin> +<link rel="preload" href="/public/fonts/atkinson-bold.woff" as="font" type="font/woff" crossorigin> <link rel="preload" href="/public/fonts/more-sugar.extras.otf" as="font" type="font/otf" crossorigin> <link rel="preload" href="/public/fonts/more-sugar.regular.otf" as="font" type="font/otf" crossorigin> @@ -14,104 +15,7 @@ <link rel="stylesheet" href="/base.css" /> - <style> - .container { - max-width: 800px; - margin: 2rem auto; - padding: 2rem; - } - - .converter-section { - border-radius: 8px; - border: 2px solid var(--lightgray); - padding: 2rem; - margin-bottom: 2rem; - } - - .converter-section h2 { - margin-top: 0; - color: var(--black); - } - - .file-input-wrapper { - margin: 1rem 0; - } - - .file-input-wrapper label { - display: block; - margin-bottom: 0.5rem; - font-weight: 600; - } - - input[type="file"] { - width: 50%; - padding: 0.5rem; - border: 2px dashed #ccc; - border-radius: 4px; - background: white; - } - - button { - background: var(--purple); - font-family: "More Thin", sans-serif; - color: var(--gray-gradient); - border: none; - padding: 0.75rem 1.5rem; - border-radius: 4px; - cursor: pointer; - font-size: 1rem; - margin-top: 1rem; - } - - button:hover { - background: var(--orange); - } - - button:disabled { - background: var(--gray); - cursor: not-allowed; - } - - .result { - margin-top: 1rem; - padding: 1rem; - background: white; - border-radius: 4px; - display: none; - } - - .result.show { - display: block; - } - - .result.success { - border-left: 4px solid var(--blue); - } - - .result.error { - border-left: 4px solid var(--red); - } - - .download-link { - display: inline-block; - margin-top: 0.5rem; - color: var(--awesome); - text-decoration: none; - } - - .download-link:hover { - text-decoration: underline; - } - - .loading { - display: none; - margin-top: 1rem; - } - - .loading.show { - display: block; - } - </style> + <link rel="stylesheet" href="markdown_to_html/index.css" /> </head> <body> <style> @@ -252,58 +156,131 @@ <script src="/index.js"></script> - + <div class="header"> + <h1>Markdown to HTML Converter</h1> + </div> + <div class="container"> - <h1>File Format Converter</h1> - <p>Convert your images and videos to different formats using FFmpeg</p> + <div class="panel"> + <div class="label">Markdown Input</div> + <textarea id="input" placeholder="Type your markdown here..."># Welcome to Markdown Converter + +## Features + +This converter supports: - <div class="converter-section"> - <h2>Image to WebP Converter</h2> - <p>Upload an image file (PNG, JPG, GIF, etc.) to convert it to WebP format</p> +- **Bold text** and *italic text* +- [Links](https://example.com) +- `inline code` +- ~~strikethrough~~ + +### Lists + +1. Ordered lists +2. Like this one +3. With numbers - <div class="file-input-wrapper"> - <label for="imageInput">Choose an image file:</label> - <input type="file" id="imageInput" accept="image/*"> - </div> +- Unordered lists +- Use dashes +- Or asterisks + +### Code Blocks + +``` +function example() { + return "Hello World"; +} +``` + +### Blockquotes - <button id="convertImageBtn" onclick="convertImageToWebP()">Convert to WebP</button> +> This is a blockquote +> It can span multiple lines - <div class="loading" id="imageLoading">Converting... Please wait.</div> +--- + +### Images - <div class="result" id="imageResult"> - <p id="imageMessage"></p> - <a id="imageDownload" class="download-link" style="display: none;">Download WebP Image</a> + + +**Try editing this text!**</textarea> + </div> + + <div class="panel"> + <div class="title"> + <div class="label">HTML Output</div> + <button id="copy"> Copy </button> </div> + <div id="output"></div> </div> - - <div class="converter-section"> - <h2>Video to MP4 Converter</h2> - <p>Upload a video file (AVI, MOV, MKV, etc.) to convert it to MP4 format</p> - - <div class="file-input-wrapper"> - <label for="videoInput">Choose a video file:</label> - <input type="file" id="videoInput" accept="video/*"> - </div> - - <button id="convertVideoBtn" onclick="convertVideoToMP4()">Convert to MP4</button> - - <div class="loading" id="videoLoading">Converting... Please wait.</div> - - <div class="result" id="videoResult"> - <p id="videoMessage"></p> - <a id="videoDownload" class="download-link" style="display: none;">Download MP4 Video</a> - </div> - </div> - <div style="display: flex; align-items: center; justify-content: center; margin: 30px 0px;"> + </div> + <div style="display: flex; align-items: center; justify-content: center; margin: 30px 0px;"> <small>© 2026 June Park</small> </div> - </div> - <script src="/tools/file_converter/index.js"></script> + <script src="/markdown_to_html.js"></script> + <script> + function convert() { + output.innerHTML = ''; + const markdown = input.value; + renderMarkdown(output, markdown); + } + input.addEventListener('input', convert); + + convert(); + + copy.addEventListener('click', () => { + const htmlBlob = new Blob([output.innerHTML], { type: 'text/html'}); + const textBlob = new Blob([output.innerText], { type: 'text/plain'}); + const data = [new ClipboardItem({ + 'text/html': htmlBlob, + 'text/plain': textBlob + })]; + navigator.clipboard.write(data).then(() => { + copy.textContent = "Copied!"; + setTimeout(() => { + copy.textContent = "Copy"; + copy.classList.remove('success'); + }, 1000); + }).catch(err => { + console.error('Failed to copy: ', err); + }); + }); + </script> </body> -</htmlLocation: /tools +</htmlLocation: / -ition-style">SOFTWARE ENGINEER</p> + <p> + <span class="entry-title-style">Tools & Platforms:</span> + <span class="skill-type-style">Bazel, PostgresSQL, Mercurial, Git, Pands, Raylib, XCode</span> + </p> + <p> + <span class="entry-title-style"> Web Frameworks: </span> + <span class="skill-type-style"> Django, Rails, React, Flask</span> + </p> + <p> + <span class="entry-title-style"> DevOp:</span> + <span class="skill-type-style"> Plummi, Heroku, DigitalOcean, AWS, Google Cloud </span> + </p> + <p> + <span class="entry-title-style"> Language:</span> + <span class="skill-type-style"> English, Korean, Japanese </span> + </p> + </div> + + <!-- Experiences --> + <div class="sub-header"> + <h2 class="section-style"> Experience </h2> + <div class="line"></div> + </div> + <div class="flex-box"> + <p class="entry-title-style"> + <a href="https://www.meta.com/">Meta</a> + </p> + <p class="entry-location-style">San Francisco, CA, USA</p> + </div> + <div class="flex-box"> + <p class="entry-position-style">SOFTWARE ENGINEER</p> <p class="entry-date-style">Oct, 2024 - Present</p> </div> <ul class="description-style"> @@ -465,4 +442,6 @@ <script href="index.js"></script> </body> -</html> +</htmlLocation: /resume +Location: /tools +
--- a/mrjunejune/test/snapshots/tools_markdown_to_html.snapshot Sat Jan 03 17:29:12 2026 -0800 +++ b/mrjunejune/test/snapshots/tools_markdown_to_html.snapshot Sat Jan 03 19:37:51 2026 -0800 @@ -1,6 +1,6 @@ HTTP/1.1 200 OK Content-Type: text/html -Content-Length: 5891 +Content-Length: 5985 Connection: close <!DOCTYPE html> @@ -9,7 +9,9 @@ <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Markdown to HTML Converter</title> - <link rel="icon" type="image/svg+xml" href="/public/epi_all_colors.svg"> + <meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<link rel="icon" type="image/svg+xml" href="/public/epi_all_colors.svg"> <link rel="preload" href="/public/fonts/Roboto-Regular.ttf" as="font" crossorigin> <link rel="preload" href="/public/fonts/Roboto-Thin.ttf"as="font" crossorigin>
--- a/mrjunejune/test/snapshots/tools_markdown_to_html_index.html.snapshot Sat Jan 03 17:29:12 2026 -0800 +++ b/mrjunejune/test/snapshots/tools_markdown_to_html_index.html.snapshot Sat Jan 03 19:37:51 2026 -0800 @@ -4,7 +4,10 @@ Connection: close Body: -t/otf" crossorigin> +/woff" crossorigin> +<link rel="preload" href="/public/fonts/atkinson-bold.woff" as="font" type="font/woff" crossorigin> + +<link rel="preload" href="/public/fonts/more-sugar.extras.otf" as="font" type="font/otf" crossorigin> <link rel="preload" href="/public/fonts/more-sugar.regular.otf" as="font" type="font/otf" crossorigin> <link rel="preload" href="/public/fonts/more-sugar.thin.otf" as="font" type="font/otf" crossorigin> @@ -13,15 +16,137 @@ <style> - .epi-photo { - display: flex; - justify-content: center; - margin-bottom: 10px; - } + .container { + max-width: 800px; + margin: 2rem auto; + padding: 2rem; + } + + .converter-section { + border-radius: 8px; + border: 2px solid var(--lightgray); + padding: 2rem; + margin-bottom: 2rem; + } + + .converter-section h2 { + margin-top: 0; + color: var(--black); + } + + .file-input-wrapper { + margin: 1rem 0; + } + + .file-input-wrapper label { + display: block; + margin-bottom: 0.5rem; + font-weight: 600; + } + + input[type="file"] { + padding: 0.75rem; + border: 2px dashed #ccc; + border-radius: 4px; + background: white; + font-size: 16px; + } + + button { + background: var(--purple); + font-family: "More Thin", sans-serif; + color: var(--gray-gradient); + border: none; + padding: 0.75rem 1.5rem; + border-radius: 4px; + cursor: pointer; + font-size: 1rem; + margin-top: 1rem; + min-height: 44px; + } + + button:hover { + background: var(--orange); + } + + button:disabled { + background: var(--gray); + cursor: not-allowed; + } + + .result { + margin-top: 1rem; + padding: 1rem; + background: white; + border-radius: 4px; + display: none; + } + + .result.show { + display: block; + } + + .result.success { + border-left: 4px solid var(--blue); + } + + .result.error { + border-left: 4px solid var(--red); + } + + .download-link { + display: inline-block; + margin-top: 0.5rem; + color: var(--awesome); + text-decoration: none; + } + + .download-link:hover { + text-decoration: underline; + } + + .loading { + display: none; + margin-top: 1rem; + } + + .loading.show { + display: block; + } + + /* Mobile responsive */ + @media (max-width: 720px) { + .container { + padding: 1rem; + margin: 1rem auto; + } + + .converter-section { + padding: 1.5rem 1rem; + } + + .converter-section h2 { + font-size: 1.5rem; + } + + .file-input-wrapper label { + font-size: 1rem; + } + + button { + font-size: 1.1rem; + padding: 1rem 1.5rem; + } + + .result { + padding: 1rem; + font-size: 1rem; + } + } </style> - </head> - <body> - <style> +</head> +<body> + <style> :root { --header-background: var(--white); --header-color: rgb(var(--black)); @@ -159,46 +284,54 @@ <script src="/index.js"></script> - <main> - <p>Hi, my name is Juntae, but most people call me June or MrJuneJune.</p> + + <div class="container"> + <h1>File Format Converter</h1> + <p>Convert your images and videos to different formats using FFmpeg</p> + + <div class="converter-section"> + <h2>Image to WebP Converter</h2> + <p>Upload an image file (PNG, JPG, GIF, etc.) to convert it to WebP format</p> - <p>I am a software engineer with experience spanning a wide range of companies, from small startups to FAANGs....</p> - <p>I know it is lame to work for them, but I have a dog so I need to put foods on my table.</p> + <div class="file-input-wrapper"> + <label for="imageInput">Choose an image file:</label> + <input type="file" id="imageInput" accept="image/*"> + </div> - <div class="epi-photo"> - <img id="currentPhoto" style="opacity: 0; transition: opacity 0.2s;" /> - </div> + <button id="convertImageBtn" onclick="convertImageToWebP()">Convert to WebP</button> + + <div class="loading" id="imageLoading">Converting... Please wait.</div> - <p>During my free time, I like to write codes mostly in C, Python, and Typescript. All in mono repo styles using mercurial and bazel. (I know that is mentally ill...)</p> - <p>Feel free to check it out. My bad code..</p> + <div class="result" id="imageResult"> + <p id="imageMessage"></p> + <a id="imageDownload" class="download-link" style="display: none;">Download WebP Image</a> + </div> + </div> + + <div class="converter-section"> + <h2>Video to MP4 Converter</h2> + <p>Upload a video file (AVI, MOV, MKV, etc.) to convert it to MP4 format</p> - <h2>Links</h2> - <ul> - <li><a href="https://zenbu.babocoder.com/file/tip">Repository</a> - Check out my monorepo code</li> - <li><a href="/resume">Resume</a> - My professional experiences </li> - <li><a href="/tools">Tools</a> - Tools </li> - <!-- <li><a href="/blogs">Blogs</a> - Personal Blogs where I rant </li> --> - </ul> - </main> - <div style="display: flex; align-items: center; justify-content: center; margin: 30px 0px;"> + <div class="file-input-wrapper"> + <label for="videoInput">Choose a video file:</label> + <input type="file" id="videoInput" accept="video/*"> + </div> + + <button id="convertVideoBtn" onclick="convertVideoToMP4()">Convert to MP4</button> + + <div class="loading" id="videoLoading">Converting... Please wait.</div> + + <div class="result" id="videoResult"> + <p id="videoMessage"></p> + <a id="videoDownload" class="download-link" style="display: none;">Download MP4 Video</a> + </div> + </div> + <div style="display: flex; align-items: center; justify-content: center; margin: 30px 0px;"> <small>© 2026 June Park</small> </div> - </body> - <script> - let arr = Array.from({ length: 18 }, (_, i) => i+1); - function setRandomImages() { - const randomIndex = Math.floor(Math.random() * arr.length); - const pos = arr[randomIndex]; - currentPhoto.src = `/public/epi-photos/webp/${pos}.webp`; - currentPhoto.onload = () => { - currentPhoto.style.opacity = "1"; - }; - setTimeout(() => setRandomImages(), 1000); - } - setRandomImages(); - </script> -</htmlLocation: / -Location: /resume -Location: /tools/markdown_to_html + </div> + <script src="/tools/file_converter/index.js"></script> +</body> +</htmlLocation: /tools/markdown_to_html
--- a/playground/main.c Sat Jan 03 17:29:12 2026 -0800 +++ b/playground/main.c Sat Jan 03 19:37:51 2026 -0800 @@ -3,37 +3,6 @@ #define REPO_ROOT "/Users/mrjunejune/zenbu" -#include <stdint.h> -#include <arpa/inet.h> - -void run_hg_command(FILE *hg_pipe) { - char channel; - uint32_t net_len; - - while (fread(&channel, 1, 1, hg_pipe) == 1) { - // 1. Read the 4-byte length - fread(&net_len, 4, 1, hg_pipe); - uint32_t length = ntohl(net_len); - - // 2. If it's an 'o' (output), it's the data you want - if (channel == 'o' || channel == 'e') { - char *buf = malloc(length + 1); - fread(buf, 1, length, hg_pipe); - buf[length] = '\0'; - printf("%s", buf); // This is your capability string - free(buf); - } - // 3. IF THE CHANNEL IS 'r', THE COMMAND IS DONE. STOP HERE. - else if (channel == 'r') { - int32_t result; - // The 'r' channel payload is the 4-byte return code - fread(&result, 4, 1, hg_pipe); - printf("\nCommand finished with result: %d\n", ntohl(result)); - break; // <--- THIS IS YOUR EXIT - } - } -} - int main() { char command[512]; @@ -50,21 +19,20 @@ fprintf(hg_pipe, "capabilities\n"); fflush(hg_pipe); - run_hg_command(hg_pipe); - // char *output = malloc(sizeof(char) * 2048); - // char *curr = output; - // int c; - // int number_of_breakline = 0; - // while ((c = fgetc(hg_pipe)) != NULL) - // { - // *curr++ = c; - // printf("output: %s\n", output); - // if (c == '\n') - // number_of_breakline++; - // if (number_of_breakline == 2) - // break; - // printf("char: %c\n", c); - // } + char *output = malloc(sizeof(char) * 2048); + char *curr = output; + int c; + int number_of_breakline = 0; + while ((c = fgetc(hg_pipe)) != NULL) + { + *curr++ = c; + printf("output: %s\n", output); + if (c == '\n') + number_of_breakline++; + if (number_of_breakline == 2) + break; + printf("char: %c\n", c); + } pclose(hg_pipe);