Mercurial
changeset 94:092afa595764
[MrJuneJune] Added Integration tests.
| author | June Park <parkjune1995@gmail.com> |
|---|---|
| date | Fri, 02 Jan 2026 18:13:32 -0800 |
| parents | be91a73d801a |
| children | b51f8cce9170 |
| files | mrjunejune/BUILD mrjunejune/test/README.md mrjunejune/test/create_snapshots.c mrjunejune/test/integration_test.c mrjunejune/test/shiba.webp mrjunejune/test/snapshots/index.html.snapshot mrjunejune/test/snapshots/resume.snapshot mrjunejune/test/snapshots/resume_index.html.snapshot mrjunejune/test/snapshots/root.snapshot mrjunejune/test/snapshots/tools.snapshot mrjunejune/test/snapshots/tools_file_converter.snapshot mrjunejune/test/snapshots/tools_file_converter_index.html.snapshot mrjunejune/test/snapshots/tools_index.html.snapshot mrjunejune/test/snapshots/tools_markdown_to_html.snapshot mrjunejune/test/snapshots/tools_markdown_to_html_index.html.snapshot mrjunejune/test/test_avi.avi |
| diffstat | 16 files changed, 3067 insertions(+), 487 deletions(-) [+] |
line wrap: on
line diff
--- a/mrjunejune/BUILD Fri Jan 02 18:02:22 2026 -0800 +++ b/mrjunejune/BUILD Fri Jan 02 18:13:32 2026 -0800 @@ -37,24 +37,6 @@ binary = ":mrjunejune_server", ) -cc_library( - name = "mrjunejune_server_lib", - srcs = ["server_entry.c"], - deps = ["//seobeo:seobeo_server"], # Use server-only target (no OpenSSL) - linkstatic = False, # ensures dynamic linking - visibility = ["//visibility:public"], -) - -# py_binary( -# name = "python_server", -# srcs = ["python_server.py"], -# deps = [ -# ":mrjunejune_server_lib", -# "@pip_deps//:cffi", -# ], -# data = [":mrjunejune_server_lib"], -# ) - cc_test( name = "integration_test", srcs = ["test/integration_test.c"], @@ -63,9 +45,10 @@ "//mrjunejune:mrjunejune_server", "//mrjunejune:src_files", "//mrjunejune:test_snapshots", + "//mrjunejune:test_files", ], - size = "medium", - timeout = "moderate", + size = "large", + timeout = "long", args = ["$(location //mrjunejune:mrjunejune_server)"], ) @@ -84,3 +67,32 @@ name = "test_snapshots", srcs = glob(["test/snapshots/**"]), ) + +filegroup( + name = "test_files", + srcs = [ + "test/shiba.webp", + "test/test_avi.avi", + ], +) + +# This was to use python ffi, but w/e +# cc_library( +# name = "mrjunejune_server_lib", +# srcs = ["server_entry.c"], +# deps = ["//seobeo:seobeo_server"], +# linkstatic = False, +# visibility = ["//visibility:public"], +# ) + +# py_binary( +# name = "python_server", +# srcs = ["python_server.py"], +# deps = [ +# ":mrjunejune_server_lib", +# "@pip_deps//:cffi", +# ], +# data = [":mrjunejune_server_lib"], +# ) + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mrjunejune/test/README.md Fri Jan 02 18:13:32 2026 -0800 @@ -0,0 +1,115 @@ +# MrJuneJune Integration Tests + +This directory contains comprehensive integration tests for all mrjunejune endpoints. + +## Test Structure + +### Test Files +- `integration_test.c` - Main integration test suite +- `create_snapshots.c` - Utility to generate/update snapshot files +- `shiba.webp` - Test image for image-to-webp conversion +- `test_avi.avi` - Test video for video-to-mp4 conversion + +### Snapshot Directory +- `snapshots/` - Contains expected HTTP responses for GET endpoints + +## Endpoints Tested + +### GET Endpoints (200 OK with Snapshot Verification) +- `/` - Home page +- `/resume` - Resume page +- `/tools` - Tools page +- `/tools/markdown_to_html` - Markdown to HTML converter +- `/tools/file_converter` - File converter tool + +### GET Endpoints (301 Redirects) +- `/index.html` → `/` +- `/resume/index.html` → `/resume` +- `/tools/index.html` → `/tools` +- `/tools/markdown_to_html/index.html` → `/tools/markdown_to_html` +- `/tools/file_converter/index.html` → `/tools/file_converter` + +### GET Endpoints (404 Not Found) +- `/nonexistent` +- `/does/not/exist` +- `/missing.html` + +### POST Endpoints +- `/api/convert/image-to-webp` - Converts images to WebP format + - Tests file upload, conversion, and download + - Verifies Content-Type: image/webp +- `/api/convert/video-to-mp4` - Converts videos to MP4 format + - Tests file upload, conversion, and download + - Verifies Content-Type: video/mp4 + +### Download Endpoint +- `/api/download/:filename` - Download converted files + - Tested automatically as part of POST conversion tests + +## Running Tests + +### First Time Setup - Create Snapshots +Before running tests for the first time, generate snapshots: + +```bash +bazel run //mrjunejune:create_snapshots +``` + +This will: +1. Start the mrjunejune server +2. Make HTTP requests to all GET endpoints +3. Save responses to `snapshots/` directory +4. Stop the server + +### Run Integration Tests +```bash +bazel test //mrjunejune:integration_test +``` + +This will: +1. Start the mrjunejune server on port 6969 +2. Test all GET endpoints against their snapshots +3. Test POST conversion endpoints with real file uploads +4. Verify downloads work correctly +5. Report pass/fail for each test + +### View Test Output +```bash +bazel test //mrjunejune:integration_test --test_output=all +``` + +## Test Coverage + +✓ All 10 registered endpoints are tested +✓ Snapshot testing for HTML responses +✓ File upload and conversion testing +✓ Download functionality testing +✓ Error handling (404 responses) +✓ Redirect testing (301 responses) + +## Updating Tests + +### When HTML Changes +If you modify any HTML templates: + +```bash +# Regenerate snapshots +bazel run //mrjunejune:create_snapshots + +# 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/create_snapshots.c Fri Jan 02 18:02:22 2026 -0800 +++ b/mrjunejune/test/create_snapshots.c Fri Jan 02 18:13:32 2026 -0800 @@ -106,11 +106,23 @@ // Define snapshots to create - paths that should succeed (200 OK) SnapshotConfig success_snapshots[] = { {"/", 200, snapshot_path, TEST_HOST, TEST_PORT}, - {"/index.html", 200, snapshot_path, TEST_HOST, TEST_PORT}, + {"/resume", 200, snapshot_path, TEST_HOST, TEST_PORT}, + {"/tools", 200, snapshot_path, TEST_HOST, TEST_PORT}, + {"/tools/markdown_to_html", 200, snapshot_path, TEST_HOST, TEST_PORT}, + {"/tools/file_converter", 200, snapshot_path, TEST_HOST, TEST_PORT}, }; int num_success = sizeof(success_snapshots) / sizeof(success_snapshots[0]); - // Define snapshots for error paths (404) + // Define snapshots for redirect endpoints (301) + SnapshotConfig redirect_snapshots[] = { + {"/index.html", 301, snapshot_path, TEST_HOST, TEST_PORT}, + {"/resume/index.html", 301, snapshot_path, TEST_HOST, TEST_PORT}, + {"/tools/index.html", 301, snapshot_path, TEST_HOST, TEST_PORT}, + {"/tools/markdown_to_html/index.html", 301, snapshot_path, TEST_HOST, TEST_PORT}, + {"/tools/file_converter/index.html", 301, snapshot_path, TEST_HOST, TEST_PORT}, + }; + int num_redirects = sizeof(redirect_snapshots) / sizeof(redirect_snapshots[0]); + SnapshotConfig error_snapshots[] = { {"/nonexistent", 404, snapshot_path, TEST_HOST, TEST_PORT}, {"/does/not/exist", 404, snapshot_path, TEST_HOST, TEST_PORT}, @@ -135,8 +147,22 @@ } } + // Create redirect snapshots + printf("\nCreating snapshots for redirect paths:\n\n"); + for (int i = 0; i < num_redirects; i++) + { + if (Seobeo_Snapshot_Create(&redirect_snapshots[i]) == 0) + { + total_passed++; + } + else + { + total_failed++; + } + } + // Create error snapshots - printf("Creating snapshots for error paths:\n\n"); + printf("\nCreating snapshots for error paths:\n\n"); for (int i = 0; i < num_errors; i++) { if (Seobeo_Snapshot_Create(&error_snapshots[i]) == 0)
--- a/mrjunejune/test/integration_test.c Fri Jan 02 18:02:22 2026 -0800 +++ b/mrjunejune/test/integration_test.c Fri Jan 02 18:13:32 2026 -0800 @@ -380,6 +380,229 @@ return 0; } +// Helper: Send POST request with file data +int send_post_file(Seobeo_Handle *client, const char *path, const char *file_data, size_t file_size) +{ + char request_buffer[8192]; + int header_len = snprintf( + request_buffer, sizeof(request_buffer), + "POST %s HTTP/1.1\r\n" + "Host: %s\r\n" + "Content-Type: application/octet-stream\r\n" + "Content-Length: %zu\r\n" + "Connection: close\r\n" + "\r\n", + path, TEST_HOST, file_size + ); + + if (header_len < 0 || header_len >= sizeof(request_buffer)) + { + fprintf(stderr, "Request header too large\n"); + return -1; + } + + // Send headers + Seobeo_Handle_Queue(client, (uint8*)request_buffer, (uint32)header_len); + + // Send file data in chunks if needed + size_t remaining = file_size; + const char *ptr = file_data; + while (remaining > 0) + { + size_t chunk_size = remaining > 4096 ? 4096 : remaining; + Seobeo_Handle_Queue(client, (uint8*)ptr, (uint32)chunk_size); + ptr += chunk_size; + remaining -= chunk_size; + } + + return Seobeo_Handle_Flush(client); +} + +// Helper: Extract JSON field value from response body +char* extract_json_field(const char *json, const char *field, char *buffer, size_t buffer_size) +{ + char search_pattern[256]; + snprintf(search_pattern, sizeof(search_pattern), "\"%s\":\"", field); + + const char *start = strstr(json, search_pattern); + if (!start) + { + return NULL; + } + + start += strlen(search_pattern); + const char *end = strchr(start, '"'); + if (!end) + { + return NULL; + } + + size_t len = end - start; + if (len >= buffer_size) + { + len = buffer_size - 1; + } + + memcpy(buffer, start, len); + buffer[len] = '\0'; + + return buffer; +} + +// Helper: Test POST file conversion +int test_file_conversion(const char *endpoint, const char *test_file_path, + const char *expected_format, pid_t server_pid) +{ + printf(" Testing: POST %s\n", endpoint); + + // Read test file + size_t file_size; + char *file_data = read_file(test_file_path, &file_size); + if (!file_data) + { + printf(" ✗ Failed to read test file: %s\n", test_file_path); + return -1; + } + + printf(" → Loaded test file (%zu bytes)\n", file_size); + + // Create client and send request + Seobeo_Handle *client = create_test_client(); + if (!client) + { + printf(" ✗ Failed to create client connection\n"); + free(file_data); + return -1; + } + + if (send_post_file(client, endpoint, file_data, file_size) < 0) + { + printf(" ✗ Failed to send POST request\n"); + free(file_data); + Seobeo_Handle_Destroy(client); + return -1; + } + + free(file_data); + + // Read response + char *response = NULL; + size_t response_len = 0; + if (read_http_response(client, &response, &response_len) < 0) + { + printf(" ✗ Failed to read response\n"); + Seobeo_Handle_Destroy(client); + return -1; + } + + Seobeo_Handle_Destroy(client); + + // Parse status + int status = parse_http_status(response); + if (status != 200) + { + printf(" ✗ Conversion failed with status: %d\n", status); + printf(" Response: %s\n", response); + free(response); + return -1; + } + + printf(" ✓ Status code: 200\n"); + + // Extract download URL from JSON response + const char *body = strstr(response, "\r\n\r\n"); + if (!body) + { + printf(" ✗ No response body found\n"); + free(response); + return -1; + } + body += 4; + + char download_url[512]; + if (!extract_json_field(body, "download_url", download_url, sizeof(download_url))) + { + printf(" ✗ Failed to extract download_url from response\n"); + printf(" Response body: %s\n", body); + free(response); + return -1; + } + + printf(" ✓ Conversion succeeded\n"); + printf(" ✓ Download URL: %s\n", download_url); + free(response); + + // Test downloading the converted file + printf(" → Testing download: GET %s\n", download_url); + + client = create_test_client(); + if (!client) + { + printf(" ✗ Failed to create client for download\n"); + return -1; + } + + if (send_http_request(client, download_url, NULL) < 0) + { + printf(" ✗ Failed to send download request\n"); + Seobeo_Handle_Destroy(client); + return -1; + } + + response = NULL; + response_len = 0; + if (read_http_response(client, &response, &response_len) < 0) + { + printf(" ✗ Failed to read download response\n"); + Seobeo_Handle_Destroy(client); + return -1; + } + + Seobeo_Handle_Destroy(client); + + status = parse_http_status(response); + if (status != 200) + { + printf(" ✗ Download failed with status: %d\n", status); + free(response); + return -1; + } + + // Find body in download response + body = strstr(response, "\r\n\r\n"); + if (!body) + { + printf(" ✗ No file data in download response\n"); + free(response); + return -1; + } + body += 4; + + size_t downloaded_size = response_len - (body - response); + + // Verify content type in response headers + const char *content_type = strstr(response, "Content-Type: "); + if (!content_type) + { + printf(" ✗ No Content-Type header in download\n"); + free(response); + return -1; + } + + if (strstr(content_type, expected_format) == NULL) + { + printf(" ✗ Wrong content type (expected %s)\n", expected_format); + free(response); + return -1; + } + + printf(" ✓ Downloaded converted file (%zu bytes)\n", downloaded_size); + printf(" ✓ Content-Type: %s\n", expected_format); + + free(response); + return 0; +} + // Helper: Start test server pid_t start_test_server(const char *server_binary) { @@ -498,10 +721,23 @@ // Define test cases - paths that should succeed (200 OK) TestCase success_tests[] = { {"/", 200, NULL, NULL, NULL, 0}, - {"/index.html", 200, NULL, NULL, NULL, 0}, + {"/resume", 200, NULL, NULL, NULL, 0}, + {"/tools", 200, NULL, NULL, NULL, 0}, + {"/tools/markdown_to_html", 200, NULL, NULL, NULL, 0}, + {"/tools/file_converter", 200, NULL, NULL, NULL, 0}, }; int num_success_tests = sizeof(success_tests) / sizeof(success_tests[0]); + // Define test cases - paths that should redirect (301) + TestCase redirect_tests[] = { + {"/index.html", 301, NULL, NULL, NULL, 0}, + {"/resume/index.html", 301, NULL, NULL, NULL, 0}, + {"/tools/index.html", 301, NULL, NULL, NULL, 0}, + {"/tools/markdown_to_html/index.html", 301, NULL, NULL, NULL, 0}, + {"/tools/file_converter/index.html", 301, NULL, NULL, NULL, 0}, + }; + int num_redirect_tests = sizeof(redirect_tests) / sizeof(redirect_tests[0]); + // Define test cases - paths that should fail (404) TestCase failure_tests[] = { {"/nonexistent", 404, NULL, NULL, NULL, 0}, @@ -515,6 +751,10 @@ { init_test_case(&success_tests[i]); } + for (int i = 0; i < num_redirect_tests; i++) + { + init_test_case(&redirect_tests[i]); + } for (int i = 0; i < num_failure_tests; i++) { init_test_case(&failure_tests[i]); @@ -536,6 +776,22 @@ printf("\n"); + // Run redirect tests + printf("Running tests for paths that should redirect:\n"); + for (int i = 0; i < num_redirect_tests; i++) + { + if (execute_test_case(&redirect_tests[i], server_pid) == 0) + { + passed_tests++; + } + else + { + failed_tests++; + } + } + + printf("\n"); + // Run failure tests printf("Running tests for paths that should fail:\n"); for (int i = 0; i < num_failure_tests; i++) @@ -572,11 +828,48 @@ failed_tests++; } + printf("\n"); + + // Test POST endpoints + printf("Running tests for POST conversion endpoints:\n"); + + // Test image-to-webp conversion + if (test_file_conversion("/api/convert/image-to-webp", + "mrjunejune/test/shiba.webp", + "image/webp", + server_pid) == 0) + { + passed_tests++; + } + else + { + failed_tests++; + } + + printf("\n"); + + // Test video-to-mp4 conversion + if (test_file_conversion("/api/convert/video-to-mp4", + "mrjunejune/test/test_avi.avi", + "video/mp4", + server_pid) == 0) + { + passed_tests++; + } + else + { + failed_tests++; + } + // Cleanup test cases for (int i = 0; i < num_success_tests; i++) { cleanup_test_case(&success_tests[i]); } + for (int i = 0; i < num_redirect_tests; i++) + { + cleanup_test_case(&redirect_tests[i]); + } for (int i = 0; i < num_failure_tests; i++) { cleanup_test_case(&failure_tests[i]);
--- a/mrjunejune/test/snapshots/index.html.snapshot Fri Jan 02 18:02:22 2026 -0800 +++ b/mrjunejune/test/snapshots/index.html.snapshot Fri Jan 02 18:13:32 2026 -0800 @@ -1,245 +1,202 @@ -HTTP/1.1 200 OK -Content-Type: text/html; charset=utf-8 -Content-Length: 12120 +HTTP/1.1 301 Moved Permanently +Content-Type: text/plain +Content-Length: 0 Connection: close +Body: -<!doctype html> -<html lang="en"> - <head> - <BaseHead title="Resume" description="June's resume" /> - <link rel="stylesheet" href="base.css" /> - <link rel="stylesheet" href="resume.css" /> +t/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" /> + + + <style> + .epi-photo { + display: flex; + justify-content: center; + margin-bottom: 10px; + } + </style> </head> <body> - <main> - <div> - <p>Hi, my name is Juntae, but most people call me <b>June</b>.</p> - <p>I am a software engineer with experience spanning a wide range of companies, from small startups to FAANG. </p> - <p>Feel free to check out my <a href="/resume.pdf"> resume </a> below, and if you're interested, don’t hesitate to contact me for contract work ranging from web/app development to embedded programming.</p> - </div> - <div class="info"> - <p><span class="header-firstname-style"> JUNTAE </span><span class="header-lastname-style">PARK</span><p> - <p class="header-position-style"> FULL STACK DEVELOPER · SOFTWARE ENGINEER </p> - <div class="header-address-style"> - Bay Area, CA, USA - </div> - <div class="header-social-style"> - <p>📱(US) 650-531-1728 |📱(CA) 437-580-8026 | <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"> - <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 - </a> - </p> - </div> - </div> + <style> + :root { + --header-background: var(--white); + --header-color: rgb(var(--black)); + --link-hover-accent: var(--awesome); + } + + /* Fixed icon in top left corner */ + #themeToggle { + position: fixed; + top: 20px; + left: 20px; + background: var(--header-background); + display: flex; + align-items: center; + border-radius: 50%; + cursor: pointer; + z-index: 1000; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); + transition: transform 0.2s ease; + } + + #themeToggle:hover { + transform: scale(1.05); + } + + /* Professional header */ + header { + margin: auto; + padding: 1.5em 1em; + font-family: "More", sans-serif; + box-shadow: 0 2px 8px rgba(var(--black), 5%); + width: 720px; + max-width: calc(100% - 2em); + text-align: center; + } + + header h1 { + margin: 0; + font-size: 1.8em; + font-weight: 700; + letter-spacing: -0.5px; + } + + header h1 a { + text-decoration: none; + color: var(--header-color); + } + + header h1 a::before { + display: none; + } + + /* Mobile responsiveness */ + @media (max-width: 720px) { + #themeToggle { + top: 15px; + left: 15px; + } + + header { + padding: 1em; + } + + header h1 { + font-size: 1.5em; + } + } - <div class="sub-header"> - <h2 class="section-style"> Summary </h2> - <div class="line"></div> - </div> - <p class="paragraph-style">Software Engineer with 8 years of hands-on experience across diverse tech stacks, from early-stage startups to FANG-scale systems. Adept in designing and delivering robust software solutions using modern languages, frameworks, and cloud platforms. Open to impactful work.</p> - <div class="sub-header"> + @media (max-width: 480px) { + #themeToggle { + top: 10px; + left: 10px; + } + + #themeToggle img { + height: 40px; + width: 40px; + } + + header h1 { + font-size: 1.3em; + } + } + + #logo { + width: 300px; + } - <h2 class="section-style"> Skills </h2> - <div class="line"></div> - </div> - <div class="paragraph-style"> - <p> - <span class="entry-title-style"> Programming Languages:</span> - <span class="skill-type-style"> TypeScript, Python, C++/C, Ruby, Java, MATLAB </span> - </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> + /* 1. DEFINE THE DEFAULTS (Light Mode) */ + :root { + --logo-invert: invert(0); + --epi-grayscale: grayscale(0) brightness(1); + } + + /* 2. MANUAL DARK OVERRIDE */ + html.dark { + --logo-invert: invert(1); + --epi-grayscale: grayscale(1); + } + + /* 3. MANUAL LIGHT OVERRIDE */ + html.light-mode { + --logo-invert: invert(0); + --epi-grayscale: brightness(2.9) grayscale(1); + } + + /* 4. SYSTEM PREFERENCE */ + @media (prefers-color-scheme: dark) { + :root:not(.light-mode) { + --logo-invert: invert(1); + } + } + + /* 5. APPLY TO ELEMENTS */ + #logo { + -webkit-filter: var(--logo-invert); + filter: var(--logo-invert); + transition: filter 0.3s ease; + } + + .epi-logo { + -webkit-filter: var(--epi-grayscale); + filter: var(--epi-grayscale); + transition: filter 0.3s ease; + } +</style> + +<div id="themeToggle"> + <img id="epiChan" class="epi-logo" aria-label="Toggle dark mode" src="/public/epi_all_colors.svg" height="50" width="50"> +</div> + +<header> + <h1><a href="/">MrJuneJune</a></h1> +</header> +<script src="/index.js"></script> + + + <main> + <p>Hi, my name is Juntae, but most people call me June or MrJuneJune.</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="epi-photo"> + <img id="currentPhoto" style="opacity: 0; transition: opacity 0.2s;" /> </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> + <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="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> + <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;"> + <small>© 2026 June Park</small> +</div> - <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> - </main> </body> -</html> + <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: / +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mrjunejune/test/snapshots/resume.snapshot Fri Jan 02 18:13:32 2026 -0800 @@ -0,0 +1,401 @@ +HTTP/1.1 200 OK +Content-Type: text/html +Content-Length: 15108 +Connection: close + +<!doctype html> +<html lang="en"> + <head> + <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> + +<link rel="preload" href="/public/fonts/atkinson-regular.woff" as="font" type="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> + +<link rel="preload" href="/base.css" as="style" /> +<link rel="stylesheet" href="/base.css" /> + + + <link rel="preload" href="resume.css" as="style" /> + <link rel="stylesheet" href="resume.css" /> + </head> + <body> + <style> + :root { + --header-background: var(--white); + --header-color: rgb(var(--black)); + --link-hover-accent: var(--awesome); + } + + /* Fixed icon in top left corner */ + #themeToggle { + position: fixed; + top: 20px; + left: 20px; + background: var(--header-background); + display: flex; + align-items: center; + border-radius: 50%; + cursor: pointer; + z-index: 1000; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); + transition: transform 0.2s ease; + } + + #themeToggle:hover { + transform: scale(1.05); + } + + /* Professional header */ + header { + margin: auto; + padding: 1.5em 1em; + font-family: "More", sans-serif; + box-shadow: 0 2px 8px rgba(var(--black), 5%); + width: 720px; + max-width: calc(100% - 2em); + text-align: center; + } + + header h1 { + margin: 0; + font-size: 1.8em; + font-weight: 700; + letter-spacing: -0.5px; + } + + header h1 a { + text-decoration: none; + color: var(--header-color); + } + + header h1 a::before { + display: none; + } + + /* Mobile responsiveness */ + @media (max-width: 720px) { + #themeToggle { + top: 15px; + left: 15px; + } + + header { + padding: 1em; + } + + header h1 { + font-size: 1.5em; + } + } + + @media (max-width: 480px) { + #themeToggle { + top: 10px; + left: 10px; + } + + #themeToggle img { + height: 40px; + width: 40px; + } + + header h1 { + font-size: 1.3em; + } + } + + #logo { + width: 300px; + } + + /* 1. DEFINE THE DEFAULTS (Light Mode) */ + :root { + --logo-invert: invert(0); + --epi-grayscale: grayscale(0) brightness(1); + } + + /* 2. MANUAL DARK OVERRIDE */ + html.dark { + --logo-invert: invert(1); + --epi-grayscale: grayscale(1); + } + + /* 3. MANUAL LIGHT OVERRIDE */ + html.light-mode { + --logo-invert: invert(0); + --epi-grayscale: brightness(2.9) grayscale(1); + } + + /* 4. SYSTEM PREFERENCE */ + @media (prefers-color-scheme: dark) { + :root:not(.light-mode) { + --logo-invert: invert(1); + } + } + + /* 5. APPLY TO ELEMENTS */ + #logo { + -webkit-filter: var(--logo-invert); + filter: var(--logo-invert); + transition: filter 0.3s ease; + } + + .epi-logo { + -webkit-filter: var(--epi-grayscale); + filter: var(--epi-grayscale); + transition: filter 0.3s ease; + } +</style> + +<div id="themeToggle"> + <img id="epiChan" class="epi-logo" aria-label="Toggle dark mode" src="/public/epi_all_colors.svg" height="50" width="50"> +</div> + +<header> + <h1><a href="/">MrJuneJune</a></h1> +</header> +<script src="/index.js"></script> + + + <main> + <div class="info"> + <a href="/public/resume.pdf">Download PDF</a> + <hr> + <p><span class="header-firstname-style"> JUNTAE </span><span class="header-lastname-style">PARK</span><p> + <p class="header-position-style"> SOFTWARE ENGINEER </p> + <div class="header-address-style"> + 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"> + <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 + </a> + </p> + </div> + </div> + + <div class="sub-header"> + <h2 class="section-style"> Summary </h2> + <div class="line"></div> + </div> + <p class="paragraph-style">Software Engineer with 9 years of hands-on experience across diverse tech stacks, from early-stage startups to FANG-scale systems. Adept in designing and delivering robust software solutions using modern languages, frameworks, and cloud platforms. <br><br> Open to impactful work.</p> + <div class="sub-header"> + + <h2 class="section-style"> Skills </h2> + <div class="line"></div> + </div> + <div class="paragraph-style"> + <p> + <span class="entry-title-style"> Programming Languages:</span> + <span class="skill-type-style"> TypeScript, Python, C/C++, Ruby, Java, MATLAB </span> + </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"> + <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>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mrjunejune/test/snapshots/resume_index.html.snapshot Fri Jan 02 18:13:32 2026 -0800 @@ -0,0 +1,203 @@ +HTTP/1.1 301 Moved Permanently +Content-Type: text/plain +Content-Length: 0 +Connection: close +Body: + +t/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" /> + + + <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)); + --link-hover-accent: var(--awesome); + } + + /* Fixed icon in top left corner */ + #themeToggle { + position: fixed; + top: 20px; + left: 20px; + background: var(--header-background); + display: flex; + align-items: center; + border-radius: 50%; + cursor: pointer; + z-index: 1000; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); + transition: transform 0.2s ease; + } + + #themeToggle:hover { + transform: scale(1.05); + } + + /* Professional header */ + header { + margin: auto; + padding: 1.5em 1em; + font-family: "More", sans-serif; + box-shadow: 0 2px 8px rgba(var(--black), 5%); + width: 720px; + max-width: calc(100% - 2em); + text-align: center; + } + + header h1 { + margin: 0; + font-size: 1.8em; + font-weight: 700; + letter-spacing: -0.5px; + } + + header h1 a { + text-decoration: none; + color: var(--header-color); + } + + header h1 a::before { + display: none; + } + + /* Mobile responsiveness */ + @media (max-width: 720px) { + #themeToggle { + top: 15px; + left: 15px; + } + + header { + padding: 1em; + } + + header h1 { + font-size: 1.5em; + } + } + + @media (max-width: 480px) { + #themeToggle { + top: 10px; + left: 10px; + } + + #themeToggle img { + height: 40px; + width: 40px; + } + + header h1 { + font-size: 1.3em; + } + } + + #logo { + width: 300px; + } + + /* 1. DEFINE THE DEFAULTS (Light Mode) */ + :root { + --logo-invert: invert(0); + --epi-grayscale: grayscale(0) brightness(1); + } + + /* 2. MANUAL DARK OVERRIDE */ + html.dark { + --logo-invert: invert(1); + --epi-grayscale: grayscale(1); + } + + /* 3. MANUAL LIGHT OVERRIDE */ + html.light-mode { + --logo-invert: invert(0); + --epi-grayscale: brightness(2.9) grayscale(1); + } + + /* 4. SYSTEM PREFERENCE */ + @media (prefers-color-scheme: dark) { + :root:not(.light-mode) { + --logo-invert: invert(1); + } + } + + /* 5. APPLY TO ELEMENTS */ + #logo { + -webkit-filter: var(--logo-invert); + filter: var(--logo-invert); + transition: filter 0.3s ease; + } + + .epi-logo { + -webkit-filter: var(--epi-grayscale); + filter: var(--epi-grayscale); + transition: filter 0.3s ease; + } +</style> + +<div id="themeToggle"> + <img id="epiChan" class="epi-logo" aria-label="Toggle dark mode" src="/public/epi_all_colors.svg" height="50" width="50"> +</div> + +<header> + <h1><a href="/">MrJuneJune</a></h1> +</header> +<script src="/index.js"></script> + + + <main> + <p>Hi, my name is Juntae, but most people call me June or MrJuneJune.</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="epi-photo"> + <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> + + <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;"> + <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 +
--- a/mrjunejune/test/snapshots/root.snapshot Fri Jan 02 18:02:22 2026 -0800 +++ b/mrjunejune/test/snapshots/root.snapshot Fri Jan 02 18:13:32 2026 -0800 @@ -1,245 +1,212 @@ HTTP/1.1 200 OK -Content-Type: text/html; charset=utf-8 -Content-Length: 12120 +Content-Type: text/html +Content-Length: 5322 Connection: close <!doctype html> <html lang="en"> <head> - <BaseHead title="Resume" description="June's resume" /> - <link rel="stylesheet" href="base.css" /> - <link rel="stylesheet" href="resume.css" /> + <title> MrJuneJune </title> + <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> + +<link rel="preload" href="/public/fonts/atkinson-regular.woff" as="font" type="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> + +<link rel="preload" href="/base.css" as="style" /> +<link rel="stylesheet" href="/base.css" /> + + + <style> + .epi-photo { + display: flex; + justify-content: center; + margin-bottom: 10px; + } + </style> </head> <body> - <main> - <div> - <p>Hi, my name is Juntae, but most people call me <b>June</b>.</p> - <p>I am a software engineer with experience spanning a wide range of companies, from small startups to FAANG. </p> - <p>Feel free to check out my <a href="/resume.pdf"> resume </a> below, and if you're interested, don’t hesitate to contact me for contract work ranging from web/app development to embedded programming.</p> - </div> - <div class="info"> - <p><span class="header-firstname-style"> JUNTAE </span><span class="header-lastname-style">PARK</span><p> - <p class="header-position-style"> FULL STACK DEVELOPER · SOFTWARE ENGINEER </p> - <div class="header-address-style"> - Bay Area, CA, USA - </div> - <div class="header-social-style"> - <p>📱(US) 650-531-1728 |📱(CA) 437-580-8026 | <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"> - <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 - </a> - </p> - </div> - </div> + <style> + :root { + --header-background: var(--white); + --header-color: rgb(var(--black)); + --link-hover-accent: var(--awesome); + } + + /* Fixed icon in top left corner */ + #themeToggle { + position: fixed; + top: 20px; + left: 20px; + background: var(--header-background); + display: flex; + align-items: center; + border-radius: 50%; + cursor: pointer; + z-index: 1000; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); + transition: transform 0.2s ease; + } + + #themeToggle:hover { + transform: scale(1.05); + } + + /* Professional header */ + header { + margin: auto; + padding: 1.5em 1em; + font-family: "More", sans-serif; + box-shadow: 0 2px 8px rgba(var(--black), 5%); + width: 720px; + max-width: calc(100% - 2em); + text-align: center; + } + + header h1 { + margin: 0; + font-size: 1.8em; + font-weight: 700; + letter-spacing: -0.5px; + } + + header h1 a { + text-decoration: none; + color: var(--header-color); + } + + header h1 a::before { + display: none; + } + + /* Mobile responsiveness */ + @media (max-width: 720px) { + #themeToggle { + top: 15px; + left: 15px; + } + + header { + padding: 1em; + } + + header h1 { + font-size: 1.5em; + } + } - <div class="sub-header"> - <h2 class="section-style"> Summary </h2> - <div class="line"></div> - </div> - <p class="paragraph-style">Software Engineer with 8 years of hands-on experience across diverse tech stacks, from early-stage startups to FANG-scale systems. Adept in designing and delivering robust software solutions using modern languages, frameworks, and cloud platforms. Open to impactful work.</p> - <div class="sub-header"> + @media (max-width: 480px) { + #themeToggle { + top: 10px; + left: 10px; + } + + #themeToggle img { + height: 40px; + width: 40px; + } + + header h1 { + font-size: 1.3em; + } + } + + #logo { + width: 300px; + } - <h2 class="section-style"> Skills </h2> - <div class="line"></div> - </div> - <div class="paragraph-style"> - <p> - <span class="entry-title-style"> Programming Languages:</span> - <span class="skill-type-style"> TypeScript, Python, C++/C, Ruby, Java, MATLAB </span> - </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> + /* 1. DEFINE THE DEFAULTS (Light Mode) */ + :root { + --logo-invert: invert(0); + --epi-grayscale: grayscale(0) brightness(1); + } + + /* 2. MANUAL DARK OVERRIDE */ + html.dark { + --logo-invert: invert(1); + --epi-grayscale: grayscale(1); + } + + /* 3. MANUAL LIGHT OVERRIDE */ + html.light-mode { + --logo-invert: invert(0); + --epi-grayscale: brightness(2.9) grayscale(1); + } + + /* 4. SYSTEM PREFERENCE */ + @media (prefers-color-scheme: dark) { + :root:not(.light-mode) { + --logo-invert: invert(1); + } + } + + /* 5. APPLY TO ELEMENTS */ + #logo { + -webkit-filter: var(--logo-invert); + filter: var(--logo-invert); + transition: filter 0.3s ease; + } + + .epi-logo { + -webkit-filter: var(--epi-grayscale); + filter: var(--epi-grayscale); + transition: filter 0.3s ease; + } +</style> + +<div id="themeToggle"> + <img id="epiChan" class="epi-logo" aria-label="Toggle dark mode" src="/public/epi_all_colors.svg" height="50" width="50"> +</div> + +<header> + <h1><a href="/">MrJuneJune</a></h1> +</header> +<script src="/index.js"></script> + + + <main> + <p>Hi, my name is Juntae, but most people call me June or MrJuneJune.</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="epi-photo"> + <img id="currentPhoto" style="opacity: 0; transition: opacity 0.2s;" /> </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> + <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="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> + <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;"> + <small>© 2026 June Park</small> +</div> - <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> - </main> </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> </html>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mrjunejune/test/snapshots/tools.snapshot Fri Jan 02 18:13:32 2026 -0800 @@ -0,0 +1,184 @@ +HTTP/1.1 200 OK +Content-Type: text/html +Content-Length: 4152 +Connection: close + +<!doctype html> +<html lang="en"> + <head> + <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> + +<link rel="preload" href="/public/fonts/atkinson-regular.woff" as="font" type="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> + +<link rel="preload" href="/base.css" as="style" /> +<link rel="stylesheet" href="/base.css" /> + + + <link rel="stylesheet" href="/tools/index.css" /> + </head> + <body> + <style> + :root { + --header-background: var(--white); + --header-color: rgb(var(--black)); + --link-hover-accent: var(--awesome); + } + + /* Fixed icon in top left corner */ + #themeToggle { + position: fixed; + top: 20px; + left: 20px; + background: var(--header-background); + display: flex; + align-items: center; + border-radius: 50%; + cursor: pointer; + z-index: 1000; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); + transition: transform 0.2s ease; + } + + #themeToggle:hover { + transform: scale(1.05); + } + + /* Professional header */ + header { + margin: auto; + padding: 1.5em 1em; + font-family: "More", sans-serif; + box-shadow: 0 2px 8px rgba(var(--black), 5%); + width: 720px; + max-width: calc(100% - 2em); + text-align: center; + } + + header h1 { + margin: 0; + font-size: 1.8em; + font-weight: 700; + letter-spacing: -0.5px; + } + + header h1 a { + text-decoration: none; + color: var(--header-color); + } + + header h1 a::before { + display: none; + } + + /* Mobile responsiveness */ + @media (max-width: 720px) { + #themeToggle { + top: 15px; + left: 15px; + } + + header { + padding: 1em; + } + + header h1 { + font-size: 1.5em; + } + } + + @media (max-width: 480px) { + #themeToggle { + top: 10px; + left: 10px; + } + + #themeToggle img { + height: 40px; + width: 40px; + } + + header h1 { + font-size: 1.3em; + } + } + + #logo { + width: 300px; + } + + /* 1. DEFINE THE DEFAULTS (Light Mode) */ + :root { + --logo-invert: invert(0); + --epi-grayscale: grayscale(0) brightness(1); + } + + /* 2. MANUAL DARK OVERRIDE */ + html.dark { + --logo-invert: invert(1); + --epi-grayscale: grayscale(1); + } + + /* 3. MANUAL LIGHT OVERRIDE */ + html.light-mode { + --logo-invert: invert(0); + --epi-grayscale: brightness(2.9) grayscale(1); + } + + /* 4. SYSTEM PREFERENCE */ + @media (prefers-color-scheme: dark) { + :root:not(.light-mode) { + --logo-invert: invert(1); + } + } + + /* 5. APPLY TO ELEMENTS */ + #logo { + -webkit-filter: var(--logo-invert); + filter: var(--logo-invert); + transition: filter 0.3s ease; + } + + .epi-logo { + -webkit-filter: var(--epi-grayscale); + filter: var(--epi-grayscale); + transition: filter 0.3s ease; + } +</style> + +<div id="themeToggle"> + <img id="epiChan" class="epi-logo" aria-label="Toggle dark mode" src="/public/epi_all_colors.svg" height="50" width="50"> +</div> + +<header> + <h1><a href="/">MrJuneJune</a></h1> +</header> +<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;"> + <small>© 2026 June Park</small> +</div> + + </body> +</html>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mrjunejune/test/snapshots/tools_file_converter.snapshot Fri Jan 02 18:13:32 2026 -0800 @@ -0,0 +1,315 @@ +HTTP/1.1 200 OK +Content-Type: text/html +Content-Length: 7742 +Connection: close + +<!DOCTYPE html> +<html lang="en"> +<head> + <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"> + +<link rel="preload" href="/public/fonts/Roboto-Regular.ttf" as="font" crossorigin> +<link rel="preload" href="/public/fonts/Roboto-Thin.ttf"as="font" crossorigin> + +<link rel="preload" href="/public/fonts/atkinson-regular.woff" as="font" type="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> + +<link rel="preload" href="/base.css" as="style" /> +<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> +</head> +<body> + <style> + :root { + --header-background: var(--white); + --header-color: rgb(var(--black)); + --link-hover-accent: var(--awesome); + } + + /* Fixed icon in top left corner */ + #themeToggle { + position: fixed; + top: 20px; + left: 20px; + background: var(--header-background); + display: flex; + align-items: center; + border-radius: 50%; + cursor: pointer; + z-index: 1000; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); + transition: transform 0.2s ease; + } + + #themeToggle:hover { + transform: scale(1.05); + } + + /* Professional header */ + header { + margin: auto; + padding: 1.5em 1em; + font-family: "More", sans-serif; + box-shadow: 0 2px 8px rgba(var(--black), 5%); + width: 720px; + max-width: calc(100% - 2em); + text-align: center; + } + + header h1 { + margin: 0; + font-size: 1.8em; + font-weight: 700; + letter-spacing: -0.5px; + } + + header h1 a { + text-decoration: none; + color: var(--header-color); + } + + header h1 a::before { + display: none; + } + + /* Mobile responsiveness */ + @media (max-width: 720px) { + #themeToggle { + top: 15px; + left: 15px; + } + + header { + padding: 1em; + } + + header h1 { + font-size: 1.5em; + } + } + + @media (max-width: 480px) { + #themeToggle { + top: 10px; + left: 10px; + } + + #themeToggle img { + height: 40px; + width: 40px; + } + + header h1 { + font-size: 1.3em; + } + } + + #logo { + width: 300px; + } + + /* 1. DEFINE THE DEFAULTS (Light Mode) */ + :root { + --logo-invert: invert(0); + --epi-grayscale: grayscale(0) brightness(1); + } + + /* 2. MANUAL DARK OVERRIDE */ + html.dark { + --logo-invert: invert(1); + --epi-grayscale: grayscale(1); + } + + /* 3. MANUAL LIGHT OVERRIDE */ + html.light-mode { + --logo-invert: invert(0); + --epi-grayscale: brightness(2.9) grayscale(1); + } + + /* 4. SYSTEM PREFERENCE */ + @media (prefers-color-scheme: dark) { + :root:not(.light-mode) { + --logo-invert: invert(1); + } + } + + /* 5. APPLY TO ELEMENTS */ + #logo { + -webkit-filter: var(--logo-invert); + filter: var(--logo-invert); + transition: filter 0.3s ease; + } + + .epi-logo { + -webkit-filter: var(--epi-grayscale); + filter: var(--epi-grayscale); + transition: filter 0.3s ease; + } +</style> + +<div id="themeToggle"> + <img id="epiChan" class="epi-logo" aria-label="Toggle dark mode" src="/public/epi_all_colors.svg" height="50" width="50"> +</div> + +<header> + <h1><a href="/">MrJuneJune</a></h1> +</header> +<script src="/index.js"></script> + + + + <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> + + </div> + <script src="/tools/file_converter/index.js"></script> +</body> +</html>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mrjunejune/test/snapshots/tools_file_converter_index.html.snapshot Fri Jan 02 18:13:32 2026 -0800 @@ -0,0 +1,174 @@ +HTTP/1.1 301 Moved Permanently +Content-Type: text/plain +Content-Length: 0 +Connection: close +Body: + +reload" 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> + :root { + --header-background: var(--white); + --header-color: rgb(var(--black)); + --link-hover-accent: var(--awesome); + } + + /* Fixed icon in top left corner */ + #themeToggle { + position: fixed; + top: 20px; + left: 20px; + background: var(--header-background); + display: flex; + align-items: center; + border-radius: 50%; + cursor: pointer; + z-index: 1000; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); + transition: transform 0.2s ease; + } + + #themeToggle:hover { + transform: scale(1.05); + } + + /* Professional header */ + header { + margin: auto; + padding: 1.5em 1em; + font-family: "More", sans-serif; + box-shadow: 0 2px 8px rgba(var(--black), 5%); + width: 720px; + max-width: calc(100% - 2em); + text-align: center; + } + + header h1 { + margin: 0; + font-size: 1.8em; + font-weight: 700; + letter-spacing: -0.5px; + } + + header h1 a { + text-decoration: none; + color: var(--header-color); + } + + header h1 a::before { + display: none; + } + + /* Mobile responsiveness */ + @media (max-width: 720px) { + #themeToggle { + top: 15px; + left: 15px; + } + + header { + padding: 1em; + } + + header h1 { + font-size: 1.5em; + } + } + + @media (max-width: 480px) { + #themeToggle { + top: 10px; + left: 10px; + } + + #themeToggle img { + height: 40px; + width: 40px; + } + + header h1 { + font-size: 1.3em; + } + } + + #logo { + width: 300px; + } + + /* 1. DEFINE THE DEFAULTS (Light Mode) */ + :root { + --logo-invert: invert(0); + --epi-grayscale: grayscale(0) brightness(1); + } + + /* 2. MANUAL DARK OVERRIDE */ + html.dark { + --logo-invert: invert(1); + --epi-grayscale: grayscale(1); + } + + /* 3. MANUAL LIGHT OVERRIDE */ + html.light-mode { + --logo-invert: invert(0); + --epi-grayscale: brightness(2.9) grayscale(1); + } + + /* 4. SYSTEM PREFERENCE */ + @media (prefers-color-scheme: dark) { + :root:not(.light-mode) { + --logo-invert: invert(1); + } + } + + /* 5. APPLY TO ELEMENTS */ + #logo { + -webkit-filter: var(--logo-invert); + filter: var(--logo-invert); + transition: filter 0.3s ease; + } + + .epi-logo { + -webkit-filter: var(--epi-grayscale); + filter: var(--epi-grayscale); + transition: filter 0.3s ease; + } +</style> + +<div id="themeToggle"> + <img id="epiChan" class="epi-logo" aria-label="Toggle dark mode" src="/public/epi_all_colors.svg" height="50" width="50"> +</div> + +<header> + <h1><a href="/">MrJuneJune</a></h1> +</header> +<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;"> + <small>© 2026 June Park</small> +</div> + + </body> +</htmlLocation: /tools/file_converter +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mrjunejune/test/snapshots/tools_index.html.snapshot Fri Jan 02 18:13:32 2026 -0800 @@ -0,0 +1,468 @@ +HTTP/1.1 301 Moved Permanently +Content-Type: text/plain +Content-Length: 0 +Connection: close +Body: + +="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" /> + + + <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> +</head> +<body> + <style> + :root { + --header-background: var(--white); + --header-color: rgb(var(--black)); + --link-hover-accent: var(--awesome); + } + + /* Fixed icon in top left corner */ + #themeToggle { + position: fixed; + top: 20px; + left: 20px; + background: var(--header-background); + display: flex; + align-items: center; + border-radius: 50%; + cursor: pointer; + z-index: 1000; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); + transition: transform 0.2s ease; + } + + #themeToggle:hover { + transform: scale(1.05); + } + + /* Professional header */ + header { + margin: auto; + padding: 1.5em 1em; + font-family: "More", sans-serif; + box-shadow: 0 2px 8px rgba(var(--black), 5%); + width: 720px; + max-width: calc(100% - 2em); + text-align: center; + } + + header h1 { + margin: 0; + font-size: 1.8em; + font-weight: 700; + letter-spacing: -0.5px; + } + + header h1 a { + text-decoration: none; + color: var(--header-color); + } + + header h1 a::before { + display: none; + } + + /* Mobile responsiveness */ + @media (max-width: 720px) { + #themeToggle { + top: 15px; + left: 15px; + } + + header { + padding: 1em; + } + + header h1 { + font-size: 1.5em; + } + } + + @media (max-width: 480px) { + #themeToggle { + top: 10px; + left: 10px; + } + + #themeToggle img { + height: 40px; + width: 40px; + } + + header h1 { + font-size: 1.3em; + } + } + + #logo { + width: 300px; + } + + /* 1. DEFINE THE DEFAULTS (Light Mode) */ + :root { + --logo-invert: invert(0); + --epi-grayscale: grayscale(0) brightness(1); + } + + /* 2. MANUAL DARK OVERRIDE */ + html.dark { + --logo-invert: invert(1); + --epi-grayscale: grayscale(1); + } + + /* 3. MANUAL LIGHT OVERRIDE */ + html.light-mode { + --logo-invert: invert(0); + --epi-grayscale: brightness(2.9) grayscale(1); + } + + /* 4. SYSTEM PREFERENCE */ + @media (prefers-color-scheme: dark) { + :root:not(.light-mode) { + --logo-invert: invert(1); + } + } + + /* 5. APPLY TO ELEMENTS */ + #logo { + -webkit-filter: var(--logo-invert); + filter: var(--logo-invert); + transition: filter 0.3s ease; + } + + .epi-logo { + -webkit-filter: var(--epi-grayscale); + filter: var(--epi-grayscale); + transition: filter 0.3s ease; + } +</style> + +<div id="themeToggle"> + <img id="epiChan" class="epi-logo" aria-label="Toggle dark mode" src="/public/epi_all_colors.svg" height="50" width="50"> +</div> + +<header> + <h1><a href="/">MrJuneJune</a></h1> +</header> +<script src="/index.js"></script> + + + + <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> + + </div> + <script src="/tools/file_converter/index.js"></script> +</body> +</htmlLocation: /tools + +ition-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>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mrjunejune/test/snapshots/tools_markdown_to_html.snapshot Fri Jan 02 18:13:32 2026 -0800 @@ -0,0 +1,261 @@ +HTTP/1.1 200 OK +Content-Type: text/html +Content-Length: 5891 +Connection: close + +<!DOCTYPE html> +<html lang="en"> +<head> + <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"> + +<link rel="preload" href="/public/fonts/Roboto-Regular.ttf" as="font" crossorigin> +<link rel="preload" href="/public/fonts/Roboto-Thin.ttf"as="font" crossorigin> + +<link rel="preload" href="/public/fonts/atkinson-regular.woff" as="font" type="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> + +<link rel="preload" href="/base.css" as="style" /> +<link rel="stylesheet" href="/base.css" /> + + + <link rel="stylesheet" href="markdown_to_html/index.css" /> +</head> +<body> + <style> + :root { + --header-background: var(--white); + --header-color: rgb(var(--black)); + --link-hover-accent: var(--awesome); + } + + /* Fixed icon in top left corner */ + #themeToggle { + position: fixed; + top: 20px; + left: 20px; + background: var(--header-background); + display: flex; + align-items: center; + border-radius: 50%; + cursor: pointer; + z-index: 1000; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); + transition: transform 0.2s ease; + } + + #themeToggle:hover { + transform: scale(1.05); + } + + /* Professional header */ + header { + margin: auto; + padding: 1.5em 1em; + font-family: "More", sans-serif; + box-shadow: 0 2px 8px rgba(var(--black), 5%); + width: 720px; + max-width: calc(100% - 2em); + text-align: center; + } + + header h1 { + margin: 0; + font-size: 1.8em; + font-weight: 700; + letter-spacing: -0.5px; + } + + header h1 a { + text-decoration: none; + color: var(--header-color); + } + + header h1 a::before { + display: none; + } + + /* Mobile responsiveness */ + @media (max-width: 720px) { + #themeToggle { + top: 15px; + left: 15px; + } + + header { + padding: 1em; + } + + header h1 { + font-size: 1.5em; + } + } + + @media (max-width: 480px) { + #themeToggle { + top: 10px; + left: 10px; + } + + #themeToggle img { + height: 40px; + width: 40px; + } + + header h1 { + font-size: 1.3em; + } + } + + #logo { + width: 300px; + } + + /* 1. DEFINE THE DEFAULTS (Light Mode) */ + :root { + --logo-invert: invert(0); + --epi-grayscale: grayscale(0) brightness(1); + } + + /* 2. MANUAL DARK OVERRIDE */ + html.dark { + --logo-invert: invert(1); + --epi-grayscale: grayscale(1); + } + + /* 3. MANUAL LIGHT OVERRIDE */ + html.light-mode { + --logo-invert: invert(0); + --epi-grayscale: brightness(2.9) grayscale(1); + } + + /* 4. SYSTEM PREFERENCE */ + @media (prefers-color-scheme: dark) { + :root:not(.light-mode) { + --logo-invert: invert(1); + } + } + + /* 5. APPLY TO ELEMENTS */ + #logo { + -webkit-filter: var(--logo-invert); + filter: var(--logo-invert); + transition: filter 0.3s ease; + } + + .epi-logo { + -webkit-filter: var(--epi-grayscale); + filter: var(--epi-grayscale); + transition: filter 0.3s ease; + } +</style> + +<div id="themeToggle"> + <img id="epiChan" class="epi-logo" aria-label="Toggle dark mode" src="/public/epi_all_colors.svg" height="50" width="50"> +</div> + +<header> + <h1><a href="/">MrJuneJune</a></h1> +</header> +<script src="/index.js"></script> + + + <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: + +- **Bold text** and *italic text* +- [Links](https://example.com) +- `inline code` +- ~~strikethrough~~ + +### Lists + +1. Ordered lists +2. Like this one +3. With numbers + +- Unordered lists +- Use dashes +- Or asterisks + +### Code Blocks + +``` +function example() { + return "Hello World"; +} +``` + +### Blockquotes + +> This is a blockquote +> It can span multiple lines + +--- + +### Images + + + +**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> +</html>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mrjunejune/test/snapshots/tools_markdown_to_html_index.html.snapshot Fri Jan 02 18:13:32 2026 -0800 @@ -0,0 +1,204 @@ +HTTP/1.1 301 Moved Permanently +Content-Type: text/plain +Content-Length: 0 +Connection: close +Body: + +t/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" /> + + + <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)); + --link-hover-accent: var(--awesome); + } + + /* Fixed icon in top left corner */ + #themeToggle { + position: fixed; + top: 20px; + left: 20px; + background: var(--header-background); + display: flex; + align-items: center; + border-radius: 50%; + cursor: pointer; + z-index: 1000; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); + transition: transform 0.2s ease; + } + + #themeToggle:hover { + transform: scale(1.05); + } + + /* Professional header */ + header { + margin: auto; + padding: 1.5em 1em; + font-family: "More", sans-serif; + box-shadow: 0 2px 8px rgba(var(--black), 5%); + width: 720px; + max-width: calc(100% - 2em); + text-align: center; + } + + header h1 { + margin: 0; + font-size: 1.8em; + font-weight: 700; + letter-spacing: -0.5px; + } + + header h1 a { + text-decoration: none; + color: var(--header-color); + } + + header h1 a::before { + display: none; + } + + /* Mobile responsiveness */ + @media (max-width: 720px) { + #themeToggle { + top: 15px; + left: 15px; + } + + header { + padding: 1em; + } + + header h1 { + font-size: 1.5em; + } + } + + @media (max-width: 480px) { + #themeToggle { + top: 10px; + left: 10px; + } + + #themeToggle img { + height: 40px; + width: 40px; + } + + header h1 { + font-size: 1.3em; + } + } + + #logo { + width: 300px; + } + + /* 1. DEFINE THE DEFAULTS (Light Mode) */ + :root { + --logo-invert: invert(0); + --epi-grayscale: grayscale(0) brightness(1); + } + + /* 2. MANUAL DARK OVERRIDE */ + html.dark { + --logo-invert: invert(1); + --epi-grayscale: grayscale(1); + } + + /* 3. MANUAL LIGHT OVERRIDE */ + html.light-mode { + --logo-invert: invert(0); + --epi-grayscale: brightness(2.9) grayscale(1); + } + + /* 4. SYSTEM PREFERENCE */ + @media (prefers-color-scheme: dark) { + :root:not(.light-mode) { + --logo-invert: invert(1); + } + } + + /* 5. APPLY TO ELEMENTS */ + #logo { + -webkit-filter: var(--logo-invert); + filter: var(--logo-invert); + transition: filter 0.3s ease; + } + + .epi-logo { + -webkit-filter: var(--epi-grayscale); + filter: var(--epi-grayscale); + transition: filter 0.3s ease; + } +</style> + +<div id="themeToggle"> + <img id="epiChan" class="epi-logo" aria-label="Toggle dark mode" src="/public/epi_all_colors.svg" height="50" width="50"> +</div> + +<header> + <h1><a href="/">MrJuneJune</a></h1> +</header> +<script src="/index.js"></script> + + + <main> + <p>Hi, my name is Juntae, but most people call me June or MrJuneJune.</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="epi-photo"> + <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> + + <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;"> + <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 +