Mercurial
changeset 112:d6d578b49a19
[PostDog] Got CRUD working.
| author | June Park <parkjune1995@gmail.com> |
|---|---|
| date | Sun, 04 Jan 2026 11:38:44 -0800 |
| parents | 48f260576059 |
| children | 7a4e942814bc |
| files | dowa/d_string.c mrjunejune/test/snapshots/index.html.snapshot mrjunejune/test/snapshots/resume_index.html.snapshot mrjunejune/test/snapshots/tools_file_converter_index.html.snapshot mrjunejune/test/snapshots/tools_index.html.snapshot mrjunejune/test/snapshots/tools_markdown_to_html_index.html.snapshot postdog/BUILD postdog/main.c third_party/raylib/include/raygui.h |
| diffstat | 9 files changed, 904 insertions(+), 1308 deletions(-) [+] |
line wrap: on
line diff
--- a/dowa/d_string.c Sun Jan 04 06:39:16 2026 -0800 +++ b/dowa/d_string.c Sun Jan 04 11:38:44 2026 -0800 @@ -20,7 +20,7 @@ char **Dowa_String_Split(char *from, char *token, int32 from_length, int32 token_length, Dowa_Arena *p_arena) { - if (!from || from[0] == '\n') + if (!from) return NULL; int32 *token_pos_arr = NULL; @@ -37,9 +37,7 @@ } if (curr_token_pointer == token_length) - { Dowa_Array_Push(token_pos_arr, i); - } } } @@ -54,17 +52,12 @@ char *val = Dowa_String_Slice(from, start, end, p_arena); if (p_arena) - { Dowa_Array_Push_Arena(splitted_strings, val, p_arena); - } else - { Dowa_Array_Push(splitted_strings, val); - } - if (i < num_tokens) { + if (i < num_tokens) start = token_pos_arr[i] + token_length; - } } Dowa_Array_Free(token_pos_arr);
--- a/mrjunejune/test/snapshots/index.html.snapshot Sun Jan 04 06:39:16 2026 -0800 +++ b/mrjunejune/test/snapshots/index.html.snapshot Sun Jan 04 11:38:44 2026 -0800 @@ -3,443 +3,5 @@ Content-Length: 0 Connection: close Body: +Location: / -"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> -</htmlLocation: / - - <p> - <span class="entry-title-style">Tools & Platforms:</span> - <span class="skill-type-style">Bazel, PostgresSQL, Mercurial, Git, Pands, Raylib, XCode</span> - </p> - <p> - <span class="entry-title-style"> Web Frameworks: </span> - <span class="skill-type-style"> Django, Rails, React, Flask</span> - </p> - <p> - <span class="entry-title-style"> DevOp:</span> - <span class="skill-type-style"> Plummi, Heroku, DigitalOcean, AWS, Google Cloud </span> - </p> - <p> - <span class="entry-title-style"> Language:</span> - <span class="skill-type-style"> English, Korean, Japanese </span> - </p> - </div> - - <!-- Experiences --> - <div class="sub-header"> - <h2 class="section-style"> Experience </h2> - <div class="line"></div> - </div> - <div class="flex-box"> - <p class="entry-title-style"> - <a href="https://www.meta.com/">Meta</a> - </p> - <p class="entry-location-style">San Francisco, CA, USA</p> - </div> - <div class="flex-box"> - <p class="entry-position-style">SOFTWARE ENGINEER</p> - <p class="entry-date-style">Oct, 2024 - Present</p> - </div> - <ul class="description-style"> - <li> - Took initiative on Channel Value Rule, targeting the 16% of ad traffic with both app and web destinations to improve value attribution and ROI. - </li> - <li> - Built full-stack features using React and Hack/GraphQL, contributing to scalable, production-ready systems. - </li> - <li> - Partnered with data science to design A/B tests and analyze revenue impact of ads destination. - </li> - <li> - Proposed and implemented alpha improvements to internal testing infrastructure, reducing test time by 50% and enhancing developer velocity. - </li> - </ul> - <div class="flex-box"> - <p class="entry-title-style"> - <a href="https://www.wmg.com/">Warner Music Group</a> - </p> - <p class="entry-location-style">Toronto, ON, Canada</p> - </div> - <div class="flex-box"> - <p class="entry-position-style">TECHNICAL LEAD ENGINEER</p> - <p class="entry-date-style">July, 2023 - Sept, 2024</p> - </div> - <ul class="description-style"> - <li> - Implements <a href="https://bazel.build/">bazel </a>structure for the company for TypeScript and JavaScript code base for hermiticity and stablishing standards for JavaScript and - </li> - <li> - TypeScript testing and code structures. - </li> - <li> - Led a team of five engineers in building GraphQL endpoints for client-facing applications using Apollo and AppSync, supporting over 2000 RPS and auto scaling depending on request values. - </li> - <li> - Improved application response times by up to 85% for graphQL response by updating database schema and SQL queries, eliminating N+1 queries and lack of indexes. - </li> - <li> - Developed CI/CD pipelines for backend structures. - </li> - <li> - Designed infrastructure for pub/sub, caching, and media processing logic. - </li> - </ul> - - <div class="flex-box"> - <p class="entry-title-style"> - <a href="https://www.google.com/">Google</a> - </p> - <p class="entry-location-style">Toronto, ON, Canada</p> - </div> - <div class="flex-box"> - <p class="entry-position-style">SOFTWARE ENGINEER</p> - <p class="entry-date-style">Feb, 2022 - July 2023</p> - </div> - <ul class="description-style"> - <li> - Implements and maintained new features relating to App Script across google workspace platform including Gmail, sheets, and Docs.</li> - <li> - Improved a response time and render time of App Script hover card components.</li> - <li> - Collaborated with a team of developers to ensure timely and accurate delivery of features.</li> - <li> - Conducted user testing and gathered feedback to iterate on features for optimal user experience.</li> - </ul> - - <div class="flex-box"> - <p class="entry-title-style"> - <a href="https://www.everlywell.com/">Everlywell</a> - </p> - <p class="entry-location-style">Toronto, ON, Canada</p> - </div> - <div class="flex-box"> - <p class="entry-position-style">SOFTWARE ENGINEER</p> - <p class="entry-date-style">December, 2020 - Jan, 2022</p> - </div> - <ul class="description-style"> - <li> - Maintained Amazon amplify apps to create and deploy React web applications for companies such as <a href="https://brooklynnets.everlywell.com/">NBA</a>, <a href="https://tinder.everlywell.com/">Tinder</a>, and other companies for COVID-19 at-home test kits.</li> - <li> - Implemented a script that helps accurately access and refund unused covid test kits; helping company save up to 200,000 USD.</li> - <li> - Created several Rails controllers for internal purposes; mocking end to end user experience for QA, mass refund features for CX department, and more, ultimately reducing support tickets amount by 50 percent.</li> - <li> - Implemented an audit table to help debug problems and logged which process was responsible for the change of the record using PaperTrail gems</li> - </ul> - - <div class="flex-box"> - <p class="entry-title-style"> - <a href="https://www.spiria.com/">Spiria</a> - </p> - <p class="entry-location-style">Oakville, ON, Canada</p> - </div> - <div class="flex-box"> - <p class="entry-position-style">SOFTWARE ENGINEER</p> - <p class="entry-date-style">October, 2018 - October, 2020</p> - </div> - <ul class="description-style"> - <li> - Constructed RESTful API endpoints in multiple different frameworks such as Django, Ruby on Rails, and Flask and automated API documentation process using swagger. - </li> - <li> - Designed custom rake tasks for importing production data into newly updated data structure to meet client's needs. - </li> - <li> - Maintained or updated staging/productions servers. Debugged problems in production postgres database using ssh and postgres console on Heroku or AWS servers - </li> - <li> - Collaborated in creating automation python scripts for websites and application using selenium covering for QA eliminating 80% of QA's manual work - </li> - </ul> - - <div class="flex-box"> - <p class="entry-title-style"> - <a href="https://www.apexscore.ai/">Apex Score</a> - </p> - <p class="entry-location-style">Oakville, ON, Canada</p> - </div> - <div class="flex-box"> - <p class="entry-position-style">SOFTWARE ENGINEER</p> - <p class="entry-date-style">September, 2019 - October, 2020</p> - </div> - <ul class="description-style"> - <li> - Developed custom Shapley value regression model to calculate importance of independent variables of data sets using sklearn, pandas, and numpy. - </li> - <li> - Created custom image uploader to Amazon s3 bucket using boto3 library. - </li> - <li> - Built RESTful API application using Flask framework and automated extensive API documentation pages using flask-restplus, pytest, and swagger, covering 95% of the code base. - </li> - <li> - Created an interactive graph using D3.js in Vue.js with data from Flask backend API. - </li> - </ul> - - <div class="sub-header"> - <h2 class="section-style"> Education </h2> - <div class="line"></div> - </div> - <div class="flex-box"> - <p class="entry-title-style"> - University of British Columbia - </p> - <p class="entry-location-style">Kelowna, British Columbia</p> - </div> - <div class="flex-box"> - <p class="entry-position-style">BACHELOR OF SCIENCE IN PHYSICS</p> - <p class="entry-date-style">2014 - 2018</p> - </div> - <div id="footer"></div> - </main> - <div style="display: flex; align-items: center; justify-content: center; margin: 30px 0px;"> - <small>© 2026 June Park</small> -</div> - - <script href="index.js"></script> - </body> -</html>
--- a/mrjunejune/test/snapshots/resume_index.html.snapshot Sun Jan 04 06:39:16 2026 -0800 +++ b/mrjunejune/test/snapshots/resume_index.html.snapshot Sun Jan 04 11:38:44 2026 -0800 @@ -3,444 +3,5 @@ Content-Length: 0 Connection: close Body: +Location: /resume -"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> -</htmlLocation: / - - <p> - <span class="entry-title-style">Tools & Platforms:</span> - <span class="skill-type-style">Bazel, PostgresSQL, Mercurial, Git, Pands, Raylib, XCode</span> - </p> - <p> - <span class="entry-title-style"> Web Frameworks: </span> - <span class="skill-type-style"> Django, Rails, React, Flask</span> - </p> - <p> - <span class="entry-title-style"> DevOp:</span> - <span class="skill-type-style"> Plummi, Heroku, DigitalOcean, AWS, Google Cloud </span> - </p> - <p> - <span class="entry-title-style"> Language:</span> - <span class="skill-type-style"> English, Korean, Japanese </span> - </p> - </div> - - <!-- Experiences --> - <div class="sub-header"> - <h2 class="section-style"> Experience </h2> - <div class="line"></div> - </div> - <div class="flex-box"> - <p class="entry-title-style"> - <a href="https://www.meta.com/">Meta</a> - </p> - <p class="entry-location-style">San Francisco, CA, USA</p> - </div> - <div class="flex-box"> - <p class="entry-position-style">SOFTWARE ENGINEER</p> - <p class="entry-date-style">Oct, 2024 - Present</p> - </div> - <ul class="description-style"> - <li> - Took initiative on Channel Value Rule, targeting the 16% of ad traffic with both app and web destinations to improve value attribution and ROI. - </li> - <li> - Built full-stack features using React and Hack/GraphQL, contributing to scalable, production-ready systems. - </li> - <li> - Partnered with data science to design A/B tests and analyze revenue impact of ads destination. - </li> - <li> - Proposed and implemented alpha improvements to internal testing infrastructure, reducing test time by 50% and enhancing developer velocity. - </li> - </ul> - <div class="flex-box"> - <p class="entry-title-style"> - <a href="https://www.wmg.com/">Warner Music Group</a> - </p> - <p class="entry-location-style">Toronto, ON, Canada</p> - </div> - <div class="flex-box"> - <p class="entry-position-style">TECHNICAL LEAD ENGINEER</p> - <p class="entry-date-style">July, 2023 - Sept, 2024</p> - </div> - <ul class="description-style"> - <li> - Implements <a href="https://bazel.build/">bazel </a>structure for the company for TypeScript and JavaScript code base for hermiticity and stablishing standards for JavaScript and - </li> - <li> - TypeScript testing and code structures. - </li> - <li> - Led a team of five engineers in building GraphQL endpoints for client-facing applications using Apollo and AppSync, supporting over 2000 RPS and auto scaling depending on request values. - </li> - <li> - Improved application response times by up to 85% for graphQL response by updating database schema and SQL queries, eliminating N+1 queries and lack of indexes. - </li> - <li> - Developed CI/CD pipelines for backend structures. - </li> - <li> - Designed infrastructure for pub/sub, caching, and media processing logic. - </li> - </ul> - - <div class="flex-box"> - <p class="entry-title-style"> - <a href="https://www.google.com/">Google</a> - </p> - <p class="entry-location-style">Toronto, ON, Canada</p> - </div> - <div class="flex-box"> - <p class="entry-position-style">SOFTWARE ENGINEER</p> - <p class="entry-date-style">Feb, 2022 - July 2023</p> - </div> - <ul class="description-style"> - <li> - Implements and maintained new features relating to App Script across google workspace platform including Gmail, sheets, and Docs.</li> - <li> - Improved a response time and render time of App Script hover card components.</li> - <li> - Collaborated with a team of developers to ensure timely and accurate delivery of features.</li> - <li> - Conducted user testing and gathered feedback to iterate on features for optimal user experience.</li> - </ul> - - <div class="flex-box"> - <p class="entry-title-style"> - <a href="https://www.everlywell.com/">Everlywell</a> - </p> - <p class="entry-location-style">Toronto, ON, Canada</p> - </div> - <div class="flex-box"> - <p class="entry-position-style">SOFTWARE ENGINEER</p> - <p class="entry-date-style">December, 2020 - Jan, 2022</p> - </div> - <ul class="description-style"> - <li> - Maintained Amazon amplify apps to create and deploy React web applications for companies such as <a href="https://brooklynnets.everlywell.com/">NBA</a>, <a href="https://tinder.everlywell.com/">Tinder</a>, and other companies for COVID-19 at-home test kits.</li> - <li> - Implemented a script that helps accurately access and refund unused covid test kits; helping company save up to 200,000 USD.</li> - <li> - Created several Rails controllers for internal purposes; mocking end to end user experience for QA, mass refund features for CX department, and more, ultimately reducing support tickets amount by 50 percent.</li> - <li> - Implemented an audit table to help debug problems and logged which process was responsible for the change of the record using PaperTrail gems</li> - </ul> - - <div class="flex-box"> - <p class="entry-title-style"> - <a href="https://www.spiria.com/">Spiria</a> - </p> - <p class="entry-location-style">Oakville, ON, Canada</p> - </div> - <div class="flex-box"> - <p class="entry-position-style">SOFTWARE ENGINEER</p> - <p class="entry-date-style">October, 2018 - October, 2020</p> - </div> - <ul class="description-style"> - <li> - Constructed RESTful API endpoints in multiple different frameworks such as Django, Ruby on Rails, and Flask and automated API documentation process using swagger. - </li> - <li> - Designed custom rake tasks for importing production data into newly updated data structure to meet client's needs. - </li> - <li> - Maintained or updated staging/productions servers. Debugged problems in production postgres database using ssh and postgres console on Heroku or AWS servers - </li> - <li> - Collaborated in creating automation python scripts for websites and application using selenium covering for QA eliminating 80% of QA's manual work - </li> - </ul> - - <div class="flex-box"> - <p class="entry-title-style"> - <a href="https://www.apexscore.ai/">Apex Score</a> - </p> - <p class="entry-location-style">Oakville, ON, Canada</p> - </div> - <div class="flex-box"> - <p class="entry-position-style">SOFTWARE ENGINEER</p> - <p class="entry-date-style">September, 2019 - October, 2020</p> - </div> - <ul class="description-style"> - <li> - Developed custom Shapley value regression model to calculate importance of independent variables of data sets using sklearn, pandas, and numpy. - </li> - <li> - Created custom image uploader to Amazon s3 bucket using boto3 library. - </li> - <li> - Built RESTful API application using Flask framework and automated extensive API documentation pages using flask-restplus, pytest, and swagger, covering 95% of the code base. - </li> - <li> - Created an interactive graph using D3.js in Vue.js with data from Flask backend API. - </li> - </ul> - - <div class="sub-header"> - <h2 class="section-style"> Education </h2> - <div class="line"></div> - </div> - <div class="flex-box"> - <p class="entry-title-style"> - University of British Columbia - </p> - <p class="entry-location-style">Kelowna, British Columbia</p> - </div> - <div class="flex-box"> - <p class="entry-position-style">BACHELOR OF SCIENCE IN PHYSICS</p> - <p class="entry-date-style">2014 - 2018</p> - </div> - <div id="footer"></div> - </main> - <div style="display: flex; align-items: center; justify-content: center; margin: 30px 0px;"> - <small>© 2026 June Park</small> -</div> - - <script href="index.js"></script> - </body> -</htmlLocation: /resume -
--- a/mrjunejune/test/snapshots/tools_file_converter_index.html.snapshot Sun Jan 04 06:39:16 2026 -0800 +++ b/mrjunejune/test/snapshots/tools_file_converter_index.html.snapshot Sun Jan 04 11:38:44 2026 -0800 @@ -4,7 +4,7 @@ Connection: close Body: -/woff" crossorigin> +"font/woff" crossorigin> <link rel="preload" href="/public/fonts/atkinson-bold.woff" as="font" type="font/woff" crossorigin> <link rel="preload" href="/public/fonts/more-sugar.extras.otf" as="font" type="font/otf" crossorigin> @@ -15,135 +15,7 @@ <link rel="stylesheet" href="/base.css" /> - <style> - .container { - max-width: 800px; - margin: 2rem auto; - padding: 2rem; - } - - .converter-section { - border-radius: 8px; - border: 2px solid var(--lightgray); - padding: 2rem; - margin-bottom: 2rem; - } - - .converter-section h2 { - margin-top: 0; - color: var(--black); - } - - .file-input-wrapper { - margin: 1rem 0; - } - - .file-input-wrapper label { - display: block; - margin-bottom: 0.5rem; - font-weight: 600; - } - - input[type="file"] { - padding: 0.75rem; - border: 2px dashed #ccc; - border-radius: 4px; - background: white; - font-size: 16px; - } - - button { - background: var(--purple); - font-family: "More Thin", sans-serif; - color: var(--gray-gradient); - border: none; - padding: 0.75rem 1.5rem; - border-radius: 4px; - cursor: pointer; - font-size: 1rem; - margin-top: 1rem; - min-height: 44px; - } - - button:hover { - background: var(--orange); - } - - button:disabled { - background: var(--gray); - cursor: not-allowed; - } - - .result { - margin-top: 1rem; - padding: 1rem; - background: white; - border-radius: 4px; - display: none; - } - - .result.show { - display: block; - } - - .result.success { - border-left: 4px solid var(--blue); - } - - .result.error { - border-left: 4px solid var(--red); - } - - .download-link { - display: inline-block; - margin-top: 0.5rem; - color: var(--awesome); - text-decoration: none; - } - - .download-link:hover { - text-decoration: underline; - } - - .loading { - display: none; - margin-top: 1rem; - } - - .loading.show { - display: block; - } - - /* Mobile responsive */ - @media (max-width: 720px) { - .container { - padding: 1rem; - margin: 1rem auto; - } - - .converter-section { - padding: 1.5rem 1rem; - } - - .converter-section h2 { - font-size: 1.5rem; - } - - .file-input-wrapper label { - font-size: 1rem; - } - - button { - font-size: 1.1rem; - padding: 1rem 1.5rem; - } - - .result { - padding: 1rem; - font-size: 1rem; - } - } - </style> + <link rel="stylesheet" href="markdown_to_html/index.css" /> </head> <body> <style> @@ -284,55 +156,292 @@ <script src="/index.js"></script> - + <div class="header"> + <h1>Markdown to HTML Converter</h1> + </div> + <div class="container"> - <h1>File Format Converter</h1> - <p>Convert your images and videos to different formats using FFmpeg</p> + <div class="panel"> + <div class="label">Markdown Input</div> + <textarea id="input" placeholder="Type your markdown here..."># Welcome to Markdown Converter + +## Features + +This converter supports: - <div class="converter-section"> - <h2>Image to WebP Converter</h2> - <p>Upload an image file (PNG, JPG, GIF, etc.) to convert it to WebP format</p> +- **Bold text** and *italic text* +- [Links](https://example.com) +- `inline code` +- ~~strikethrough~~ + +### Lists + +1. Ordered lists +2. Like this one +3. With numbers - <div class="file-input-wrapper"> - <label for="imageInput">Choose an image file:</label> - <input type="file" id="imageInput" accept="image/*"> - </div> +- Unordered lists +- Use dashes +- Or asterisks + +### Code Blocks + +``` +function example() { + return "Hello World"; +} +``` + +### Blockquotes - <button id="convertImageBtn" onclick="convertImageToWebP()">Convert to WebP</button> +> This is a blockquote +> It can span multiple lines - <div class="loading" id="imageLoading">Converting... Please wait.</div> +--- + +### Images - <div class="result" id="imageResult"> - <p id="imageMessage"></p> - <a id="imageDownload" class="download-link" style="display: none;">Download WebP Image</a> + + +**Try editing this text!**</textarea> + </div> + + <div class="panel"> + <div class="title"> + <div class="label">HTML Output</div> + <button id="copy"> Copy </button> </div> + <div id="output"></div> </div> - - <div class="converter-section"> - <h2>Video to MP4 Converter</h2> - <p>Upload a video file (AVI, MOV, MKV, etc.) to convert it to MP4 format</p> - - <div class="file-input-wrapper"> - <label for="videoInput">Choose a video file:</label> - <input type="file" id="videoInput" accept="video/*"> - </div> - - <button id="convertVideoBtn" onclick="convertVideoToMP4()">Convert to MP4</button> - - <div class="loading" id="videoLoading">Converting... Please wait.</div> - - <div class="result" id="videoResult"> - <p id="videoMessage"></p> - <a id="videoDownload" class="download-link" style="display: none;">Download MP4 Video</a> - </div> - </div> - <div style="display: flex; align-items: center; justify-content: center; margin: 30px 0px;"> + </div> + <div style="display: flex; align-items: center; justify-content: center; margin: 30px 0px;"> <small>© 2026 June Park</small> </div> - </div> - <script src="/tools/file_converter/index.js"></script> + <script src="/markdown_to_html.js"></script> + <script> + function convert() { + output.innerHTML = ''; + const markdown = input.value; + renderMarkdown(output, markdown); + } + input.addEventListener('input', convert); + + convert(); + + copy.addEventListener('click', () => { + const htmlBlob = new Blob([output.innerHTML], { type: 'text/html'}); + const textBlob = new Blob([output.innerText], { type: 'text/plain'}); + const data = [new ClipboardItem({ + 'text/html': htmlBlob, + 'text/plain': textBlob + })]; + navigator.clipboard.write(data).then(() => { + copy.textContent = "Copied!"; + setTimeout(() => { + copy.textContent = "Copy"; + copy.classList.remove('success'); + }, 1000); + }).catch(err => { + console.error('Failed to copy: ', err); + }); + }); + </script> </body> +</htmlLocation: /tools + + + <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> </htmlLocation: /tools/markdown_to_html Location: /tools/file_converter
--- a/mrjunejune/test/snapshots/tools_index.html.snapshot Sun Jan 04 06:39:16 2026 -0800 +++ b/mrjunejune/test/snapshots/tools_index.html.snapshot Sun Jan 04 11:38:44 2026 -0800 @@ -248,9 +248,9 @@ }); </script> </body> -</htmlLocation: / +</htmlLocation: /tools - <p> + <span class="entry-title-style">Tools & Platforms:</span> <span class="skill-type-style">Bazel, PostgresSQL, Mercurial, Git, Pands, Raylib, XCode</span> </p> @@ -442,6 +442,4 @@ <script href="index.js"></script> </body> -</htmlLocation: /resume -Location: /tools - +</html>
--- a/mrjunejune/test/snapshots/tools_markdown_to_html_index.html.snapshot Sun Jan 04 06:39:16 2026 -0800 +++ b/mrjunejune/test/snapshots/tools_markdown_to_html_index.html.snapshot Sun Jan 04 11:38:44 2026 -0800 @@ -4,7 +4,7 @@ Connection: close Body: -/woff" crossorigin> +"font/woff" crossorigin> <link rel="preload" href="/public/fonts/atkinson-bold.woff" as="font" type="font/woff" crossorigin> <link rel="preload" href="/public/fonts/more-sugar.extras.otf" as="font" type="font/otf" crossorigin> @@ -15,135 +15,7 @@ <link rel="stylesheet" href="/base.css" /> - <style> - .container { - max-width: 800px; - margin: 2rem auto; - padding: 2rem; - } - - .converter-section { - border-radius: 8px; - border: 2px solid var(--lightgray); - padding: 2rem; - margin-bottom: 2rem; - } - - .converter-section h2 { - margin-top: 0; - color: var(--black); - } - - .file-input-wrapper { - margin: 1rem 0; - } - - .file-input-wrapper label { - display: block; - margin-bottom: 0.5rem; - font-weight: 600; - } - - input[type="file"] { - padding: 0.75rem; - border: 2px dashed #ccc; - border-radius: 4px; - background: white; - font-size: 16px; - } - - button { - background: var(--purple); - font-family: "More Thin", sans-serif; - color: var(--gray-gradient); - border: none; - padding: 0.75rem 1.5rem; - border-radius: 4px; - cursor: pointer; - font-size: 1rem; - margin-top: 1rem; - min-height: 44px; - } - - button:hover { - background: var(--orange); - } - - button:disabled { - background: var(--gray); - cursor: not-allowed; - } - - .result { - margin-top: 1rem; - padding: 1rem; - background: white; - border-radius: 4px; - display: none; - } - - .result.show { - display: block; - } - - .result.success { - border-left: 4px solid var(--blue); - } - - .result.error { - border-left: 4px solid var(--red); - } - - .download-link { - display: inline-block; - margin-top: 0.5rem; - color: var(--awesome); - text-decoration: none; - } - - .download-link:hover { - text-decoration: underline; - } - - .loading { - display: none; - margin-top: 1rem; - } - - .loading.show { - display: block; - } - - /* Mobile responsive */ - @media (max-width: 720px) { - .container { - padding: 1rem; - margin: 1rem auto; - } - - .converter-section { - padding: 1.5rem 1rem; - } - - .converter-section h2 { - font-size: 1.5rem; - } - - .file-input-wrapper label { - font-size: 1rem; - } - - button { - font-size: 1.1rem; - padding: 1rem 1.5rem; - } - - .result { - padding: 1rem; - font-size: 1rem; - } - } - </style> + <link rel="stylesheet" href="markdown_to_html/index.css" /> </head> <body> <style> @@ -284,54 +156,291 @@ <script src="/index.js"></script> - + <div class="header"> + <h1>Markdown to HTML Converter</h1> + </div> + <div class="container"> - <h1>File Format Converter</h1> - <p>Convert your images and videos to different formats using FFmpeg</p> + <div class="panel"> + <div class="label">Markdown Input</div> + <textarea id="input" placeholder="Type your markdown here..."># Welcome to Markdown Converter + +## Features + +This converter supports: - <div class="converter-section"> - <h2>Image to WebP Converter</h2> - <p>Upload an image file (PNG, JPG, GIF, etc.) to convert it to WebP format</p> +- **Bold text** and *italic text* +- [Links](https://example.com) +- `inline code` +- ~~strikethrough~~ + +### Lists + +1. Ordered lists +2. Like this one +3. With numbers - <div class="file-input-wrapper"> - <label for="imageInput">Choose an image file:</label> - <input type="file" id="imageInput" accept="image/*"> - </div> +- Unordered lists +- Use dashes +- Or asterisks + +### Code Blocks + +``` +function example() { + return "Hello World"; +} +``` + +### Blockquotes - <button id="convertImageBtn" onclick="convertImageToWebP()">Convert to WebP</button> +> This is a blockquote +> It can span multiple lines - <div class="loading" id="imageLoading">Converting... Please wait.</div> +--- + +### Images - <div class="result" id="imageResult"> - <p id="imageMessage"></p> - <a id="imageDownload" class="download-link" style="display: none;">Download WebP Image</a> + + +**Try editing this text!**</textarea> + </div> + + <div class="panel"> + <div class="title"> + <div class="label">HTML Output</div> + <button id="copy"> Copy </button> </div> + <div id="output"></div> </div> - - <div class="converter-section"> - <h2>Video to MP4 Converter</h2> - <p>Upload a video file (AVI, MOV, MKV, etc.) to convert it to MP4 format</p> - - <div class="file-input-wrapper"> - <label for="videoInput">Choose a video file:</label> - <input type="file" id="videoInput" accept="video/*"> - </div> - - <button id="convertVideoBtn" onclick="convertVideoToMP4()">Convert to MP4</button> - - <div class="loading" id="videoLoading">Converting... Please wait.</div> - - <div class="result" id="videoResult"> - <p id="videoMessage"></p> - <a id="videoDownload" class="download-link" style="display: none;">Download MP4 Video</a> - </div> - </div> - <div style="display: flex; align-items: center; justify-content: center; margin: 30px 0px;"> + </div> + <div style="display: flex; align-items: center; justify-content: center; margin: 30px 0px;"> <small>© 2026 June Park</small> </div> - </div> - <script src="/tools/file_converter/index.js"></script> + <script src="/markdown_to_html.js"></script> + <script> + function convert() { + output.innerHTML = ''; + const markdown = input.value; + renderMarkdown(output, markdown); + } + input.addEventListener('input', convert); + + convert(); + + copy.addEventListener('click', () => { + const htmlBlob = new Blob([output.innerHTML], { type: 'text/html'}); + const textBlob = new Blob([output.innerText], { type: 'text/plain'}); + const data = [new ClipboardItem({ + 'text/html': htmlBlob, + 'text/plain': textBlob + })]; + navigator.clipboard.write(data).then(() => { + copy.textContent = "Copied!"; + setTimeout(() => { + copy.textContent = "Copy"; + copy.classList.remove('success'); + }, 1000); + }).catch(err => { + console.error('Failed to copy: ', err); + }); + }); + </script> </body> +</htmlLocation: /tools + + + <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> </htmlLocation: /tools/markdown_to_html
--- a/postdog/BUILD Sun Jan 04 06:39:16 2026 -0800 +++ b/postdog/BUILD Sun Jan 04 11:38:44 2026 -0800 @@ -3,10 +3,12 @@ raylib_binary( name = "postdog", - srcs = ["main.c"], + srcs = [ + "main.c", + ], deps = [ "//third_party/raylib:raylib", - "//dowa:dowa" + "//dowa:dowa", ], linkopts_macos = [ "-framework CoreVideo",
--- a/postdog/main.c Sun Jan 04 06:39:16 2026 -0800 +++ b/postdog/main.c Sun Jan 04 11:38:44 2026 -0800 @@ -14,37 +14,26 @@ #define SCREEN_WIDTH 1280 #define SCREEN_HEIGHT 780 #define TEXT_SIZE 10 -#define LINE_HEIGHT 15 -#define GENERIC_PADDING 15 - -#define SIDEBAR_WIDTH 200 -#define SIDEBAR_PADDING_GENERAL 5 -#define SIDEBAR_AREA_PADDING_X 10 -#define SIDEBAR_AREA_PADDING_Y 10 -#define SIDEBAR_REFERSH_BUTTON_WIDTH 90 -#define SIDEBAR_REFERSH_BUTTON_HEIGHT 20 -#define SIDEBAR_HISTORY_ITEM_HEIGHT 45 - -#define URL_INPUT_HEIGHT 40 - -#define TAB_LEN 3 -#define METHOD_BUTTON_WIDTH 100 -#define METHOD_BUTTON_HEIGHT 40 -#define SEND_BUTTON_WIDTH 100 -#define SEND_BUTTON_HEIGHT 40 #define JSON_INPUT_BUFFER_LEN 8192 #define HEADER_INPUT_BUFFER_LEN 4096 #define PARAM_INPUT_BUFFER_LEN 4096 #define MAX_HISTORY_ITEMS 100 -// Structure to hold response data +#define HEADER_BUFFER_LENGTH 1024 * 4 +#define DEFAULT_TEXT_BUFFER_LENGTH 1024 * 4 +#define URL_TEXT_BUFFER 1024 * 10 +#define BODY_BUFFER_LENGTH 1024 * 1025 * 5 +#define RESULT_BUFFER_LENGTH 1024 * 1025 * 5 +#define AREANA_BUFFER_LENGTH 1024 * 1025 * 15 + +typedef Dowa_KV(char*, char*) INPUT_HASHMAP; + typedef struct { char *data; size_t size; } ResponseBuffer; -// Structure to hold history item typedef struct { char filename[256]; char displayName[128]; @@ -52,11 +41,16 @@ time_t timestamp; } HistoryItem; -typedef enum { - ActiveTab_JSON = 0, - ActiveTab_Headers, - ActiveTab_Params, -} ActiveTab; +char *PostDog_Enum_To_String(int active_enum) +{ + switch(active_enum) + { + case 0: return "GET"; + case 1: return "POST"; + case 2: return "PUT"; + case 3: return "DELETE"; + } +} static size_t Postdog_Curl_Callback(void *contents, size_t size, size_t nmemb, void *userp) { @@ -78,13 +72,19 @@ return real_size; } -int PostDog_Make_HttpRequest(const char *url, const char *method, const char *headers, - const char *body, char *response, size_t responseSize) +int PostDog_Make_HttpRequest( + const char *url, + const char *method, + const char *headers, + const char *body, + char *response, size_t responseSize) { CURL *curl; CURLcode res; ResponseBuffer buffer = { .data = malloc(1), .size = 0 }; + response[0] = '\n'; + if (buffer.data == NULL) { snprintf(response, responseSize, "Error: Failed to allocate memory"); @@ -180,8 +180,77 @@ typedef struct { Rectangle rectangle; char *label; + bool active; } TabItem; +typedef enum { + TAB_HEADER = 0, + TAB_BODY, + TAB_GET_PARAMS, + TAB_BAR, +} PostDog_Tab_Enum; + +void PostDog_Update_URL(char **p_url_input_text, char* get_params) +{ + char *url_input_text = *p_url_input_text; + + // Reset + char *question_mark = strchr(url_input_text, '?'); + if (question_mark) + *question_mark = '\0'; + + int get_params_length = (int)strlen(get_params) ; + if (get_params_length == 0) + return; + + char *separator = "?"; + + Dowa_Arena *arena = Dowa_Arena_Create(1024*1024); + char **lines = Dowa_String_Split(get_params, "\n", get_params_length, 1, arena); + + size_t url_len = strlen(url_input_text); + size_t url_capacity = URL_TEXT_BUFFER; + + for (int i = 0; i < Dowa_Array_Length(lines); i++) + { + char *line = lines[i]; + char **key_value = Dowa_String_Split(line, " ", (int)strlen(line), 1, arena); + + if (Dowa_Array_Length(key_value) < 2) + continue; // Skip this line, not break entire loop + + // Check buffer capacity before each append + size_t needed = url_len + strlen(separator) + strlen(key_value[0]) + 1 + 10; // +10 for "=" and safety + if (needed >= url_capacity) break; + + strcat(url_input_text, separator); + strcat(url_input_text, key_value[0]); + strcat(url_input_text, "="); + url_len = strlen(url_input_text); + + for (int i = 1; i < Dowa_Array_Length(key_value); i++) + { + if (!key_value[i] || key_value[i][0] == '\0') + break; + + size_t value_len = strlen(key_value[i]); + needed = url_len + value_len + 4; // +4 for "%20" if needed + if (needed >= url_capacity) break; + + printf("\n\n------\n\n"); + printf("key_value[%i]: %s, p = %p\n", i, key_value[i], key_value[i]); + printf("\n\n------\n\n"); + + if (i > 1) strcat(url_input_text, "%20"); + strcat(url_input_text, key_value[i]); + url_len = strlen(url_input_text); + } + separator = "&"; + } + + Dowa_Arena_Free(arena); +} + int main() { // -- initizlied --// @@ -189,7 +258,7 @@ SetWindowState(FLAG_WINDOW_RESIZABLE); SetTargetFPS(60); - Dowa_Arena *arena = Dowa_Arena_Create(1024 * 1025 * 10); + Dowa_Arena *arena = Dowa_Arena_Create(AREANA_BUFFER_LENGTH); // -- Starting pos ---// float side_bar_x = 10; @@ -200,36 +269,44 @@ Rectangle url_area = { 0 }; Rectangle textBounds = { 0 }; Rectangle url_input_bounds = { 0 }; + bool url_input_bool = false; Rectangle url_text_bounds = { 0 }; Rectangle url_enter_button = { 0 }; + Rectangle method_dropdown = { 0 }; - char *input_text = (char *)Dowa_Arena_Allocate(arena, 1024 * 4); + char *url_input_text = (char *)Dowa_Arena_Allocate(arena, URL_TEXT_BUFFER); + snprintf(url_input_text, URL_TEXT_BUFFER, "https://httpbin.org/get"); + + INPUT_HASHMAP *url_body_map = NULL; + Dowa_HashMap_Push_Arena(url_body_map, "Header", (char *)Dowa_Arena_Allocate(arena, HEADER_BUFFER_LENGTH), arena); + Dowa_HashMap_Push_Arena(url_body_map, "Body", (char *)Dowa_Arena_Allocate(arena, BODY_BUFFER_LENGTH), arena); + Dowa_HashMap_Push_Arena(url_body_map, "Get Param", (char *)Dowa_Arena_Allocate(arena, DEFAULT_TEXT_BUFFER_LENGTH), arena); + Dowa_HashMap_Push_Arena(url_body_map, "Bar", (char *)Dowa_Arena_Allocate(arena, DEFAULT_TEXT_BUFFER_LENGTH), arena); + + snprintf(url_body_map[0].value, HEADER_BUFFER_LENGTH, "Content-Type: application/json"); + snprintf(url_body_map[1].value, HEADER_BUFFER_LENGTH, ""); + + char *url_result_text = (char *)Dowa_Arena_Allocate(arena, RESULT_BUFFER_LENGTH); + + int active_method_dropdown = 0; + bool method_edit = false; + int sendRequest; + // -- input --// Rectangle input_area = { 0 }; Rectangle input_tab = { 0 }; - TabItem input_tab_items[4] = { - { - .rectangle={0}, .label="body" - }, - { - .rectangle={0}, .label="header" - }, - { - .rectangle={0}, .label="foo" - }, - { - .rectangle={0}, .label="bar" - }, - }; + Rectangle input_tab_item = { 0 }; Rectangle input_body = { 0 }; + bool input_body_bool = false; + // -- result --// Rectangle result_area = { 0 }; Rectangle result_body = { 0 }; // General styling. int padding = 10; // TODO make it % based? - + int active_input_tab = 0; while (!WindowShouldClose()) { @@ -260,6 +337,11 @@ url_enter_button.width = (url_area.width - padding) * 0.1; url_enter_button.height = TEXT_SIZE * 2; + method_dropdown.x = url_enter_button.x + url_enter_button.width + padding; + method_dropdown.y = (url_area.height) / 2; + method_dropdown.width = (url_area.width - padding) * 0.1; + method_dropdown.height = TEXT_SIZE * 2; + // -- Input Area --// input_area.x = (side_bar_x + history_sidebar.width); input_area.y = (side_bar_y + url_area.height); @@ -270,12 +352,8 @@ input_tab.y = (side_bar_y + url_area.height) + padding; input_tab.width = url_area.width * 0.49 - padding; input_tab.height = input_area.height * 0.1; - for (int pos = 0; pos < 4; pos++) - { - input_tab_items[pos].rectangle = input_tab; - input_tab_items[pos].rectangle.x = input_tab.x + (pos * input_tab.width / 4); - input_tab_items[pos].rectangle.width = input_tab.width / 4; - } + input_tab_item = input_tab; + input_tab_item.width = input_tab.width / 4; input_body.x = input_tab.x; input_body.y = input_tab.y + input_tab.height; @@ -302,21 +380,34 @@ // URL area Rect GuiDrawText("URL: ", url_text_bounds, TEXT_ALIGN_CENTER, RED); DrawRectangleRec(url_area, Fade(RED, 0.1f)); - GuiTextBox(url_input_bounds, input_text, 1024 * 4, true); + if (GuiTextBox(url_input_bounds, url_input_text, DEFAULT_TEXT_BUFFER_LENGTH, url_input_bool)) + url_input_bool = !url_input_bool; sendRequest = GuiButton(url_enter_button, "ENTER"); + if (sendRequest) + PostDog_Make_HttpRequest( + url_input_text, + PostDog_Enum_To_String(active_method_dropdown), + url_body_map[0].value, + url_body_map[1].value, + url_result_text, + RESULT_BUFFER_LENGTH + ); + if (GuiDropdownBox(method_dropdown, "GET;POST;PUT;DELETE", &active_method_dropdown, method_edit)) + method_edit = !method_edit; // Input Tabs Rect DrawRectangleRec(input_area, Fade(BLUE, 0.1f)); DrawRectangleRec(input_tab, Fade(DARKBLUE, 0.1f)); - for (int pos = 0; pos < 4; pos++) - { - JUNE_GuiButton(input_tab_items[pos].rectangle, input_tab_items[pos].label, 0, Fade(DARKBLUE, 0.1f)); - } - JUNE_GuiTextBox(input_body, input_text, 1024 * 4, true); + GuiSetStyle(TOGGLE, GROUP_PADDING, 0); + if (JUNE_GuiTextBox(input_body, url_body_map[active_input_tab].value, DEFAULT_TEXT_BUFFER_LENGTH, input_body_bool)) + input_body_bool = !input_body_bool; + GuiToggleGroup(input_tab_item, "Header;Body;Get Param;Bar", &active_input_tab); + PostDog_Update_URL(&url_input_text, url_body_map[TAB_GET_PARAMS].value); // Result Rect DrawRectangleRec(result_area, Fade(GREEN, 0.1f)); DrawRectangleRec(result_body, Fade(DARKGREEN, 0.1f)); + GuiTextBoxMulti(result_body, url_result_text, RESULT_BUFFER_LENGTH, false); EndDrawing(); } CloseWindow();
--- a/third_party/raylib/include/raygui.h Sun Jan 04 06:39:16 2026 -0800 +++ b/third_party/raylib/include/raygui.h Sun Jan 04 11:38:44 2026 -0800 @@ -738,6 +738,7 @@ RAYGUIAPI int GuiButton(Rectangle bounds, const char *text); // Button control, returns true when clicked RAYGUIAPI int GuiLabelButton(Rectangle bounds, const char *text); // Label button control, returns true when clicked RAYGUIAPI int GuiToggle(Rectangle bounds, const char *text, bool *active); // Toggle Button control +RAYGUIAPI void JUNE_DrawRectangleLinesNoBottom(Rectangle rect, float thickness, Color color); RAYGUIAPI int GuiToggleGroup(Rectangle bounds, const char *text, int *active); // Toggle Group control RAYGUIAPI int GuiToggleSlider(Rectangle bounds, const char *text, int *active); // Toggle Slider control RAYGUIAPI int GuiCheckBox(Rectangle bounds, const char *text, bool *checked); // Check Box control, returns true when active @@ -2093,7 +2094,31 @@ //-------------------------------------------------------------------- if (state == STATE_NORMAL) { - GuiDrawRectangle(bounds, GuiGetStyle(TOGGLE, BORDER_WIDTH), GetColor(GuiGetStyle(TOGGLE, ((*active)? BORDER_COLOR_PRESSED : (BORDER + state*3)))), GetColor(GuiGetStyle(TOGGLE, ((*active)? BASE_COLOR_PRESSED : (BASE + state*3))))); + if (*active) + { + Rectangle tmp = bounds; + tmp.height += GuiGetStyle(TOGGLE, BORDER_WIDTH); + GuiDrawRectangle( + tmp, + 0, + GetColor(GuiGetStyle(TOGGLE, ((*active)? BORDER_COLOR_PRESSED : (BORDER + state*3)))), + GetColor(GuiGetStyle(TOGGLE, ((*active)? BASE_COLOR_PRESSED : (BASE + state*3)))) + ); + JUNE_DrawRectangleLinesNoBottom( + bounds, + GuiGetStyle(TOGGLE, BORDER_WIDTH), + GetColor(GuiGetStyle(TOGGLE, BORDER_COLOR_PRESSED)) + ); + } + else + { + GuiDrawRectangle( + bounds, + GuiGetStyle(TOGGLE, BORDER_WIDTH), + GetColor(GuiGetStyle(TOGGLE, ((*active)? BORDER_COLOR_PRESSED : (BORDER + state*3)))), + GetColor(GuiGetStyle(TOGGLE, ((*active)? BASE_COLOR_PRESSED : (BASE + state*3)))) + ); + } GuiDrawText(text, GetTextBounds(TOGGLE, bounds), GuiGetStyle(TOGGLE, TEXT_ALIGNMENT), GetColor(GuiGetStyle(TOGGLE, ((*active)? TEXT_COLOR_PRESSED : (TEXT + state*3))))); } else @@ -2102,7 +2127,9 @@ GuiDrawText(text, GetTextBounds(TOGGLE, bounds), GuiGetStyle(TOGGLE, TEXT_ALIGNMENT), GetColor(GuiGetStyle(TOGGLE, TEXT + state*3))); } - if (state == STATE_FOCUSED) GuiTooltip(bounds); + if (state == STATE_FOCUSED) { + GuiTooltip(bounds); + } //-------------------------------------------------------------------- return result; @@ -2923,7 +2950,6 @@ return result; // Mouse button pressed: result = 1 } -/* // Text Box control with multiple lines and word-wrap // NOTE: This text-box is readonly, no editing supported by default bool GuiTextBoxMulti(Rectangle bounds, char *text, int textSize, bool editMode) @@ -2943,7 +2969,6 @@ return pressed; } -*/ // Spinner control, returns selected value int GuiSpinner(Rectangle bounds, const char *text, int *value, int minValue, int maxValue, bool editMode) @@ -6031,21 +6056,55 @@ int result = 0; GuiState state = guiState; - bool multiline = true; + bool multiline = true; // TODO: Consider multiline text input int wrapMode = GuiGetStyle(DEFAULT, TEXT_WRAP_MODE); Rectangle textBounds = GetTextBounds(TEXTBOX, bounds); int textLength = (text != NULL)? (int)strlen(text) : 0; // Get current text length int thisCursorIndex = textBoxCursorIndex; if (thisCursorIndex > textLength) thisCursorIndex = textLength; - int textWidth = GuiGetTextWidth(text) - GuiGetTextWidth(text + thisCursorIndex); + + // Calculate cursor position for multiline + int cursorLine = 0; // Current line number (0-based) + int lineStart = 0; // Start index of current line + + if (multiline) + { + for (int i = 0; i < thisCursorIndex; i++) + { + if (text[i] == '\n') + { + cursorLine++; + lineStart = i + 1; + } + } + } + + // Calculate horizontal position within current line + char lineText[1024] = { 0 }; + int lineTextLen = 0; + if (multiline) + { + // Extract current line text up to cursor + int i = lineStart; + while (i < thisCursorIndex && text[i] != '\n' && lineTextLen < 1023) + { + lineText[lineTextLen++] = text[i++]; + } + lineText[lineTextLen] = '\0'; + } + + int textWidth = multiline ? GuiGetTextWidth(lineText) : (GuiGetTextWidth(text) - GuiGetTextWidth(text + thisCursorIndex)); int textIndexOffset = 0; // Text index offset to start drawing in the box + // Line height for multiline (matches GuiDrawText line spacing) + int lineHeight = GuiGetStyle(DEFAULT, TEXT_SIZE); + // Cursor rectangle - // NOTE: Position X value should be updated + // NOTE: Position X and Y values updated for multiline support Rectangle cursor = { textBounds.x + textWidth + GuiGetStyle(DEFAULT, TEXT_SPACING), - textBounds.y + GuiGetStyle(DEFAULT, TEXT_SIZE), + multiline ? (textBounds.y + cursorLine * lineHeight) : (textBounds.y + textBounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)), 2, (float)GuiGetStyle(DEFAULT, TEXT_SIZE) }; @@ -6343,6 +6402,66 @@ textBoxCursorIndex += nextCodepointSize; } + // Vertical cursor movement for multiline + if (multiline && (IsKeyPressed(KEY_UP) || (IsKeyDown(KEY_UP) && autoCursorShouldTrigger))) + { + // Find start of current line + int currentLineStart = textBoxCursorIndex; + while (currentLineStart > 0 && text[currentLineStart - 1] != '\n') currentLineStart--; + + // Calculate horizontal position in current line + int horizontalPos = textBoxCursorIndex - currentLineStart; + + // Find start of previous line + if (currentLineStart > 0) + { + int prevLineEnd = currentLineStart - 1; // Skip the newline + int prevLineStart = prevLineEnd; + while (prevLineStart > 0 && text[prevLineStart - 1] != '\n') prevLineStart--; + + // Move to same horizontal position on previous line (or end of line if shorter) + int prevLineLength = prevLineEnd - prevLineStart; + int targetPos = (horizontalPos < prevLineLength) ? horizontalPos : prevLineLength; + textBoxCursorIndex = prevLineStart + targetPos; + } + else + { + // Already on first line, move to start + textBoxCursorIndex = 0; + } + } + else if (multiline && (IsKeyPressed(KEY_DOWN) || (IsKeyDown(KEY_DOWN) && autoCursorShouldTrigger))) + { + // Find start of current line + int currentLineStart = textBoxCursorIndex; + while (currentLineStart > 0 && text[currentLineStart - 1] != '\n') currentLineStart--; + + // Calculate horizontal position in current line + int horizontalPos = textBoxCursorIndex - currentLineStart; + + // Find end of current line + int currentLineEnd = textBoxCursorIndex; + while (currentLineEnd < textLength && text[currentLineEnd] != '\n') currentLineEnd++; + + // Find next line + if (currentLineEnd < textLength) + { + int nextLineStart = currentLineEnd + 1; // Skip the newline + int nextLineEnd = nextLineStart; + while (nextLineEnd < textLength && text[nextLineEnd] != '\n') nextLineEnd++; + + // Move to same horizontal position on next line (or end of line if shorter) + int nextLineLength = nextLineEnd - nextLineStart; + int targetPos = (horizontalPos < nextLineLength) ? horizontalPos : nextLineLength; + textBoxCursorIndex = nextLineStart + targetPos; + } + else + { + // Already on last line, move to end + textBoxCursorIndex = textLength; + } + } + // Move cursor position with mouse if (CheckCollisionPointRec(mousePosition, textBounds)) // Mouse hover text { @@ -6364,6 +6483,24 @@ { mouseCursor.x = textBounds.x + widthToMouseX; mouseCursorIndex = i; + printf("before: %i\n", mouseCursorIndex); + break; + } + + if (mousePosition.y >= textBounds.y && mousePosition.y <= textBounds.height) + { + mouseCursor.y = mousePosition.y; + mouseCursorIndex = i; + int number_of_n = (int)(mousePosition.y / lineHeight); + for (int i = 0; i < textSize; i++) + { + if (text[i] == '\n') + { + number_of_n--; + if (number_of_n == 0) + mouseCursorIndex += i; + } + } break; } @@ -6387,11 +6524,38 @@ } else mouseCursor.x = -1; - // Recalculate cursor position.y depending on textBoxCursorIndex - cursor.x = bounds.x + GuiGetStyle(TEXTBOX, TEXT_PADDING) + GuiGetTextWidth(text + textIndexOffset) - GuiGetTextWidth(text + textBoxCursorIndex) + GuiGetStyle(DEFAULT, TEXT_SPACING); - if (multiline) { - const char **lines = GetTextLines(text, &cursor.y); - printf("Cursor: %f", cursor.y); + // Recalculate cursor position for multiline + if (multiline) + { + // Recalculate cursor line and position + int newCursorLine = 0; + int newLineStart = 0; + + for (int i = 0; i < textBoxCursorIndex; i++) + { + if (text[i] == '\n') + { + newCursorLine++; + newLineStart = i + 1; + } + } + + // Extract current line text up to cursor + char currentLineText[1024] = { 0 }; + int currentLineLen = 0; + int i = newLineStart; + while (i < textBoxCursorIndex && text[i] != '\n' && currentLineLen < 1023) + { + currentLineText[currentLineLen++] = text[i++]; + } + currentLineText[currentLineLen] = '\0'; + + cursor.x = bounds.x + GuiGetStyle(TEXTBOX, TEXT_PADDING) + GuiGetTextWidth(currentLineText) + GuiGetStyle(DEFAULT, TEXT_SPACING); + cursor.y = textBounds.y + (newCursorLine * lineHeight * 1.5); + } + else + { + cursor.x = bounds.x + GuiGetStyle(TEXTBOX, TEXT_PADDING) + GuiGetTextWidth(text + textIndexOffset) - GuiGetTextWidth(text + textBoxCursorIndex) + GuiGetStyle(DEFAULT, TEXT_SPACING); } // Finish text editing on ENTER or mouse click outside bounds @@ -6432,18 +6596,15 @@ } else GuiDrawRectangle(bounds, GuiGetStyle(TEXTBOX, BORDER_WIDTH), GetColor(GuiGetStyle(TEXTBOX, BORDER + (state*3))), BLANK); - - + // Draw text considering index offset if required + // NOTE: Text index offset depends on cursor position + // Set vertical alignment to top for multiline int prevVerticalAlignment = GuiGetStyle(DEFAULT, TEXT_ALIGNMENT_VERTICAL); if (multiline) GuiSetStyle(DEFAULT, TEXT_ALIGNMENT_VERTICAL, TEXT_ALIGN_TOP); - // Draw text considering index offset if required - // NOTE: Text index offset depends on cursor position - GuiDrawText( - text + textIndexOffset, - textBounds, GuiGetStyle(TEXTBOX, TEXT_ALIGNMENT), GetColor(GuiGetStyle(TEXTBOX, TEXT + (state*3)))); - - // RESET + GuiDrawText(text + textIndexOffset, textBounds, GuiGetStyle(TEXTBOX, TEXT_ALIGNMENT), GetColor(GuiGetStyle(TEXTBOX, TEXT + (state*3)))); + + // Restore previous vertical alignment if (multiline) GuiSetStyle(DEFAULT, TEXT_ALIGNMENT_VERTICAL, prevVerticalAlignment); // Draw cursor @@ -6453,7 +6614,7 @@ GuiDrawRectangle(cursor, 0, BLANK, GetColor(GuiGetStyle(TEXTBOX, BORDER_COLOR_PRESSED))); // Draw mouse position cursor (if required) - if (mouseCursor.x >= 0) GuiDrawRectangle(mouseCursor, 0, BLANK, GetColor(GuiGetStyle(TEXTBOX, BORDER_COLOR_PRESSED))); + // if (mouseCursor.x >= 0) GuiDrawRectangle(mouseCursor, 0, BLANK, GetColor(GuiGetStyle(TEXTBOX, BORDER_COLOR_PRESSED))); } else if (state == STATE_FOCUSED) GuiTooltip(bounds); //-------------------------------------------------------------------- @@ -6461,5 +6622,15 @@ return result; // Mouse button pressed: result = 1 } +void JUNE_DrawRectangleLinesNoBottom(Rectangle rect, float thickness, Color color) { + Vector2 topLeft = { rect.x, rect.y }; + Vector2 topRight = { rect.x + rect.width, rect.y }; + Vector2 bottomLeft = { rect.x, rect.y + rect.height }; + Vector2 bottomRight = { rect.x + rect.width, rect.y + rect.height }; + + DrawLineEx(topLeft, topRight, thickness, color); + DrawLineEx(topLeft, bottomLeft, thickness, color); + DrawLineEx(topRight, bottomRight, thickness, color); +} #endif // RAYGUI_IMPLEMENTnk_command_bufferATION