Mercurial
changeset 114:e2a73e64e8e6
[Postdog] Got history working.
| author | June Park <parkjune1995@gmail.com> |
|---|---|
| date | Tue, 06 Jan 2026 08:15:37 -0800 |
| parents | 7a4e942814bc |
| children | 96db6c3f38d6 |
| files | postdog/BUILD postdog/Roboto-Regular.ttf postdog/history/d8ff107c-856d-e516-4863-53fe587855eb.txt postdog/history/fca62761-983e-c04b-2f77-c3bfde99f400.txt postdog/history/test.txt postdog/main.c third_party/raylib/include/raygui.h third_party/raylib/raylib.bzl |
| diffstat | 8 files changed, 668 insertions(+), 69 deletions(-) [+] |
line wrap: on
line diff
--- a/postdog/BUILD Sun Jan 04 14:42:54 2026 -0800 +++ b/postdog/BUILD Tue Jan 06 08:15:37 2026 -0800 @@ -10,6 +10,7 @@ "//third_party/raylib:raylib", "//dowa:dowa", ], + data = [":all_static_assets",], linkopts_macos = [ "-framework CoreVideo", "-framework IOKit", @@ -36,3 +37,10 @@ bundle_id = "com.june.postdog" ) +filegroup( + name = "all_static_assets", + srcs = glob([ + "**/*.ttf", + "**/*.txt", + ], allow_empty=True) +)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/postdog/history/d8ff107c-856d-e516-4863-53fe587855eb.txt Tue Jan 06 08:15:37 2026 -0800 @@ -0,0 +1,240 @@ +https:/mrjunejune.com +--- +GET +--- +Content-Type: application/json +--- + +--- +HTTP Status: 200 + +<!doctype html> +<html lang="en"> + <head> + <title> MrJuneJune </title> + <meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<link rel="icon" type="image/svg+xml" href="/public/epi_all_colors.svg"> + +<link rel="preload" href="/public/fonts/Roboto-Regular.ttf" as="font" crossorigin> +<link rel="preload" href="/public/fonts/Roboto-Thin.ttf"as="font" crossorigin> + +<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; + } + + .epi-photo img { + max-width: 100%; + height: auto; + border-radius: 8px; + } + + @media (max-width: 720px) { + .epi-photo { + margin-bottom: 1.5em; + } + + ul { + padding-left: 1.5em; + } + + li { + margin-bottom: 0.75em; + } + } + </style> + </head> + <body> + <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 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 code</li> + <li><a href="/blog">Blogs</a> - My thoughts / Experiments </li> + <li><a href="/resume">Resume</a> - My professional experiences </li> + <li><a href="/tools">Tools</a> - Things I made for myself </li> + </ul> + </main> + <div style="display: flex; align-items: center; justify-content: center; margin: 30px 0px;"> + <small>© 2026 June Park</small> +</div> + + <script>(function(){function c(){var b=a.contentDocument||a.contentWindow.document;if(b){var d=b.createElement('script');d.innerHTML="window.__CF$cv$params={r:'9b9c39a8799bcbfd',t:'MTc2NzcxMzA5Nw=='};var a=document.createElement('script');a.src='/cdn-cgi/challenge-platform/scripts/jsd/main.js';document.getElementsByTagName('head')[0].appendChild(a);";b.getElementsByTagName('head')[0].appendChild(d)}}if(document.body){var a=document.createElement('iframe');a.height=1;a.width=1;a.style.position='absolute';a.style.top=0;a.style.left=0;a.style.border='none';a.style.visibility='hidden';document.body.appendChild(a);if('loading'!==document.readyState)c();else if(window.addEventListener)document.addEventListener('DOMContentLoaded',c);else{var e=document.onreadystatechange||function(){};document.onreadystatechange=function(b){e(b);'loading'!==document.readyState&&(document.onreadystatechange=e,c())}}}})();</script></body> + <script> + let arr = Array.from({ length: 18 }, (_, i) => i+1); + function setRandomImages() { + const randomIndex = Math.floor(Math.random() * arr.length); + const pos = arr[randomIndex]; + currentPhoto.src = `/public/epi-photos/webp/${pos}.webp`; + currentPhoto.onload = () => { + currentPhoto.style.opacity = "1"; + }; + setTimeout(() => setRandomImages(), 1000); + } + setRandomImages(); + </script> +</html> +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/postdog/history/fca62761-983e-c04b-2f77-c3bfde99f400.txt Tue Jan 06 08:15:37 2026 -0800 @@ -0,0 +1,22 @@ +https://httpbin.org/get +--- +GET +--- +Content-Type: application/json +--- + +--- +HTTP Status: 200 + +{ + "args": {}, + "headers": { + "Accept": "*/*", + "Content-Type": "application/json", + "Host": "httpbin.org", + "X-Amzn-Trace-Id": "Root=1-695d2b83-44f5bafa141af3382d8e56b8" + }, + "origin": "134.128.194.38", + "url": "https://httpbin.org/get" +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/postdog/history/test.txt Tue Jan 06 08:15:37 2026 -0800 @@ -0,0 +1,23 @@ +https://httpbin.org/get +--- +GET +--- +Content-Type: application/json +June: Park +--- + +--- +HTTP Status: 200 + +{ + "args": {}, + "headers": { + "Accept": "*/*", + "Content-Type": "application/json", + "Host": "httpbin.org", + "X-Amzn-Trace-Id": "Root=1-695bdbd6-243c50b9737c539f43a5badc" + }, + "origin": "134.128.194.38", + "url": "https://httpbin.org/get" +} +
--- a/postdog/main.c Sun Jan 04 14:42:54 2026 -0800 +++ b/postdog/main.c Tue Jan 06 08:15:37 2026 -0800 @@ -3,7 +3,6 @@ #include <string.h> #include <time.h> #include <sys/stat.h> -#include <dirent.h> #include "dowa/dowa.h" #include <curl/curl.h> @@ -11,6 +10,10 @@ #define RAYGUI_IMPLEMENTATION #include "third_party/raylib/include/raygui.h" +#ifndef POSTDOG_PATHS + #define POSTDOG_PATHS "/Users/mrjunejune/zenbu/postdog/history" +#endif + #define SCREEN_WIDTH 1280 #define SCREEN_HEIGHT 780 #define TEXT_SIZE 10 @@ -27,6 +30,19 @@ #define RESULT_BUFFER_LENGTH 1024 * 1025 * 5 #define AREANA_BUFFER_LENGTH 1024 * 1025 * 15 + +#ifdef _WIN32 + #include <direct.h> + #include <io.h> + #define mkdir(path, mode) _mkdir(path) + #define access _access + #define F_OK 0 +#else + #include <sys/stat.h> + #include <dirent.h> + #include <unistd.h> +#endif + typedef Dowa_KV(char*, char*) INPUT_HASHMAP; typedef struct { @@ -35,12 +51,114 @@ } ResponseBuffer; typedef struct { - char filename[256]; - char displayName[128]; - char method[16]; - time_t timestamp; + char *filename; + Rectangle rect; + long time_modified; } HistoryItem; +typedef struct { + Rectangle rectangle; + char *label; + bool active; +} TabItem; + +typedef enum { + TAB_HEADER = 0, + TAB_BODY, + TAB_GET_PARAMS, + TAB_BAR, + TAB_LENGTH +} PostDog_Tab_Enum; + +static uint32 counter = 0; +HistoryItem *history_items = NULL; +HistoryItem *new_history_items = NULL; + +int CompareHistoryItemsByDate(const void *a, const void *b) { + HistoryItem *itemA = (HistoryItem *)a; + HistoryItem *itemB = (HistoryItem *)b; + return (itemB->time_modified - itemA->time_modified); +} + +// TODO: Make this into generic fucntion so I can use it across different thing. +void PostDog_List_Directory(const char *path, HistoryItem **p_file_arr) +{ + HistoryItem *file_arr = *p_file_arr; +#ifdef _WIN32 + struct _finddata_t fileinfo; + intptr_t handle; + char search_path[256]; + sprintf(search_path, "%s\\*", path); + + if ((handle = _findfirst(search_path, &fileinfo)) == -1L) { + printf("Directory is empty or cannot be read.\n"); + } else { + do { + HistoryItem item = {0}; + item.filename = strdup(fileinfo.name); + item.rect = (Rectangle){0}; + item.time_modified = fileinfo.time_write; + Dowa_Array_Push(file_arr, item); + } while (_findnext(handle, &fileinfo) == 0); + _findclose(handle); + } +#else + struct dirent *entry; + struct stat file_stat; + DIR *dp = opendir(path); + if (dp == NULL) return; + + char full_path[256]; + while ((entry = readdir(dp))) + { + if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) + continue; + snprintf(full_path, sizeof(full_path), "%s/%s", path, entry->d_name); + if (stat(full_path, &file_stat) == 0) + { + HistoryItem item = {0}; + item.filename = strdup(entry->d_name); + item.time_modified = file_stat.st_mtime; + Dowa_Array_Push(file_arr, item); + } + } + closedir(dp); +#endif + int count = Dowa_Array_Length(file_arr); + if (count > 1) { + qsort(file_arr, count, sizeof(HistoryItem), CompareHistoryItemsByDate); + } +} + +int PostDog_History_Load(HistoryItem **p_history_files) +{ + if (access(POSTDOG_PATHS, F_OK) == -1) + { + printf("Directory '%s' not found. Creating it...\n", POSTDOG_PATHS); + if (mkdir(POSTDOG_PATHS, 0777) != 0) + return -1; + return 0; + } + printf("Directory '%s' already exists.\n", POSTDOG_PATHS); + PostDog_List_Directory(POSTDOG_PATHS, p_history_files); + return 0; +} + +bool InArea(Vector2 mouse_position, Rectangle area) +{ + return ( + mouse_position.x >= area.x && + mouse_position.x < area.x + area.width && + mouse_position.y >= area.y && + mouse_position.y < area.y + area.height + ); +} + +bool Clicked(Vector2 mouse_position, Rectangle area) +{ + return (InArea(mouse_position, area) && IsMouseButtonPressed(MOUSE_BUTTON_LEFT)); +} + char *PostDog_Enum_To_String(int active_enum) { switch(active_enum) @@ -50,6 +168,74 @@ case 2: return "PUT"; case 3: return "DELETE"; } + return 0; +} + +void PostDog_History_CreateFile(char *filename, char* values) +{ + char full_file_path[512] = {0}; + snprintf(full_file_path, 512, "%s/%s", POSTDOG_PATHS, filename); + FILE *file = fopen(full_file_path, "w"); + if (!file) + { + printf("Failed to create a file: %s\n", full_file_path); + return; + } + fwrite(values, 1, strlen(values), file); + fclose(file); +} + +void PostDog_Request_SaveFile( + const char *url, + const char *method, + const char *headers, + const char *body, + const char *response) +{ + size_t new_file_size = 1024 * 1024; + Dowa_Arena *arena = Dowa_Arena_Create(1024 * 1024 * 2); + char *new_file = Dowa_Arena_Allocate(arena, 1024 * 1024); + snprintf( + new_file, + new_file_size, + "%s\n" + "---\n" + "%s\n" + "---\n" + "%s\n" + "---\n" + "%s\n" + "---\n" + "%s\n", + url, + method, + headers, + body, + response + ); + char *filename = Dowa_Arena_Allocate(arena, 1024); + if (!filename) + { + perror("Error opening file"); + exit(EXIT_FAILURE); + } + char *uuid4 = (char *)Dowa_Arena_Allocate(arena, 37); + if (!uuid4) + { + perror("Error uuid"); + exit(EXIT_FAILURE); + } + + int32 seed = (uint32)time(NULL) ^ counter++; + Dowa_String_UUID(seed, uuid4); + snprintf(filename, 1024, "%s.txt", uuid4); + PostDog_History_CreateFile(filename, new_file); + + HistoryItem item = (HistoryItem){ .filename = malloc(sizeof(char) * strlen(filename) + 1), .rect = (Rectangle){0} }; + memcpy(item.filename, filename, strlen(filename) + 1); + Dowa_Array_Push(new_history_items, item); + + Dowa_Arena_Free(arena); } static size_t Postdog_Curl_Callback(void *contents, size_t size, size_t nmemb, void *userp) @@ -72,7 +258,7 @@ return real_size; } -int PostDog_Make_HttpRequest( +int PostDog_Http_Request( const char *url, const char *method, const char *headers, @@ -106,16 +292,14 @@ if (strcmp(method, "POST") == 0) { curl_easy_setopt(curl, CURLOPT_POST, 1L); - if (body && strlen(body) > 0) { + if (body && strlen(body) > 0) curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body); - } } else if (strcmp(method, "PUT") == 0) { curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PUT"); - if (body && strlen(body) > 0) { + if (body && strlen(body) > 0) curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body); - } } else if (strcmp(method, "DELETE") == 0) { @@ -131,9 +315,8 @@ while (line != NULL) { // Trim whitespace while (*line == ' ' || *line == '\t') line++; - if (strlen(line) > 0) { + if (strlen(line) > 0) headerList = curl_slist_append(headerList, line); - } line = strtok(NULL, "\n"); } curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headerList); @@ -146,10 +329,8 @@ // Follow redirects curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); - // Set timeout curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30L); - // Perform request res = curl_easy_perform(curl); @@ -174,27 +355,20 @@ free(buffer.data); curl_global_cleanup(); + PostDog_Request_SaveFile( + url, + method, + headers, + body, + response); return 0; } -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 + // Reset char *question_mark = strchr(url_input_text, '?'); if (question_mark) *question_mark = '\0'; @@ -207,43 +381,23 @@ 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; + 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 = "&"; } @@ -251,6 +405,100 @@ Dowa_Arena_Free(arena); } +int PostDog_String_To_MethodEnum(char *value) +{ + if (strstr(value, "GET")) + return 0; + if (strstr(value, "POST")) + return 1; + if (strstr(value, "PUT")) + return 2; + if (strstr(value, "DELETE")) + return 3; + return 0; +} + +void PostDog_Params_Reset( + char **p_url_input_text, + int *p_active_method_dropdown, + char **url_body_map, + char **p_url_result_text +) +{ + char *url_input_text = *p_url_input_text; + char *url_result_text = *p_url_result_text; + int active_method_dropdown = *p_active_method_dropdown; + + url_input_text = ""; + url_result_text = ""; + active_method_dropdown = 0; + for (int i = 0; i < Dowa_Array_Length(url_body_map); i++) + url_body_map[i] = ""; +} + +void PostDog_Load_File( + const char *filename, + char **p_url_input_text, + int *p_active_method_dropdown, + char **url_body_map, + char **p_url_result_text +) { + char *url_input_text = *p_url_input_text; + char *url_result_text = *p_url_result_text; + int active_method_dropdown = *p_active_method_dropdown; + + char full_file_path[512] = {0}; + snprintf(full_file_path, 512, "%s/%s", POSTDOG_PATHS, filename); + FILE *file = fopen(full_file_path, "r"); + if (!file) + return; + + fseek(file, 0, SEEK_END); + size_t file_size = ftell(file); + fseek(file, 0, SEEK_SET); + + Dowa_Arena *init_arena = Dowa_Arena_Create(file_size + 2); + Dowa_Arena *split_arena = Dowa_Arena_Create(file_size * 2); + char *file_buffer = Dowa_Arena_Allocate(init_arena, file_size+1); + fread(file_buffer, 1, file_size, file); + char **values = Dowa_String_Split(file_buffer, "---\n", file_size, 4, split_arena); + Dowa_Arena_Free(init_arena); + for (int i = 0; i < Dowa_Array_Length(values); i++) + { + if (i == 0) + { + snprintf(url_input_text, strlen(values[i]) + 1, "%s", values[i]); + url_input_text[strcspn(url_input_text, "\n")] = '\0'; + } + else if (i == 1) + active_method_dropdown = PostDog_String_To_MethodEnum(values[i]); + else if (i <= 3) + { + snprintf(url_body_map[i-2], strlen(values[i]) + 1, "%s", values[i]); + for (int j = strlen(values[i]); j > 0; j--) + { + if (url_body_map[i-2][j] == '\n') + { + url_body_map[i-2][j] = '\0'; + break; + } + } + } + else + snprintf(url_result_text, strlen(values[i]) + 1, "%s", values[i]); + } +} + +Rectangle AddPadding(Rectangle rect, float padding) +{ + return (Rectangle){ + rect.x + padding, + rect.y + padding, + rect.width - (2 * padding), + rect.height - (2 * padding) + }; +} + int main() { // -- initizlied --// @@ -260,31 +508,39 @@ Dowa_Arena *arena = Dowa_Arena_Create(AREANA_BUFFER_LENGTH); + Font customFont = LoadFontEx("postdog/Roboto-Regular.ttf", 20, 0, 0); + GuiSetFont(customFont); + GuiSetStyle(DEFAULT, TEXT_SIZE, 20); + // -- Starting pos ---// + // Everyhting is relative to sidebar at this point lol float side_bar_x = 10; float side_bar_y = 10; - Rectangle history_sidebar = { .x = side_bar_x, .y = side_bar_y, .width = 0, .height = 0 }; + Dowa_Array_Reserve(history_items, 10); + Dowa_Array_Reserve(new_history_items, 10); + PostDog_History_Load(&history_items); Rectangle url_area = { 0 }; Rectangle textBounds = { 0 }; Rectangle url_input_bounds = { 0 }; - bool url_input_bool = false; + bool url_input_edit = false; Rectangle url_text_bounds = { 0 }; Rectangle url_enter_button = { 0 }; Rectangle method_dropdown = { 0 }; + 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); + char **url_body_map = NULL; + Dowa_Array_Push_Arena(url_body_map, (char *)Dowa_Arena_Allocate(arena, HEADER_BUFFER_LENGTH), arena); + Dowa_Array_Push_Arena(url_body_map, (char *)Dowa_Arena_Allocate(arena, BODY_BUFFER_LENGTH), arena); + Dowa_Array_Push_Arena(url_body_map, (char *)Dowa_Arena_Allocate(arena, DEFAULT_TEXT_BUFFER_LENGTH), arena); + Dowa_Array_Push_Arena(url_body_map, (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, ""); + snprintf(url_body_map[TAB_HEADER], HEADER_BUFFER_LENGTH, "Content-Type: application/json"); + snprintf(url_body_map[TAB_BODY], HEADER_BUFFER_LENGTH, ""); char *url_result_text = (char *)Dowa_Arena_Allocate(arena, RESULT_BUFFER_LENGTH); @@ -305,7 +561,7 @@ Rectangle result_body = { 0 }; // General styling. - int padding = 10; // TODO make it % based? + float padding = 10; // TODO make it % based? int active_input_tab = 0; while (!WindowShouldClose()) @@ -314,7 +570,19 @@ int screen_height = GetScreenHeight(); history_sidebar.width = screen_width * 0.15; - history_sidebar.height = screen_width - 10; + history_sidebar.height = screen_width - 10; + + int32 new_history_items_length = Dowa_Array_Length(new_history_items); + int32 history_item_length = Dowa_Array_Length(history_items); + int32 total = new_history_items_length + history_item_length; + for (int i = 0; i < total; i++) + { + HistoryItem *curr_history_items = i < new_history_items_length ? &new_history_items[i] : &history_items[i - new_history_items_length]; + curr_history_items->rect.x = history_sidebar.x + padding; + curr_history_items->rect.y = history_sidebar.y + (padding * 2 * (i+1)) + (i * history_sidebar.height * 0.05); + curr_history_items->rect.width = history_sidebar.width - (padding * 2); + curr_history_items->rect.height = history_sidebar.height * 0.05; + } // -- URL Area --// url_area.x = (side_bar_x + history_sidebar.width); @@ -371,24 +639,32 @@ result_body.width = url_area.width * 0.49 - padding; result_body.height = result_area.height - input_tab.height - padding; + Vector2 mouse_position = GetMousePosition(); + BeginDrawing(); ClearBackground(GetColor(GuiGetStyle(DEFAULT, BACKGROUND_COLOR))); - // Sidebar Rect DrawRectangleRec(history_sidebar, Fade(GRAY, 0.1f)); + for (int i = 0; i < total; i++) + { + HistoryItem *curr_history_items = i < new_history_items_length ? &new_history_items[i] : &history_items[i - new_history_items_length]; + DrawRectangleRec(curr_history_items->rect, Fade(RED, 0.1f)); + GuiDrawText(curr_history_items->filename, AddPadding(curr_history_items->rect, padding), TEXT_ALIGN_CENTER, RED); + } // URL area Rect GuiDrawText("URL: ", url_text_bounds, TEXT_ALIGN_CENTER, RED); DrawRectangleRec(url_area, Fade(RED, 0.1f)); - if (GuiTextBox(url_input_bounds, url_input_text, DEFAULT_TEXT_BUFFER_LENGTH, url_input_bool)) - url_input_bool = !url_input_bool; + if (GuiTextBox(url_input_bounds, url_input_text, DEFAULT_TEXT_BUFFER_LENGTH, url_input_edit)) + url_input_edit = !url_input_edit; + sendRequest = GuiButton(url_enter_button, "ENTER"); if (sendRequest) - PostDog_Make_HttpRequest( + PostDog_Http_Request( url_input_text, PostDog_Enum_To_String(active_method_dropdown), - url_body_map[0].value, - url_body_map[1].value, + url_body_map[TAB_HEADER], + url_body_map[TAB_BODY], url_result_text, RESULT_BUFFER_LENGTH ); @@ -399,15 +675,43 @@ DrawRectangleRec(input_area, Fade(BLUE, 0.1f)); DrawRectangleRec(input_tab, Fade(DARKBLUE, 0.1f)); GuiSetStyle(TOGGLE, GROUP_PADDING, 0); - if (JUNE_GuiTextBox(input_body, url_body_map[active_input_tab].value, DEFAULT_TEXT_BUFFER_LENGTH, input_body_bool)) + if (JUNE_GuiTextBox(input_body, url_body_map[active_input_tab], 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); + + PostDog_Update_URL(&url_input_text, url_body_map[TAB_GET_PARAMS]); // 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); + + if (url_input_edit && (IsKeyDown(KEY_LEFT_SUPER) || IsKeyDown(KEY_LEFT_CONTROL)) && IsKeyPressed(KEY_C)) + { + SetClipboardText(url_input_text); + } + else if (input_body_bool && (IsKeyDown(KEY_LEFT_SUPER) || IsKeyDown(KEY_LEFT_CONTROL)) && IsKeyPressed(KEY_C)) + { + SetClipboardText(url_body_map[active_input_tab]); + } + else if (InArea(mouse_position, result_body)) + { + DrawRectangleRec(result_body, Fade(GREEN, 0.3f)); + if ((IsKeyDown(KEY_LEFT_SUPER) || IsKeyDown(KEY_LEFT_CONTROL)) && IsKeyPressed(KEY_C)) + SetClipboardText(url_result_text); + } + + for (int i = 0; i < Dowa_Array_Length(history_items); i++) + { + if (Clicked(mouse_position, history_items[i].rect)) + PostDog_Load_File( + history_items[i].filename, + &url_input_text, + &active_method_dropdown, + url_body_map, + &url_result_text + ); + } EndDrawing(); } CloseWindow();
--- a/third_party/raylib/include/raygui.h Sun Jan 04 14:42:54 2026 -0800 +++ b/third_party/raylib/include/raygui.h Tue Jan 06 08:15:37 2026 -0800 @@ -6594,7 +6594,7 @@ { GuiDrawRectangle(bounds, GuiGetStyle(TEXTBOX, BORDER_WIDTH), GetColor(GuiGetStyle(TEXTBOX, BORDER + (state*3))), GetColor(GuiGetStyle(TEXTBOX, BASE_COLOR_DISABLED))); } - else GuiDrawRectangle(bounds, GuiGetStyle(TEXTBOX, BORDER_WIDTH), GetColor(GuiGetStyle(TEXTBOX, BORDER + (state*3))), BLANK); + else GuiDrawRectangle(bounds, GuiGetStyle(TEXTBOX, BORDER_WIDTH), GetColor(GuiGetStyle(TEXTBOX, BORDER + (state*3))), GetColor(GuiGetStyle(TEXTBOX, BASE_COLOR_PRESSED))); // Draw text considering index offset if required // NOTE: Text index offset depends on cursor position
--- a/third_party/raylib/raylib.bzl Sun Jan 04 14:42:54 2026 -0800 +++ b/third_party/raylib/raylib.bzl Tue Jan 06 08:15:37 2026 -0800 @@ -2,6 +2,7 @@ name, srcs, deps = [], + data = [], deps_macos = [], deps_linux = [], deps_windows = [], @@ -46,6 +47,7 @@ native.cc_binary( name = name, srcs = srcs, + data = data, deps = deps + select({ "//config:macos": deps_macos, "//config:linux": deps_linux,