diff postdog/main.c @ 173:827c6ac504cd hg-web

Merged in default here.
author MrJuneJune <me@mrjunejune.com>
date Mon, 19 Jan 2026 18:59:10 -0800
parents 0face9898d04
children
line wrap: on
line diff
--- a/postdog/main.c	Sat Jan 10 13:35:09 2026 -0800
+++ b/postdog/main.c	Mon Jan 19 18:59:10 2026 -0800
@@ -3,15 +3,29 @@
 #include <string.h>
 #include <time.h>
 #include <sys/stat.h>
-#include "dowa/dowa.h"
+#include <uv.h>
 
-#include <curl/curl.h>
+#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
+
+
+#include "dowa/dowa.h"
+#include "seobeo/seobeo.h"
 #include "third_party/raylib/include/raylib.h"
-#define RAYGUI_IMPLEMENTATION
 #include "third_party/raylib/include/raygui.h"
+#include "third_party/raylib/custom.h"
 
 #ifndef POSTDOG_PATHS
-  #define POSTDOG_PATHS "/Users/mrjunejune/zenbu/postdog/history"
+  #define POSTDOG_PATHS "/home/june/zenbu/postdog/history"
 #endif
 
 #define SCREEN_WIDTH 1280
@@ -24,18 +38,1165 @@
 #define BODY_BUFFER_LENGTH 1024 * 1024 * 5
 #define RESULT_BUFFER_LENGTH 1024 * 1024 * 5
 
+// #define URL_TEXT_DEFAULT "https://httpbin.org/get"
+#define URL_TEXT_DEFAULT "wss://mrjunejune.com/echo"
+#define HEADER_TEXT_DEFAULT "Content-Type: application/json"
+#define BODY_TEXT_DEFAULT ""
+#define GET_PARAM_TEXT_DEFAULT "foo bar"
 
-#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
+// ============================================================================
+// TextArea Component
+// ============================================================================
+
+#define TEXT_SIZE_DEFAULT 20 // used to calcualte spacing
+#define TEXT_AREA_LINE_HEIGHT GuiGetStyle(DEFAULT, TEXT_SIZE)
+#define TEXT_AREA_PADDING 30
+#define TEXT_AREA_CURSOR_WIDTH 2
+#define TEXT_AREA_MAX_UNDO_STATES 64
+#define TEXT_AREA_MAX_INSTANCES 8
+
+typedef struct {
+  char *text;
+  int cursor_pos;
+  int selection_start;
+  int selection_end;
+} TextAreaUndoEntry;
+
+// Cached line info for fast rendering
+typedef struct {
+  int start_pos;      // Character position where this line starts
+  int end_pos;        // Character position where this line ends (exclusive)
+  int char_count;     // Number of characters in this line
+} LineInfo;
+
+typedef struct {
+  int id;             // Unique ID for this text area
+  int cursor_pos;         // Current cursor position in text
+  int selection_start;      // Selection start (-1 if no selection)
+  int selection_end;      // Selection end (-1 if no selection)
+  float scroll_offset_y;    // Vertical scroll offset
+  float scroll_offset_x;    // Horizontal scroll offset (for non-wrap mode)
+  boolean is_selecting;     // Currently dragging to select
+  boolean is_initialized;     // State has been initialized
+
+  // Undo history
+  TextAreaUndoEntry *undo_stack;  // Dowa array of undo entries
+  int undo_index;         // Current position in undo stack
+
+  // Internal tracking
+  double last_blink_time;
+  boolean cursor_visible;
+
+  // Line cache for fast rendering
+  LineInfo *line_cache;     // Dowa array of line info
+  int cached_text_len;      // Text length when cache was built
+  int cached_text_hash;     // Simple hash to detect text changes
+  float cached_content_width; // Content width when cache was built
+  boolean cache_valid;      // Whether cache is valid
+} TextAreaState;
+
+static TextAreaState g_text_area_states[TEXT_AREA_MAX_INSTANCES] = {0};
+static int g_text_area_state_count = 0;
+static char *g_clipboard_text = NULL;
+static Dowa_Arena *g_text_area_arena = NULL;
+
+// Helper functions
+static int TA_Min_Int(int a, int b) { return a < b ? a : b; }
+static int TA_Max_Int(int a, int b) { return a > b ? a : b; }
+static float TA_Min_Float(float a, float b) { return a < b ? a : b; }
+static float TA_Max_Float(float a, float b) { return a > b ? a : b; }
+
+// Sanitize text for display - replace tabs with spaces, remove other non-printable chars
+static void SanitizeTextForDisplay(char *text) {
+  if (!text) return;
+  char *read = text;
+  char *write = text;
+  while (*read) {
+    if (*read == '\t') {
+      // Replace tab with 2 spaces
+      *write++ = ' ';
+      *write++ = ' ';
+    } else if (*read == '\n' || *read == '\r') {
+      // Keep newlines and carriage returns
+      *write++ = *read;
+    } else if ((unsigned char)*read >= 32 && (unsigned char)*read < 127) {
+      // Keep printable ASCII
+      *write++ = *read;
+    } else if ((unsigned char)*read >= 128) {
+      // Keep UTF-8 characters (high bit set)
+      *write++ = *read;
+    }
+    // Skip other non-printable characters
+    read++;
+  }
+  *write = '\0';
+}
+
+static TextAreaState* GetTextAreaState(int id) {
+  for (int i = 0; i < g_text_area_state_count; i++) {
+    if (g_text_area_states[i].id == id && g_text_area_states[i].is_initialized) {
+      return &g_text_area_states[i];
+    }
+  }
+  return NULL;
+}
+
+static TextAreaState* CreateTextAreaState(int id) {
+  // First, check if there's an existing slot with this ID (possibly reset)
+  for (int i = 0; i < g_text_area_state_count; i++) {
+    if (g_text_area_states[i].id == id) {
+      TextAreaState *state = &g_text_area_states[i];
+      // Reuse this slot - clear it but keep the id
+      memset(state, 0, sizeof(TextAreaState));
+      state->id = id;
+      state->selection_start = -1;
+      state->selection_end = -1;
+      state->undo_index = -1;
+      state->cursor_visible = TRUE;
+      state->is_initialized = TRUE;
+      return state;
+    }
+  }
+
+  // No existing slot, create a new one
+  if (g_text_area_state_count >= TEXT_AREA_MAX_INSTANCES) {
+    return &g_text_area_states[0]; // Reuse first slot as fallback
+  }
+
+  TextAreaState *state = &g_text_area_states[g_text_area_state_count++];
+  memset(state, 0, sizeof(TextAreaState));
+  state->id = id;
+  state->selection_start = -1;
+  state->selection_end = -1;
+  state->undo_index = -1;
+  state->cursor_visible = TRUE;
+  state->is_initialized = TRUE;
+  return state;
+}
+
+static void GetLineAndColumn(const char *text, int pos, int *out_line, int *out_column) {
+  int line = 0;
+  int column = 0;
+  for (int i = 0; i < pos && text[i] != '\0'; i++) {
+    if (text[i] == '\n') {
+      line++;
+      column = 0;
+    } else {
+      column++;
+    }
+  }
+  *out_line = line;
+  *out_column = column;
+}
+
+static int GetPosFromLineColumn(const char *text, int line, int column) {
+  int current_line = 0;
+  int current_col = 0;
+  int i = 0;
+
+  while (text[i] != '\0') {
+    if (current_line == line && current_col == column) {
+      return i;
+    }
+    if (text[i] == '\n') {
+      if (current_line == line) {
+        return i;
+      }
+      current_line++;
+      current_col = 0;
+    } else {
+      current_col++;
+    }
+    i++;
+  }
+  return i;
+}
+
+static int GetLineStart(const char *text, int pos) {
+  int i = pos;
+  while (i > 0 && text[i - 1] != '\n') {
+    i--;
+  }
+  return i;
+}
+
+static int GetLineEnd(const char *text, int pos) {
+  int i = pos;
+  int len = strlen(text);
+  while (i < len && text[i] != '\n') {
+    i++;
+  }
+  return i;
+}
+
+static int CountLines(const char *text) {
+  int count = 1;
+  for (int i = 0; text[i] != '\0'; i++) {
+    if (text[i] == '\n') count++;
+  }
+  return count;
+}
+
+// Simple hash to detect text changes (djb2)
+static int SimpleTextHash(const char *text, int len) {
+  int hash = 5381;
+  int sample_count = len < 100 ? len : 100;  // Sample first 100 chars
+  for (int i = 0; i < sample_count; i++) {
+    hash = ((hash << 5) + hash) + text[i];
+  }
+  // Also sample some chars from middle and end
+  if (len > 200) {
+    for (int i = len/2; i < len/2 + 50 && i < len; i++) {
+      hash = ((hash << 5) + hash) + text[i];
+    }
+  }
+  return hash;
+}
+
+// Build line cache - O(n) once, then O(1) for rendering
+static void BuildLineCache(TextAreaState *state, const char *text, float content_width,
+                           int font_size, boolean wrap, Dowa_Arena *arena) {
+  int text_len = strlen(text);
+
+  // Check if cache is still valid
+  int new_hash = SimpleTextHash(text, text_len);
+  if (state->cache_valid &&
+      state->cached_text_len == text_len &&
+      state->cached_text_hash == new_hash &&
+      state->cached_content_width == content_width) {
+    return;  // Cache is valid, no need to rebuild
+  }
+
+  // Clear old cache
+  if (state->line_cache) {
+    Dowa_Array_Free(state->line_cache);
+    state->line_cache = NULL;
+  }
+
+  // Approximate character width (for non-monospace, use average)
+  float avg_char_width = font_size * 0.6f;  // Reasonable approximation for most fonts
+  int max_chars_per_line = (int)(content_width / avg_char_width);
+  if (max_chars_per_line < 1) max_chars_per_line = 1;
+
+  Dowa_Array_Reserve(state->line_cache, text_len / max_chars_per_line + 10);
+
+  int line_start = 0;
+  int i = 0;
+
+  while (i <= text_len) {
+    boolean is_end = (i == text_len);
+    boolean is_newline = (!is_end && text[i] == '\n');
+    int chars_in_line = i - line_start;
+
+    // Check if we need to wrap (based on character count approximation)
+    boolean should_wrap = wrap && !is_end && !is_newline &&
+                          chars_in_line >= max_chars_per_line;
+
+    if (is_end || is_newline || should_wrap) {
+      LineInfo line = {
+        .start_pos = line_start,
+        .end_pos = i,
+        .char_count = chars_in_line
+      };
+      Dowa_Array_Push(state->line_cache, line);
+
+      if (is_newline) {
+        line_start = i + 1;
+      } else if (should_wrap) {
+        // Try to find a space to wrap at
+        int wrap_pos = i;
+        for (int j = i - 1; j > line_start && j > i - 20; j--) {
+          if (text[j] == ' ') {
+            wrap_pos = j;
+            break;
+          }
+        }
+        // Update the last line entry with correct end
+        state->line_cache[Dowa_Array_Length(state->line_cache) - 1].end_pos = wrap_pos;
+        state->line_cache[Dowa_Array_Length(state->line_cache) - 1].char_count = wrap_pos - line_start;
+        line_start = (text[wrap_pos] == ' ') ? wrap_pos + 1 : wrap_pos;
+        i = line_start - 1;  // Will be incremented at end of loop
+      } else {
+        line_start = i + 1;
+      }
+    }
+    i++;
+  }
+
+  // Ensure at least one line exists
+  if (Dowa_Array_Length(state->line_cache) == 0) {
+    LineInfo empty_line = { .start_pos = 0, .end_pos = 0, .char_count = 0 };
+    Dowa_Array_Push(state->line_cache, empty_line);
+  }
+
+  // Update cache metadata
+  state->cached_text_len = text_len;
+  state->cached_text_hash = new_hash;
+  state->cached_content_width = content_width;
+  state->cache_valid = TRUE;
+}
+
+static int MeasureTextRange(const char *text, int start, int end, int font_size) {
+  if (start >= end) return 0;
+
+  char temp[1024];
+  int len = TA_Min_Int(end - start, 1023);
+  strncpy(temp, text + start, len);
+  temp[len] = '\0';
+
+  return MeasureTextEx(GuiGetFont(), temp, font_size, TEXT_SIZE_DEFAULT/GuiGetStyle(DEFAULT, TEXT_SIZE)).x;
+}
+
+static int GetCharIndexFromPos(const char *text, Rectangle bounds, Vector2 pos,
+                boolean wrap, float scroll_y, int font_size, int line_height) {
+  if (!text || strlen(text) == 0) return 0;
+
+  float content_x = bounds.x + TEXT_AREA_PADDING;
+  float content_y = bounds.y + TEXT_AREA_PADDING - scroll_y;
+  float content_width = bounds.width - TEXT_AREA_PADDING * 2;
+
+  int text_len = strlen(text);
+
+  float click_line_y = (pos.y - content_y) / line_height;
+  int target_visual_line = (int)click_line_y;
+  if (target_visual_line < 0) target_visual_line = 0;
+
+  int current_visual_line = 0;
+  int i = 0;
+  int line_char_start = 0;
+
+  while (i <= text_len) {
+    boolean is_newline = (i < text_len && text[i] == '\n');
+
+    if (wrap && i > line_char_start) {
+      int line_width = MeasureTextRange(text, line_char_start, i, font_size);
+      if (line_width > content_width && i > line_char_start + 1) {
+        int wrap_pos = i - 1;
+        for (int j = i - 1; j > line_char_start; j--) {
+          if (text[j] == ' ') {
+            wrap_pos = j;
+            break;
+          }
+        }
+
+        if (current_visual_line == target_visual_line) {
+          float click_x = pos.x - content_x;
+          int best_pos = line_char_start;
+          float best_dist = 99999;
+
+          for (int k = line_char_start; k <= wrap_pos; k++) {
+            int char_x = MeasureTextRange(text, line_char_start, k, font_size);
+            float dist = (float)(click_x - char_x);
+            if (dist < 0) dist = -dist;
+            if (dist < best_dist) {
+              best_dist = dist;
+              best_pos = k;
+            }
+          }
+          return best_pos;
+        }
+
+        current_visual_line++;
+        line_char_start = (text[wrap_pos] == ' ') ? wrap_pos + 1 : wrap_pos;
+        i = line_char_start;
+        continue;
+      }
+    }
+
+    if (is_newline || i == text_len) {
+      if (current_visual_line == target_visual_line || i == text_len) {
+        float click_x = pos.x - content_x;
+        int line_end = i;
+        int best_pos = line_char_start;
+        float best_dist = 99999;
+
+        for (int k = line_char_start; k <= line_end; k++) {
+          int char_x = MeasureTextRange(text, line_char_start, k, font_size);
+          float dist = (float)(click_x - char_x);
+          if (dist < 0) dist = -dist;
+          if (dist < best_dist) {
+            best_dist = dist;
+            best_pos = k;
+          }
+        }
+        return TA_Min_Int(best_pos, text_len);
+      }
+
+      current_visual_line++;
+      line_char_start = i + 1;
+    }
+
+    i++;
+  }
+
+  return text_len;
+}
+
+// Get character index from mouse position using line cache
+static int GetCharIndexFromPosWithCache(const char *text, Rectangle bounds, Vector2 pos,
+                       float scroll_y, int font_size, int line_height,
+                       LineInfo *line_cache) {
+  if (!text || strlen(text) == 0) return 0;
+  if (!line_cache || Dowa_Array_Length(line_cache) == 0) return 0;
+
+  float content_x = bounds.x + TEXT_AREA_PADDING;
+  float content_y = bounds.y + TEXT_AREA_PADDING - scroll_y;
+
+  int text_len = strlen(text);
+  int line_count = Dowa_Array_Length(line_cache);
+
+  // Find which visual line was clicked
+  float click_line_y = (pos.y - content_y) / line_height;
+  int target_line = (int)click_line_y;
+  if (target_line < 0) target_line = 0;
+  if (target_line >= line_count) target_line = line_count - 1;
+  if (target_line < 0) return 0;  // Safety check
+
+  LineInfo *line = &line_cache[target_line];
+
+  // Find character position within the line
+  float click_x = pos.x - content_x;
+  if (click_x < 0) return line->start_pos;
+
+  // Linear search for the character closest to click position
+  int best_pos = line->start_pos;
+  float best_dist = 99999;
+
+  for (int k = line->start_pos; k <= line->end_pos; k++) {
+    int char_x = MeasureTextRange(text, line->start_pos, k, font_size);
+    float dist = click_x - char_x;
+    if (dist < 0) dist = -dist;
+
+    if (dist < best_dist) {
+      best_dist = dist;
+      best_pos = k;
+    }
+  }
+
+  return TA_Min_Int(best_pos, text_len);
+}
+
+// Get cursor position using line cache (fast O(log n) or O(n) worst case)
+static Vector2 GetCursorScreenPosFromCache(const char *text, int cursor_pos, Rectangle bounds,
+                       float scroll_y, int font_size, int line_height,
+                       LineInfo *line_cache) {
+  float content_x = bounds.x + TEXT_AREA_PADDING;
+  float content_y = bounds.y + TEXT_AREA_PADDING - scroll_y;
+
+  if (!text || cursor_pos == 0) {
+    return (Vector2){content_x, content_y};
+  }
+  if (!line_cache || Dowa_Array_Length(line_cache) == 0) {
+    return (Vector2){content_x, content_y};
+  }
+
+  int text_len = strlen(text);
+  cursor_pos = TA_Min_Int(cursor_pos, text_len);
+
+  // Find which line contains the cursor
+  int line_count = Dowa_Array_Length(line_cache);
+  for (int i = 0; i < line_count; i++) {
+    LineInfo *line = &line_cache[i];
+
+    // Cursor is within this line or at the end of this line
+    if (cursor_pos >= line->start_pos && cursor_pos <= line->end_pos) {
+      float x = content_x + MeasureTextRange(text, line->start_pos, cursor_pos, font_size);
+      float y = content_y + i * line_height;
+      return (Vector2){x, y};
+    }
+  }
+
+  // Fallback: cursor at the end
+  int last_line = line_count > 0 ? line_count - 1 : 0;
+  LineInfo *line = &line_cache[last_line];
+  float x = content_x + MeasureTextRange(text, line->start_pos, cursor_pos, font_size);
+  float y = content_y + last_line * line_height;
+  return (Vector2){x, y};
+}
+
+static float GetContentHeight(const char *text, Rectangle bounds, boolean wrap,
+                 int font_size, int line_height) {
+  if (!text || strlen(text) == 0) return line_height;
+
+  float content_width = bounds.width - TEXT_AREA_PADDING * 2;
+  int visual_lines = 1;
+  int line_char_start = 0;
+  int text_len = strlen(text);
+
+  for (int i = 0; i <= text_len; i++) {
+    if (i == text_len || text[i] == '\n') {
+      visual_lines++;
+      line_char_start = i + 1;
+    } else if (wrap) {
+      int line_width = MeasureTextRange(text, line_char_start, i + 1, font_size);
+      if (line_width > content_width && i > line_char_start) {
+        int wrap_pos = i;
+        for (int j = i; j > line_char_start; j--) {
+          if (text[j] == ' ') {
+            wrap_pos = j;
+            break;
+          }
+        }
+        visual_lines++;
+        line_char_start = (text[wrap_pos] == ' ') ? wrap_pos + 1 : wrap_pos;
+      }
+    }
+  }
+
+  return visual_lines * line_height;
+}
+
+static void PushUndoState(TextAreaState *state, const char *text, Dowa_Arena *arena) {
+  TextAreaUndoEntry entry;
+  entry.text = Dowa_String_Copy_Arena((char*)text, arena);
+  entry.cursor_pos = state->cursor_pos;
+  entry.selection_start = state->selection_start;
+  entry.selection_end = state->selection_end;
+
+  if (state->undo_index < (int)Dowa_Array_Length(state->undo_stack) - 1) {
+    dowa__header(state->undo_stack)->length = state->undo_index + 1;
+  }
+
+  Dowa_Array_Push_Arena(state->undo_stack, entry, arena);
+  state->undo_index = Dowa_Array_Length(state->undo_stack) - 1;
+
+  if (Dowa_Array_Length(state->undo_stack) > TEXT_AREA_MAX_UNDO_STATES) {
+    for (int i = 0; i < (int)Dowa_Array_Length(state->undo_stack) - 1; i++) {
+      state->undo_stack[i] = state->undo_stack[i + 1];
+    }
+    dowa__header(state->undo_stack)->length--;
+    state->undo_index--;
+  }
+}
+
+static boolean PerformUndo(TextAreaState *state, char *text, int text_size) {
+  if (state->undo_index <= 0) return FALSE;
+
+  state->undo_index--;
+  TextAreaUndoEntry *entry = &state->undo_stack[state->undo_index];
+
+  strncpy(text, entry->text, text_size - 1);
+  text[text_size - 1] = '\0';
+  state->cursor_pos = entry->cursor_pos;
+  state->selection_start = entry->selection_start;
+  state->selection_end = entry->selection_end;
+
+  return TRUE;
+}
+
+static boolean PerformRedo(TextAreaState *state, char *text, int text_size) {
+  if (state->undo_index >= (int)Dowa_Array_Length(state->undo_stack) - 1) return FALSE;
+
+  state->undo_index++;
+  TextAreaUndoEntry *entry = &state->undo_stack[state->undo_index];
+
+  strncpy(text, entry->text, text_size - 1);
+  text[text_size - 1] = '\0';
+  state->cursor_pos = entry->cursor_pos;
+  state->selection_start = entry->selection_start;
+  state->selection_end = entry->selection_end;
+
+  return TRUE;
+}
+
+static void InsertTextAtCursor(char *text, int text_size, int cursor_pos,
+                const char *insert_text, int *new_cursor_pos) {
+  int text_len = strlen(text);
+  int insert_len = strlen(insert_text);
+
+  if (text_len + insert_len >= text_size - 1) {
+    insert_len = text_size - 1 - text_len;
+    if (insert_len <= 0) return;
+  }
+
+  memmove(text + cursor_pos + insert_len,
+      text + cursor_pos,
+      text_len - cursor_pos + 1);
+
+  memcpy(text + cursor_pos, insert_text, insert_len);
+
+  *new_cursor_pos = cursor_pos + insert_len;
+}
+
+static void DeleteTextRange(char *text, int start, int end) {
+  if (start >= end) return;
+  int text_len = strlen(text);
+  if (start < 0) start = 0;
+  if (end > text_len) end = text_len;
+
+  memmove(text + start, text + end, text_len - end + 1);
+}
+
+static char* GetSelectedText(const char *text, int sel_start, int sel_end, Dowa_Arena *arena) {
+  if (sel_start < 0 || sel_end < 0 || sel_start >= sel_end) return NULL;
+
+  int len = sel_end - sel_start;
+  char *result = Dowa_Arena_Allocate(arena, len + 1);
+  strncpy(result, text + sel_start, len);
+  result[len] = '\0';
+  return result;
+}
+
+boolean GuiTextArea(int id, Rectangle bounds, char *text, int text_size, boolean is_edit_mode,
+          boolean should_text_wrap, boolean readonly, Dowa_Arena *arena) {
+  boolean should_toggle_edit_mode = FALSE;
+
+  // Get or create state for this text area
+  TextAreaState *state = GetTextAreaState(id);
+  if (!state) {
+    state = CreateTextAreaState(id);
+    state->cursor_pos = readonly ? 0 : strlen(text);  // Readonly starts at beginning
+    state->last_blink_time = GetTime();
+    if (!readonly) PushUndoState(state, text, arena);
+  }
+
+  int text_len = strlen(text);
+  Vector2 mouse_pos = GetMousePosition();
+  boolean mouse_in_bounds = CheckCollisionPointRec(mouse_pos, bounds);
+
+  // Handle click to enter/exit edit mode (readonly still allows selection mode)
+  if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) {
+    if (mouse_in_bounds && !is_edit_mode) {
+      should_toggle_edit_mode = TRUE;
+    } else if (!mouse_in_bounds && is_edit_mode) {
+      should_toggle_edit_mode = TRUE;
+    }
+  }
+
+  // Content area - build/update line cache
+  float content_width = bounds.width - TEXT_AREA_PADDING * 2;
+  BuildLineCache(state, text, content_width, GuiGetStyle(DEFAULT, TEXT_SIZE), should_text_wrap, arena);
+
+  // Calculate content height from cache (O(1))
+  int total_lines = Dowa_Array_Length(state->line_cache);
+  float content_height = total_lines * TEXT_AREA_LINE_HEIGHT;
+  float visible_height = bounds.height - TEXT_AREA_PADDING * 2;
+  float max_scroll = TA_Max_Float(0, content_height - visible_height);
+
+  // Handle scrolling
+  float wheel = GetMouseWheelMove();
+  if (mouse_in_bounds && wheel != 0) {
+    state->scroll_offset_y -= wheel * TEXT_AREA_LINE_HEIGHT * 3;
+    state->scroll_offset_y = TA_Max_Float(0, TA_Min_Float(state->scroll_offset_y, max_scroll));
+  }
+
+  if (is_edit_mode) {
+    boolean ctrl_pressed = IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL) ||
+                 IsKeyDown(KEY_LEFT_SUPER) || IsKeyDown(KEY_RIGHT_SUPER);
+    boolean shift_pressed = IsKeyDown(KEY_LEFT_SHIFT) || IsKeyDown(KEY_RIGHT_SHIFT);
+    boolean text_changed = FALSE;
+
+    double current_time = GetTime();
+    if (current_time - state->last_blink_time > 0.5) {
+      state->cursor_visible = !state->cursor_visible;
+      state->last_blink_time = current_time;
+    }
+
+    // Mouse Selection (use line cache for accurate position)
+    if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT) && mouse_in_bounds) {
+      int click_pos = GetCharIndexFromPosWithCache(text, bounds, mouse_pos,
+                         state->scroll_offset_y, GuiGetStyle(DEFAULT, TEXT_SIZE),
+                         TEXT_AREA_LINE_HEIGHT, state->line_cache);
+      state->cursor_pos = click_pos;
+      state->selection_start = -1;
+      state->selection_end = -1;
+      state->is_selecting = TRUE;
+      state->cursor_visible = TRUE;
+      state->last_blink_time = current_time;
+    }
+
+    if (state->is_selecting && IsMouseButtonDown(MOUSE_BUTTON_LEFT)) {
+      int drag_pos = GetCharIndexFromPosWithCache(text, bounds, mouse_pos,
+                        state->scroll_offset_y, GuiGetStyle(DEFAULT, TEXT_SIZE),
+                        TEXT_AREA_LINE_HEIGHT, state->line_cache);
+      if (drag_pos != state->cursor_pos) {
+        if (state->selection_start < 0) {
+          state->selection_start = state->cursor_pos;
+        }
+        state->selection_end = drag_pos;
+      }
+    }
+
+    if (IsMouseButtonReleased(MOUSE_BUTTON_LEFT)) {
+      state->is_selecting = FALSE;
+      if (state->selection_start >= 0 && state->selection_end >= 0) {
+        if (state->selection_start > state->selection_end) {
+          int temp = state->selection_start;
+          state->selection_start = state->selection_end;
+          state->selection_end = temp;
+        }
+        if (state->selection_start == state->selection_end) {
+          state->selection_start = -1;
+          state->selection_end = -1;
+        }
+      }
+    }
+
+    // Ctrl+A: Select All
+    if (ctrl_pressed && IsKeyPressed(KEY_A)) {
+      state->selection_start = 0;
+      state->selection_end = text_len;
+      state->cursor_pos = text_len;
+    }
+
+    // Ctrl+C: Copy
+    if (ctrl_pressed && IsKeyPressed(KEY_C)) {
+      if (state->selection_start >= 0 && state->selection_end >= 0 &&
+        state->selection_start != state->selection_end) {
+        int sel_min = TA_Min_Int(state->selection_start, state->selection_end);
+        int sel_max = TA_Max_Int(state->selection_start, state->selection_end);
+        char *selected = GetSelectedText(text, sel_min, sel_max, arena);
+        if (selected) {
+          if (g_clipboard_text) free(g_clipboard_text);
+          g_clipboard_text = strdup(selected);
+          SetClipboardText(g_clipboard_text);
+        }
+      }
+    }
+
+    // Ctrl+X: Cut (not allowed in readonly mode)
+    if (!readonly && ctrl_pressed && IsKeyPressed(KEY_X)) {
+      if (state->selection_start >= 0 && state->selection_end >= 0 &&
+        state->selection_start != state->selection_end) {
+        int sel_min = TA_Min_Int(state->selection_start, state->selection_end);
+        int sel_max = TA_Max_Int(state->selection_start, state->selection_end);
+        char *selected = GetSelectedText(text, sel_min, sel_max, arena);
+        if (selected) {
+          if (g_clipboard_text) free(g_clipboard_text);
+          g_clipboard_text = strdup(selected);
+          SetClipboardText(g_clipboard_text);
+        }
+
+        PushUndoState(state, text, arena);
+        DeleteTextRange(text, sel_min, sel_max);
+        state->cursor_pos = sel_min;
+        state->selection_start = -1;
+        state->selection_end = -1;
+        text_changed = TRUE;
+      }
+    }
+
+    // Ctrl+V: Paste (not allowed in readonly mode)
+    if (!readonly && ctrl_pressed && IsKeyPressed(KEY_V)) {
+      const char *clipboard = GetClipboardText();
+      if (clipboard && strlen(clipboard) > 0) {
+        // Skip leading whitespace (spaces, tabs, newlines)
+        while (*clipboard && (*clipboard == ' ' || *clipboard == '\t' ||
+               *clipboard == '\n' || *clipboard == '\r')) {
+          clipboard++;
+        }
+
+        if (strlen(clipboard) > 0) {
+          if (state->selection_start >= 0 && state->selection_end >= 0 &&
+            state->selection_start != state->selection_end) {
+            int sel_min = TA_Min_Int(state->selection_start, state->selection_end);
+            int sel_max = TA_Max_Int(state->selection_start, state->selection_end);
+            PushUndoState(state, text, arena);
+            DeleteTextRange(text, sel_min, sel_max);
+            state->cursor_pos = sel_min;
+            state->selection_start = -1;
+            state->selection_end = -1;
+          } else {
+            PushUndoState(state, text, arena);
+          }
+
+          int new_cursor;
+          InsertTextAtCursor(text, text_size, state->cursor_pos, clipboard, &new_cursor);
+          state->cursor_pos = new_cursor;
+          text_changed = TRUE;
+        }
+      }
+    }
+
+    // Ctrl+Z: Undo / Ctrl+Shift+Z: Redo (not allowed in readonly mode)
+    if (!readonly && ctrl_pressed && IsKeyPressed(KEY_Z)) {
+      if (shift_pressed) {
+        PerformRedo(state, text, text_size);
+      } else {
+        PerformUndo(state, text, text_size);
+      }
+    }
+
+    // Arrow Keys Navigation
+    if (IsKeyPressed(KEY_LEFT) || IsKeyPressedRepeat(KEY_LEFT)) {
+      if (state->selection_start >= 0 && !shift_pressed) {
+        state->cursor_pos = TA_Min_Int(state->selection_start, state->selection_end);
+        state->selection_start = -1;
+        state->selection_end = -1;
+      } else if (state->cursor_pos > 0) {
+        if (shift_pressed) {
+          if (state->selection_start < 0) {
+            state->selection_start = state->cursor_pos;
+            state->selection_end = state->cursor_pos;
+          }
+          state->cursor_pos--;
+          state->selection_end = state->cursor_pos;
+        } else {
+          state->cursor_pos--;
+        }
+      }
+      state->cursor_visible = TRUE;
+      state->last_blink_time = current_time;
+    }
+
+    if (IsKeyPressed(KEY_RIGHT) || IsKeyPressedRepeat(KEY_RIGHT)) {
+      if (state->selection_start >= 0 && !shift_pressed) {
+        state->cursor_pos = TA_Max_Int(state->selection_start, state->selection_end);
+        state->selection_start = -1;
+        state->selection_end = -1;
+      } else if (state->cursor_pos < text_len) {
+        if (shift_pressed) {
+          if (state->selection_start < 0) {
+            state->selection_start = state->cursor_pos;
+            state->selection_end = state->cursor_pos;
+          }
+          state->cursor_pos++;
+          state->selection_end = state->cursor_pos;
+        } else {
+          state->cursor_pos++;
+        }
+      }
+      state->cursor_visible = TRUE;
+      state->last_blink_time = current_time;
+    }
+
+    if (IsKeyPressed(KEY_UP) || IsKeyPressedRepeat(KEY_UP)) {
+      int line, col;
+      GetLineAndColumn(text, state->cursor_pos, &line, &col);
+      if (line > 0) {
+        int new_pos = GetPosFromLineColumn(text, line - 1, col);
+        if (shift_pressed) {
+          if (state->selection_start < 0) {
+            state->selection_start = state->cursor_pos;
+            state->selection_end = state->cursor_pos;
+          }
+          state->selection_end = new_pos;
+        } else {
+          state->selection_start = -1;
+          state->selection_end = -1;
+        }
+        state->cursor_pos = new_pos;
+      }
+      state->cursor_visible = TRUE;
+      state->last_blink_time = current_time;
+    }
+
+    if (IsKeyPressed(KEY_DOWN) || IsKeyPressedRepeat(KEY_DOWN)) {
+      int line, col;
+      GetLineAndColumn(text, state->cursor_pos, &line, &col);
+      int total_lines = CountLines(text);
+      if (line < total_lines - 1) {
+        int new_pos = GetPosFromLineColumn(text, line + 1, col);
+        if (shift_pressed) {
+          if (state->selection_start < 0) {
+            state->selection_start = state->cursor_pos;
+            state->selection_end = state->cursor_pos;
+          }
+          state->selection_end = new_pos;
+        } else {
+          state->selection_start = -1;
+          state->selection_end = -1;
+        }
+        state->cursor_pos = new_pos;
+      }
+      state->cursor_visible = TRUE;
+      state->last_blink_time = current_time;
+    }
+
+    // Home/End keys
+    if (IsKeyPressed(KEY_HOME)) {
+      int line_start = GetLineStart(text, state->cursor_pos);
+      if (shift_pressed) {
+        if (state->selection_start < 0) {
+          state->selection_start = state->cursor_pos;
+          state->selection_end = state->cursor_pos;
+        }
+        state->selection_end = line_start;
+      } else {
+        state->selection_start = -1;
+        state->selection_end = -1;
+      }
+      state->cursor_pos = line_start;
+    }
+
+    if (IsKeyPressed(KEY_END)) {
+      int line_end = GetLineEnd(text, state->cursor_pos);
+      if (shift_pressed) {
+        if (state->selection_start < 0) {
+          state->selection_start = state->cursor_pos;
+          state->selection_end = state->cursor_pos;
+        }
+        state->selection_end = line_end;
+      } else {
+        state->selection_start = -1;
+        state->selection_end = -1;
+      }
+      state->cursor_pos = line_end;
+    }
+
+    // Text Input (not allowed in readonly mode)
+    if (!readonly && !ctrl_pressed) {
+      int key = GetCharPressed();
+      while (key > 0) {
+        if (key >= 32 && key <= 126) {
+          if (state->selection_start >= 0 && state->selection_end >= 0 &&
+            state->selection_start != state->selection_end) {
+            int sel_min = TA_Min_Int(state->selection_start, state->selection_end);
+            int sel_max = TA_Max_Int(state->selection_start, state->selection_end);
+            PushUndoState(state, text, arena);
+            DeleteTextRange(text, sel_min, sel_max);
+            state->cursor_pos = sel_min;
+            state->selection_start = -1;
+            state->selection_end = -1;
+            text_changed = TRUE;
+          } else if (!text_changed) {
+            PushUndoState(state, text, arena);
+          }
+
+          char insert_str[2] = {(char)key, '\0'};
+          int new_cursor;
+          InsertTextAtCursor(text, text_size, state->cursor_pos, insert_str, &new_cursor);
+          state->cursor_pos = new_cursor;
+          text_changed = TRUE;
+        }
+        key = GetCharPressed();
+      }
+    } else if (readonly) {
+      // Consume key presses to prevent them from being handled elsewhere
+      while (GetCharPressed() > 0) {}
+    }
+
+    // Enter key (not allowed in readonly mode)
+    if (!readonly && (IsKeyPressed(KEY_ENTER) || IsKeyPressed(KEY_KP_ENTER))) {
+      if (state->selection_start >= 0 && state->selection_end >= 0 &&
+        state->selection_start != state->selection_end) {
+        int sel_min = TA_Min_Int(state->selection_start, state->selection_end);
+        int sel_max = TA_Max_Int(state->selection_start, state->selection_end);
+        PushUndoState(state, text, arena);
+        DeleteTextRange(text, sel_min, sel_max);
+        state->cursor_pos = sel_min;
+        state->selection_start = -1;
+        state->selection_end = -1;
+      } else {
+        PushUndoState(state, text, arena);
+      }
+
+      int new_cursor;
+      InsertTextAtCursor(text, text_size, state->cursor_pos, "\n", &new_cursor);
+      state->cursor_pos = new_cursor;
+      text_changed = TRUE;
+    }
+
+    // Backspace (not allowed in readonly mode)
+    if (!readonly && (IsKeyPressed(KEY_BACKSPACE) || IsKeyPressedRepeat(KEY_BACKSPACE))) {
+      if (state->selection_start >= 0 && state->selection_end >= 0 &&
+        state->selection_start != state->selection_end) {
+        int sel_min = TA_Min_Int(state->selection_start, state->selection_end);
+        int sel_max = TA_Max_Int(state->selection_start, state->selection_end);
+        PushUndoState(state, text, arena);
+        DeleteTextRange(text, sel_min, sel_max);
+        state->cursor_pos = sel_min;
+        state->selection_start = -1;
+        state->selection_end = -1;
+        text_changed = TRUE;
+      } else if (state->cursor_pos > 0) {
+        PushUndoState(state, text, arena);
+        DeleteTextRange(text, state->cursor_pos - 1, state->cursor_pos);
+        state->cursor_pos--;
+        text_changed = TRUE;
+      }
+    }
+
+    // Delete key (not allowed in readonly mode)
+    if (!readonly && (IsKeyPressed(KEY_DELETE) || IsKeyPressedRepeat(KEY_DELETE))) {
+      text_len = strlen(text);
+      if (state->selection_start >= 0 && state->selection_end >= 0 &&
+        state->selection_start != state->selection_end) {
+        int sel_min = TA_Min_Int(state->selection_start, state->selection_end);
+        int sel_max = TA_Max_Int(state->selection_start, state->selection_end);
+        PushUndoState(state, text, arena);
+        DeleteTextRange(text, sel_min, sel_max);
+        state->cursor_pos = sel_min;
+        state->selection_start = -1;
+        state->selection_end = -1;
+        text_changed = TRUE;
+      } else if (state->cursor_pos < text_len) {
+        PushUndoState(state, text, arena);
+        DeleteTextRange(text, state->cursor_pos, state->cursor_pos + 1);
+        text_changed = TRUE;
+      }
+    }
+
+    // Tab key (not allowed in readonly mode)
+    if (!readonly && IsKeyPressed(KEY_TAB)) {
+      if (state->selection_start >= 0 && state->selection_end >= 0 &&
+        state->selection_start != state->selection_end) {
+        int sel_min = TA_Min_Int(state->selection_start, state->selection_end);
+        int sel_max = TA_Max_Int(state->selection_start, state->selection_end);
+        PushUndoState(state, text, arena);
+        DeleteTextRange(text, sel_min, sel_max);
+        state->cursor_pos = sel_min;
+        state->selection_start = -1;
+        state->selection_end = -1;
+      } else {
+        PushUndoState(state, text, arena);
+      }
+
+      int new_cursor;
+      InsertTextAtCursor(text, text_size, state->cursor_pos, "  ", &new_cursor);
+      state->cursor_pos = new_cursor;
+      text_changed = TRUE;
+    }
+
+    // Auto-scroll to keep cursor visible
+    // Rebuild cache if text changed during editing
+    if (text_changed) {
+      state->cache_valid = FALSE;
+      BuildLineCache(state, text, content_width, GuiGetStyle(DEFAULT, TEXT_SIZE), should_text_wrap, arena);
+    }
+    Vector2 cursor_screen = GetCursorScreenPosFromCache(text, state->cursor_pos, bounds,
+                          state->scroll_offset_y,
+                          GuiGetStyle(DEFAULT, TEXT_SIZE), TEXT_AREA_LINE_HEIGHT,
+                          state->line_cache);
+
+    float visible_top = bounds.y + TEXT_AREA_PADDING;
+    float visible_bottom = bounds.y + bounds.height - TEXT_AREA_PADDING - TEXT_AREA_LINE_HEIGHT;
+
+    if (cursor_screen.y < visible_top) {
+      state->scroll_offset_y -= visible_top - cursor_screen.y;
+    } else if (cursor_screen.y > visible_bottom) {
+      state->scroll_offset_y += cursor_screen.y - visible_bottom;
+    }
+
+    state->scroll_offset_y = TA_Max_Float(0, TA_Min_Float(state->scroll_offset_y, max_scroll));
+  }
+
+  // Drawing
+  DrawRectangleSelectiveRounded(bounds, 0.2, 1, g_colors.secondary, FALSE, FALSE, TRUE, TRUE);
+  DrawRectangleRec(AddPadding(bounds, 10), WHITE);
+  // DrawRectangleRec(bounds, is_edit_mode ? DARKGRAY : (Color){40, 40, 40, 255});
+  // DrawRectangleLinesEx(bounds, 1, is_edit_mode ? WHITE : GRAY);
+  // DrawRectangleRoundedLines(bounds, 0.2, 1, is_edit_mode ? BLACK : GRAY);
+
+  BeginScissorMode((int)bounds.x, (int)bounds.y, (int)bounds.width, (int)bounds.height);
+
+  float content_x = bounds.x + TEXT_AREA_PADDING;
+  float content_y = bounds.y + TEXT_AREA_PADDING - state->scroll_offset_y;
+
+  text_len = strlen(text);
+
+  // Calculate visible line range (only render what's visible)
+  int first_visible_line = (int)(state->scroll_offset_y / TEXT_AREA_LINE_HEIGHT);
+  int last_visible_line = first_visible_line + (int)(visible_height / TEXT_AREA_LINE_HEIGHT) + 2;
+  int line_count = Dowa_Array_Length(state->line_cache);
+  if (first_visible_line < 0) first_visible_line = 0;
+  if (last_visible_line > line_count) last_visible_line = line_count;
+
+  // Draw selection highlight (only for visible lines)
+  if (state->selection_start >= 0 && state->selection_end >= 0 &&
+    state->selection_start != state->selection_end) {
+    int sel_min = TA_Min_Int(state->selection_start, state->selection_end);
+    int sel_max = TA_Max_Int(state->selection_start, state->selection_end);
+
+    for (int line_idx = first_visible_line; line_idx < last_visible_line; line_idx++) {
+      LineInfo *line = &state->line_cache[line_idx];
+
+      // Check if selection intersects this line
+      if (sel_min < line->end_pos && sel_max > line->start_pos) {
+        int highlight_start = TA_Max_Int(sel_min, line->start_pos);
+        int highlight_end = TA_Min_Int(sel_max, line->end_pos);
+
+        float x1 = content_x + MeasureTextRange(text, line->start_pos, highlight_start, GuiGetStyle(DEFAULT, TEXT_SIZE));
+        float x2 = content_x + MeasureTextRange(text, line->start_pos, highlight_end, GuiGetStyle(DEFAULT, TEXT_SIZE));
+        float y = content_y + line_idx * TEXT_AREA_LINE_HEIGHT;
+
+        DrawRectangle((int)x1, (int)y, (int)(x2 - x1), TEXT_AREA_LINE_HEIGHT,
+                Fade(SKYBLUE, 0.5f));
+      }
+    }
+  }
+
+  // Draw text using line cache (only visible lines)
+  Color text_color = should_text_wrap ? BLACK : WHITE;
+  for (int line_idx = first_visible_line; line_idx < last_visible_line; line_idx++) {
+    LineInfo *line = &state->line_cache[line_idx];
+
+    if (line->char_count > 0) {
+      char line_buffer[1024];
+      int line_len = TA_Min_Int(line->end_pos - line->start_pos, 1023);
+      strncpy(line_buffer, text + line->start_pos, line_len);
+      line_buffer[line_len] = '\0';
+
+      Vector2 draw_text_vector = {
+        .x = content_x,
+        .y = content_y + line_idx * TEXT_AREA_LINE_HEIGHT
+      };
+      DrawTextEx(GuiGetFont(), line_buffer, draw_text_vector,
+          GuiGetStyle(DEFAULT, TEXT_SIZE), TEXT_SIZE_DEFAULT/GuiGetStyle(DEFAULT, TEXT_SIZE), text_color);
+    }
+  }
+
+  // Draw cursor
+  if (is_edit_mode && state->cursor_visible) {
+    Vector2 cursor_pos = GetCursorScreenPosFromCache(text, state->cursor_pos, bounds,
+                         state->scroll_offset_y,
+                         GuiGetStyle(DEFAULT, TEXT_SIZE), TEXT_AREA_LINE_HEIGHT,
+                         state->line_cache);
+
+    DrawRectangle((int)cursor_pos.x, (int)cursor_pos.y,
+            TEXT_AREA_CURSOR_WIDTH, TEXT_AREA_LINE_HEIGHT, BLACK);
+  }
+
+  EndScissorMode();
+
+  // Draw scrollbar if needed
+  if (max_scroll > 0) {
+    float scrollbar_height = (visible_height / content_height) * visible_height;
+    float scrollbar_y = bounds.y + TEXT_AREA_PADDING +
+               (state->scroll_offset_y / max_scroll) * (visible_height - scrollbar_height);
+
+    DrawRectangle((int)(bounds.x + bounds.width - 8), (int)scrollbar_y,
+            6, (int)scrollbar_height, Fade(WHITE, 0.3f));
+  }
+
+  return should_toggle_edit_mode;
+}
+
+void GuiTextAreaResetState(int id) {
+  TextAreaState *state = GetTextAreaState(id);
+  if (state) {
+    if (state->undo_stack) {
+      Dowa_Array_Free(state->undo_stack);
+      state->undo_stack = NULL;
+    }
+    if (state->line_cache) {
+      Dowa_Array_Free(state->line_cache);
+      state->line_cache = NULL;
+    }
+    state->cache_valid = FALSE;
+    state->is_initialized = FALSE;
+  }
+}
+
+void GuiTextAreaResetAllStates(void) {
+  for (int i = 0; i < g_text_area_state_count; i++) {
+    if (g_text_area_states[i].undo_stack) {
+      Dowa_Array_Free(g_text_area_states[i].undo_stack);
+      g_text_area_states[i].undo_stack = NULL;
+    }
+    if (g_text_area_states[i].line_cache) {
+      Dowa_Array_Free(g_text_area_states[i].line_cache);
+      g_text_area_states[i].line_cache = NULL;
+    }
+    g_text_area_states[i].cache_valid = FALSE;
+    g_text_area_states[i].is_initialized = FALSE;
+  }
+  g_text_area_state_count = 0;
+}
+
+// ============================================================================
+// End TextArea Component
+// ============================================================================
 
 typedef Dowa_KV(char*, char*) INPUT_HASHMAP;
 
@@ -46,7 +1207,7 @@
 
 typedef struct {
   char *filename;
-  char *title; 
+  char *title;
   Rectangle rect;
   long time_modified;
   boolean deleted;
@@ -62,19 +1223,42 @@
   TAB_HEADER = 0,
   TAB_BODY,
   TAB_GET_PARAMS,
-  TAB_BAR,
+  TAB_WEBSOCKET,
   TAB_LENGTH
 } PostDog_Tab_Enum;
 
+typedef enum {
+  RESULT_TAB_BODY = 0,
+  RESULT_TAB_HEADERS,
+  RESULT_TAB_LENGTH
+} PostDog_ResultTab_Enum;
+
+// Text area IDs
+#define TEXT_AREA_ID_INPUT_HEADER   1
+#define TEXT_AREA_ID_INPUT_BODY   2
+#define TEXT_AREA_ID_INPUT_PARAMS   3
+#define TEXT_AREA_ID_INPUT_WS     4
+#define TEXT_AREA_ID_RESULT_BODY    5
+#define TEXT_AREA_ID_RESULT_HEADERS 6
+
 static uint32 counter = 0;
+static uv_mutex_t history_mutex;
+static uv_loop_t *main_loop = NULL;
 HistoryItem *history_items = NULL;
 HistoryItem *new_history_items = NULL;
 
 // Global UI state
 char *url_input_text = NULL;
-char *url_result_text = NULL;
-char **url_body_map = NULL;
+char **result_body_array = NULL;  // [RESULT_TAB_BODY, RESULT_TAB_HEADERS]
+char **input_body_array = NULL;
 int active_method_dropdown = 0;
+int active_input_tab = 0;
+int active_result_tab = 0;
+Seobeo_WebSocket *ws = NULL;
+boolean WS_BREAK = FALSE;
+uv_thread_t websocket_thread_id;
+Color TEXT_COLOR = BLACK;
+boolean LOADING = FALSE;
 
 int CompareHistoryItemsByDate(const void *a, const void *b) {
   HistoryItem *itemA = (HistoryItem *)a;
@@ -88,13 +1272,18 @@
   snprintf(full_file_path, 512, "%s/%s", POSTDOG_PATHS, filename);
   FILE *file = fopen(full_file_path, "r");
   if (!file)
-    return strdup(filename);
+  return strdup(filename);
 
   char *title = malloc(sizeof(char) * 512);
   if (!fgets(title, 512, file)) {
-    fclose(file);
-    return strdup(filename);
+  fclose(file);
+  free(title);
+  return strdup(filename);
   }
+  fclose(file);
+
+  // Strip trailing newline
+  title[strcspn(title, "\n")] = '\0';
 
   return title;
 }
@@ -131,24 +1320,28 @@
   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.title = PostDog_Extract_Title(entry->d_name);
-      item.time_modified = file_stat.st_mtime;
-      item.deleted = FALSE;
-      Dowa_Array_Push(file_arr, item);
-    }
+  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.title = PostDog_Extract_Title(entry->d_name);
+    item.time_modified = file_stat.st_mtime;
+    item.deleted = FALSE;
+    Dowa_Array_Push(file_arr, item);
+  }
   }
   closedir(dp);
 #endif
+
+  // Update the caller's pointer in case array was reallocated
+  *p_file_arr = file_arr;
+
   int count = Dowa_Array_Length(file_arr);
   if (count > 1) {
-    qsort(file_arr, count, sizeof(HistoryItem), CompareHistoryItemsByDate);
+  qsort(file_arr, count, sizeof(HistoryItem), CompareHistoryItemsByDate);
   }
 }
 
@@ -161,6 +1354,7 @@
       return -1;
     return 0;
   }
+
   printf("Directory '%s' already exists.\n", POSTDOG_PATHS);
   PostDog_List_Directory(POSTDOG_PATHS, p_history_files);
   return 0;
@@ -169,10 +1363,10 @@
 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
+  mouse_position.x >= area.x &&
+  mouse_position.x < area.x + area.width &&
+  mouse_position.y >= area.y &&
+  mouse_position.y < area.y + area.height
   );
 }
 
@@ -181,37 +1375,39 @@
   return (InArea(mouse_position, area) && IsMouseButtonPressed(MOUSE_BUTTON_LEFT));
 }
 
+// -------- END of UI ---- //
+
 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";
+  case 0: return "GET";
+  case 1: return "POST";
+  case 2: return "PUT";
+  case 3: return "DELETE";
   }
   return 0;
 }
 
-char *PostDog_Construct_URL(char *filename)
+char *PostDog_Construct_URL(char *filename, char *out_buffer, size_t buffer_size)
 {
-  char full_file_path[512] = {0};
-  snprintf(full_file_path, 512, "%s/%s", POSTDOG_PATHS, filename);
-  return &full_file_path;
+  snprintf(out_buffer, buffer_size, "%s/%s", POSTDOG_PATHS, filename);
+  return out_buffer;
 }
 
-void PostDog_History_CreateFile(char *filename, char* values)
+boolean 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;
+  fprintf(stderr, "Failed to create a file: %s\n", full_file_path);
+  return FALSE;
   }
   fwrite(values, 1, strlen(values), file);
   fclose(file);
+  return TRUE;
 }
 
 void PostDog_Request_SaveFile(void)
@@ -220,237 +1416,350 @@
   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);
-  char *title = malloc(strlen(method) + strlen(url_input_text) + 2);
+  char *title = Dowa_Arena_Allocate(arena, strlen(method) + strlen(url_input_text) + 2);
   sprintf(title, "%s %s", method, url_input_text);
   snprintf(
-      new_file,
-      new_file_size,
-      "%s\n"
-      "---\n"
-      "%s\n"
-      "---\n"
-      "%s\n"
-      "---\n"
-      "%s\n"
-      "---\n"
-      "%s\n"
-      "---\n"
-      "%s\n"
-      "---\n"
-      "%s\n"
-      "---\n"
-      "%s\n",
-      title,
-      url_input_text,
-      method,
-      url_body_map[TAB_HEADER],
-      url_body_map[TAB_BODY],
-      url_body_map[TAB_GET_PARAMS],
-      url_body_map[TAB_BAR],
-      url_result_text
+    new_file,
+    new_file_size,
+    "%s\n"
+    "---\n"
+    "%s\n"
+    "---\n"
+    "%s\n"
+    "---\n"
+    "%s\n"
+    "---\n"
+    "%s\n"
+    "---\n"
+    "%s\n"
+    "---\n"
+    "%s\n"
+    "---\n"
+    "%s\n",
+    title,
+    url_input_text,
+    method,
+    input_body_array[TAB_HEADER],
+    input_body_array[TAB_BODY],
+    input_body_array[TAB_GET_PARAMS],
+    input_body_array[TAB_WEBSOCKET],
+    result_body_array[RESULT_TAB_BODY]
   );
   char *filename = Dowa_Arena_Allocate(arena, 1024);
   if (!filename)
   {
-    perror("Error opening file");
-    exit(EXIT_FAILURE); 
+  perror("Error opening file");
+  exit(EXIT_FAILURE);
   }
   char *uuid4 = (char *)Dowa_Arena_Allocate(arena, 37);
   if (!uuid4)
   {
-    perror("Error uuid");
-    exit(EXIT_FAILURE); 
+  perror("Error uuid");
+  exit(EXIT_FAILURE);
   }
-  
+
+  uv_mutex_lock(&history_mutex);
+
   int32 seed = (uint32)time(NULL) ^ counter++;
   Dowa_String_UUID(seed, uuid4);
   snprintf(filename, 1024, "%s.txt", uuid4);
-  PostDog_History_CreateFile(filename, new_file);
 
+  if (PostDog_History_CreateFile(filename, new_file))
+  {
   HistoryItem item = {0};
   item.filename = strdup(filename);
-  item.title = title;
+  item.title = strdup(title);
   item.deleted = FALSE;
+
   Dowa_Array_Push(new_history_items, item);
-  
+  }
+
+  uv_mutex_unlock(&history_mutex);
   Dowa_Arena_Free(arena);
 }
 
-static size_t Postdog_Curl_Callback(void *contents, size_t size, size_t nmemb, void *userp)
+void PostDog_Websocket_Listen(void)
 {
-  size_t real_size = size * nmemb;
-  ResponseBuffer *buf = (ResponseBuffer *)userp;
+  while (TRUE)
+  {
+    if (WS_BREAK) break;
+
+    Seobeo_WebSocket_Message *p_msg = Seobeo_WebSocket_Receive(ws);
+    if (p_msg)
+    {
+      if (p_msg->opcode == SEOBEO_WS_OPCODE_TEXT)
+      {
+        printf("Response: %.*s\n", (int)p_msg->length, (char*)p_msg->data);
+        char *body = result_body_array[RESULT_TAB_BODY];
+        size_t current_len = strlen(body);
+        snprintf(body + current_len, RESULT_BUFFER_LENGTH - current_len,
+          "\n%s", (char*)p_msg->data);
+      }
+      Seobeo_WebSocket_Message_Destroy(p_msg);
+    }
+    usleep(10000);
+  }
+  return;
+}
+
+void PostDog_Websocket_Connect(void)
+{
+  // Pass headers from the Headers tab when connecting
+  const char *headers = input_body_array[TAB_HEADER];
+  if (headers && strlen(headers) > 0)
+    ws = Seobeo_WebSocket_Connect_With_Headers(url_input_text, headers);
+  else
+    ws = Seobeo_WebSocket_Connect(url_input_text);
+
+  result_body_array[RESULT_TAB_BODY][0] = '\0';
+  result_body_array[RESULT_TAB_HEADERS][0] = '\0';
 
-  char *ptr = realloc(buf->data, buf->size + real_size + 1);
-  if (ptr == NULL)
+  // Reset result text area states
+  GuiTextAreaResetState(TEXT_AREA_ID_RESULT_BODY);
+  GuiTextAreaResetState(TEXT_AREA_ID_RESULT_HEADERS);
+}
+
+int PostDog_Websocket_Send(void)
+{
+  char *body = result_body_array[RESULT_TAB_BODY];
+  if (Seobeo_WebSocket_Send_Text(ws, input_body_array[active_input_tab]) < 0)
   {
-    printf("Not enough memory for response\n");
-    return 0;
+    snprintf(body + strlen(body), RESULT_BUFFER_LENGTH - strlen(body),
+      "Failed to send message\n");
+    return -1;
+  }
+  snprintf(body + strlen(body), RESULT_BUFFER_LENGTH - strlen(body),
+    "\n%s", input_body_array[active_input_tab]);
+  return 0;
+}
+
+void PostDog_Websocket_Destroy(uv_thread_t thread_id)
+{
+  Seobeo_WebSocket_Destroy(ws);
+  uv_thread_join(&thread_id);
+}
+
+void PostDog_Websocket_Start(void *arg)
+{
+  PostDog_Websocket_Connect();
+  PostDog_Websocket_Listen();
+}
+
+uv_thread_t PostDog_Websocket_Start_Thread()
+{
+  uv_thread_t thread_id;
+
+  if (uv_thread_create(&thread_id, PostDog_Websocket_Start, NULL) != 0)
+  {
+    perror("Failed to create thread");
+    memset(&thread_id, 0, sizeof(thread_id));
+    return thread_id;
   }
 
-  buf->data = ptr;
-  memcpy(&(buf->data[buf->size]), contents, real_size);
-  buf->size += real_size;
-  buf->data[buf->size] = 0;
-
-  return real_size;
+  return thread_id;
 }
 
 int PostDog_Http_Request(void)
 {
-  const char *method = PostDog_Enum_To_String(active_method_dropdown);
-  CURL *curl;
-  CURLcode res;
-  ResponseBuffer buffer = { .data = malloc(1), .size = 0 };
-
-  url_result_text[0] = '\n';
-
-  if (buffer.data == NULL)
+  Seobeo_Client_Request *req = Seobeo_Client_Request_Create(url_input_text);
+  printf("URL: %s\n", url_input_text);
+  Seobeo_Client_Response *res;
+  switch (active_method_dropdown)
   {
-    snprintf(url_result_text, RESULT_BUFFER_LENGTH, "Error: Failed to allocate memory");
-    return -1;
-  }
-  buffer.data[0] = '\0';
-
-  curl_global_init(CURL_GLOBAL_DEFAULT);
-  curl = curl_easy_init();
-
-  if (curl)
-  {
-    struct curl_slist *headerList = NULL;
-
-    // Set URL
-    curl_easy_setopt(curl, CURLOPT_URL, url_input_text);
-
-    // Set HTTP method
-    if (strcmp(method, "POST") == 0)
+    case 0:
+    {
+      Seobeo_Client_Request_Set_Method(req, "GET");
+      break;
+    }
+    case 1:
+    {
+      Seobeo_Client_Request_Set_Method(req, "POST");
+      break;
+    }
+    case 2:
     {
-      curl_easy_setopt(curl, CURLOPT_POST, 1L);
-      if (url_body_map[TAB_BODY] && strlen(url_body_map[TAB_BODY]) > 0)
-        curl_easy_setopt(curl, CURLOPT_POSTFIELDS, url_body_map[TAB_BODY]);
+      Seobeo_Client_Request_Set_Method(req, "PUT");
+      break;
     }
-    else if (strcmp(method, "PUT") == 0)
+    case 3:
     {
-      curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PUT");
-      if (url_body_map[TAB_BODY] && strlen(url_body_map[TAB_BODY]) > 0)
-        curl_easy_setopt(curl, CURLOPT_POSTFIELDS, url_body_map[TAB_BODY]);
-    }
-    else if (strcmp(method, "DELETE") == 0)
-    {
-      curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE");
+      Seobeo_Client_Request_Set_Method(req, "DELETE");
+      break;
     }
-    // Default is GET
+  }
 
-    // Parse and add headers
-    if (url_body_map[TAB_HEADER] && strlen(url_body_map[TAB_HEADER]) > 0)
+  if (input_body_array[TAB_HEADER] && strlen(input_body_array[TAB_HEADER]) > 0)
+  {
+    char *headersCopy = strdup(input_body_array[TAB_HEADER]);
+    char *line = strtok(headersCopy, "\n");
+    while (line != NULL)
     {
-      char *headersCopy = strdup(url_body_map[TAB_HEADER]);
-      char *line = strtok(headersCopy, "\n");
-      while (line != NULL) {
-        while (*line == ' ' || *line == '\t') line++;
-        if (strlen(line) > 0)
-          headerList = curl_slist_append(headerList, line);
-        line = strtok(NULL, "\n");
-      }
-      curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headerList);
-      free(headersCopy);
+      while (*line == ' ' || *line == '\t') line++;
+      if (strlen(line) > 0)
+        Seobeo_Client_Request_Add_Header_Array(req, line);
+      line = strtok(NULL, "\n");
     }
 
-    // Set write callback
-    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, Postdog_Curl_Callback);
-    curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&buffer);
+  }
+  Seobeo_Client_Request_Set_Follow_Redirects(req, TRUE, 5); // TODO: remove magic number;
+  res = Seobeo_Client_Request_Execute(req);
 
-    // Follow redirects
-    curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
-    curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30L);
-    res = curl_easy_perform(curl);
-
-    if (res != CURLE_OK)
-      snprintf(url_result_text, RESULT_BUFFER_LENGTH, "Error: %s\n", curl_easy_strerror(res));
-    else
-    {
-      long response_code;
-      curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code);
+  if (res == NULL) {
+    snprintf(result_body_array[RESULT_TAB_BODY], RESULT_BUFFER_LENGTH, "Error: Failed to send the request\n");
+    result_body_array[RESULT_TAB_HEADERS][0] = '\0';
+  } else {
+    // Store response body
+    snprintf(result_body_array[RESULT_TAB_BODY], RESULT_BUFFER_LENGTH, "%s",
+         res->body ? res->body : "");
+    // Sanitize body for display (replace tabs, remove non-printable chars)
+    SanitizeTextForDisplay(result_body_array[RESULT_TAB_BODY]);
 
-      if (buffer.size > RESULT_BUFFER_LENGTH)
-        printf("TODO: Realloc\n");
-
-      snprintf(url_result_text, RESULT_BUFFER_LENGTH, "HTTP Status: %ld\n\n%s",
-               response_code, buffer.data ? buffer.data : "");
+    // Store response headers
+    int offset = snprintf(result_body_array[RESULT_TAB_HEADERS], RESULT_BUFFER_LENGTH,
+                          "HTTP Status: %d %s\n\n",
+                          res->status_code, res->status_text ? res->status_text : "");
+    if (res->headers) {
+      size_t header_count = Dowa_Array_Length(res->headers);
+      for (size_t i = 0; i < header_count && offset < RESULT_BUFFER_LENGTH - 100; i++) {
+        offset += snprintf(result_body_array[RESULT_TAB_HEADERS] + offset,
+                          RESULT_BUFFER_LENGTH - offset,
+                          "%s: %s\n", res->headers[i].key, res->headers[i].value);
+      }
     }
+  }
+  printf("Body: %s\n", res ? res->body : "NULL");
+  Seobeo_Client_Request_Destroy(req);
+  Seobeo_Client_Response_Destroy(res);
+  PostDog_Request_SaveFile();
 
-    if (headerList) curl_slist_free_all(headerList);
-    curl_easy_cleanup(curl);
-  }
-  else
-    snprintf(url_result_text, RESULT_BUFFER_LENGTH, "Error: Failed to initialize curl");
+  // Reset result text area states so scroll/cursor don't carry over
+  GuiTextAreaResetState(TEXT_AREA_ID_RESULT_BODY);
+  GuiTextAreaResetState(TEXT_AREA_ID_RESULT_HEADERS);
 
-  free(buffer.data);
-  curl_global_cleanup();
-
-  PostDog_Request_SaveFile();
   return 0;
 }
 
-void PostDog_Update_URL(void)
+void PostDog_Http_Work(uv_work_t *req)
 {
-  // Save existing query string if present
-  char *question_mark = strchr(url_input_text, '?');
-  if (question_mark)
-    *question_mark = '\0';
+  PostDog_Http_Request();
+  printf("HTTP request finished.\n");
+}
+
+void PostDog_Http_Work_Done(uv_work_t *req, int status)
+{
+  LOADING = FALSE;
+  free(req);
+}
 
-  int get_params_length = (int)strlen(url_body_map[TAB_GET_PARAMS]);
-  if (get_params_length == 0)
-    return;
+void PostDog_Http_Thread_Request()
+{
+  uv_work_t *work_req = malloc(sizeof(uv_work_t));
+  if (!work_req)
+  {
+  perror("Failed to allocate work request");
+  return;
+  }
+  LOADING = TRUE;
+  if (uv_queue_work(main_loop, work_req, PostDog_Http_Work, PostDog_Http_Work_Done) != 0)
+  {
+  perror("Failed to queue work");
+  free(work_req);
+  LOADING = FALSE;
+  }
+}
 
-  char *separator = "?";
-
-  Dowa_Arena *arena = Dowa_Arena_Create(1024*1024);
-  char **lines = Dowa_String_Split(url_body_map[TAB_GET_PARAMS], "\n", get_params_length, 1, arena);
-  for (int i = 0; i < Dowa_Array_Length(lines); i++)
+void PostDog_Update_URL(boolean is_url_updated)
+{
+  char *params_start = strchr(url_input_text, '?');
+  if (is_url_updated)
   {
-    char *line = lines[i];
-    char **key_value = Dowa_String_Split(line, " ", (int)strlen(line), 1, arena);
+    if (!params_start)
+      return;
+    params_start++;
+    Dowa_Arena *arena = Dowa_Arena_Create(1024*1024);
 
-    if (Dowa_Array_Length(key_value) < 2)
-      break;
+    char buffer[4096] = "";
+    char **lines = Dowa_String_Split(params_start, "&", URL_TEXT_BUFFER_LENGTH, 1, arena);
+    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);
 
-    strcat(url_input_text, separator);
-    strcat(url_input_text, key_value[0]);
-    strcat(url_input_text, "=");
-    for (int i = 1; i < Dowa_Array_Length(key_value); i++)
-    {
-      if (!key_value[i] || key_value[i][0] == '\0')
+      if (Dowa_Array_Length(key_value) < 2)
         break;
-      if (i > 1) strcat(url_input_text, "%20");
-      strcat(url_input_text, key_value[i]);
+      snprintf(buffer + strlen(buffer), 4096 - strlen(buffer), "%s %s\n", key_value[0], key_value[1]);
+    }
+    snprintf(input_body_array[TAB_GET_PARAMS], URL_TEXT_BUFFER_LENGTH, "%s", buffer);
+    int length = strlen(input_body_array[TAB_GET_PARAMS]);
+    input_body_array[TAB_GET_PARAMS][length--] = '\0';
+    Dowa_Arena_Free(arena);
+  }
+  else
+  {
+    if (params_start)
+    {
+      size_t length = params_start - url_input_text;
+      url_input_text[length] = '\0';
     }
-    separator = "&";
+    int get_params_length = (int)strlen(input_body_array[TAB_GET_PARAMS]);
+    if (get_params_length == 0) 
+      return;
+
+    char *separator = "?";
+    Dowa_Arena *arena = Dowa_Arena_Create(1024*1024);
+    char **lines = Dowa_String_Split(input_body_array[TAB_GET_PARAMS], "\n", get_params_length, 1, arena);
+    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)
+        break;
+
+      strcat(url_input_text, separator);
+      strcat(url_input_text, key_value[0]);
+      strcat(url_input_text, "=");
+      for (int i = 1; i < Dowa_Array_Length(key_value); i++)
+      {
+        if (!key_value[i] || key_value[i][0] == '\0')
+        break;
+        if (i > 1) strcat(url_input_text, "%20");
+        strcat(url_input_text, key_value[i]);
+      }
+      separator = "&";
+    }
+
+    Dowa_Arena_Free(arena);
   }
-
-  Dowa_Arena_Free(arena);
 }
 
 int PostDog_String_To_MethodEnum(char *value)
 {
   if (strstr(value, "GET"))
-    return 0;
+  return 0;
   if (strstr(value, "POST"))
-    return 1;
+  return 1;
   if (strstr(value, "PUT"))
-    return 2;
+  return 2;
   if (strstr(value, "DELETE"))
-    return 3;
+  return 3;
   return 0;
 }
 
 void PostDog_Params_Reset(void)
 {
   url_input_text[0] = '\0';
-  url_result_text[0] = '\0';
   active_method_dropdown = 0;
-  for (int i = 0; i < Dowa_Array_Length(url_body_map); i++)
-    url_body_map[i][0] = '\0';
+  active_result_tab = 0;
+
+  for (int i = 0; i < Dowa_Array_Length(input_body_array); i++)
+    input_body_array[i][0] = '\0';
+
+  for (int i = 0; i < Dowa_Array_Length(result_body_array); i++)
+    result_body_array[i][0] = '\0';
+
+  // Reset text area states when clearing
+  GuiTextAreaResetAllStates();
 }
 
 void PostDog_Load_File(const char *filename)
@@ -459,7 +1768,7 @@
   snprintf(full_file_path, 512, "%s/%s", POSTDOG_PATHS, filename);
   FILE *file = fopen(full_file_path, "r");
   if (!file)
-    return;
+  return;
 
   fseek(file, 0, SEEK_END);
   size_t file_size = ftell(file);
@@ -478,493 +1787,625 @@
     switch (i)
     {
       case 0:  // Title - skip
-        break;
+      break;
 
       case 1:  // URL
-        snprintf(url_input_text, strlen(values[i]) + 1, "%s", values[i]);
-        url_input_text[strcspn(url_input_text, "\n")] = '\0';
-        break;
+      snprintf(url_input_text, strlen(values[i]) + 1, "%s", values[i]);
+      url_input_text[strcspn(url_input_text, "\n")] = '\0';
+      break;
 
       case 2:  // Method
-        active_method_dropdown = PostDog_String_To_MethodEnum(values[i]);
-        break;
+      active_method_dropdown = PostDog_String_To_MethodEnum(values[i]);
+      break;
 
       case 3:  // Headers (TAB_HEADER)
       case 4:  // Body (TAB_BODY)
       case 5:  // Get Params (TAB_GET_PARAMS)
-      case 6:  // Bar (TAB_BAR)
+      case 6:  // Websocket (TAB_WEBSOCKET)
       {
-        int map_index = i - 3;  // 3->0, 4->1, 5->2, 6->3
-        snprintf(url_body_map[map_index], strlen(values[i]) + 1, "%s", values[i]);
-        // Trim trailing newlines
-        for (int j = strlen(values[i]); j > 0; j--)
+      int map_index = i - 3;  // 3->0, 4->1, 5->2, 6->3
+      snprintf(input_body_array[map_index], strlen(values[i]) + 1, "%s", values[i]);
+      // Trim trailing newlines
+      for (int j = strlen(values[i]); j > 0; j--)
+      {
+        if (input_body_array[map_index][j] == '\n')
         {
-          if (url_body_map[map_index][j] == '\n')
-          {
-            url_body_map[map_index][j] = '\0';
-            break;
-          }
+        input_body_array[map_index][j] = '\0';
+        break;
         }
-        break;
+      }
+      break;
       }
 
-      default:  // Response (index 7+)
-        snprintf(url_result_text, strlen(values[i]) + 1, "%s", values[i]);
-        break;
+      default:  // Response (index 7+) - load into body tab for backward compatibility
+      snprintf(result_body_array[RESULT_TAB_BODY], strlen(values[i]) + 1, "%s", values[i]);
+      break;
     }
   }
 
+  // Reset result tab to body
+  active_result_tab = RESULT_TAB_BODY;
+
+  // Reset text area states when loading new file
+  GuiTextAreaResetAllStates();
+
   Dowa_Arena_Free(init_arena);
   Dowa_Arena_Free(split_arena);
 }
 
-Rectangle AddPadding(Rectangle rect, float padding)
+// ============================================================================
+// UI LAYOUT STRUCTURE - All rectangles for the UI
+// ============================================================================
+typedef struct {
+  // Main areas
+  Rectangle screen;
+  Rectangle sidebar;
+  Rectangle content;
+
+  // Sidebar sections
+  Rectangle logo_area;
+  Rectangle history_list;
+
+  // URL bar section
+  Rectangle url_bar;
+  Rectangle method_dropdown;
+  Rectangle url_input;
+  Rectangle send_button;
+
+  // Body section (input + result split)
+  Rectangle body_area;
+  Rectangle input_panel;
+  Rectangle result_panel;
+
+  // Input panel internals
+  Rectangle input_tabs;
+  Rectangle input_body;
+
+  // Result panel internals
+  Rectangle result_tabs;
+  Rectangle result_body;
+} UILayout;
+
+// ============================================================================
+// UI STATE - All interactive state
+// ============================================================================
+typedef struct {
+  boolean url_edit_mode;
+  boolean method_edit_mode;
+  boolean input_body_edit_mode;
+  boolean result_body_edit_mode;
+  int prev_input_tab;
+  float history_scroll_offset;
+} UIState;
+
+// ============================================================================
+// LAYOUT CALCULATION FUNCTIONS
+// ============================================================================
+static UILayout CalculateLayout(int screen_width, int screen_height, float padding)
 {
-  return (Rectangle){
-    rect.x + padding,
-    rect.y + padding,
-    rect.width - (2 * padding),
-    rect.height - (2 * padding)
+  UILayout layout = {0};
+
+  // Screen
+  layout.screen = (Rectangle){0, 0, screen_width, screen_height};
+
+  // Main split: sidebar (20%) | content (80%)
+  layout.sidebar = LeftColumn(layout.screen, 0.20f, 0);
+  layout.content = RightColumn(layout.screen, layout.sidebar, 0);
+
+  // Sidebar sections
+  layout.logo_area = (Rectangle){
+    .x = layout.sidebar.x,
+    .y = layout.sidebar.y,
+    .width = layout.sidebar.width,
+    .height = 120
+  };
+
+  layout.history_list = (Rectangle){
+    .x = layout.sidebar.x + padding,
+    .y = layout.logo_area.y + layout.logo_area.height + padding,
+    .width = layout.sidebar.width - (2 * padding),
+    .height = layout.sidebar.height - layout.logo_area.height - (2 * padding)
+  };
+
+  // URL bar section (top of content area)
+  float url_bar_height = 60;
+  layout.url_bar = (Rectangle){
+    .x = layout.content.x,
+    .y = layout.content.y + padding,
+    .width = layout.content.width,
+    .height = url_bar_height
   };
-}
+
+  // URL bar components (method dropdown + URL input + send button)
+  float dropdown_width = 100;
+  float button_width = 80;
+  float url_bar_inner_padding = 8;
+  float control_height = 36;
+  float control_y = layout.url_bar.y + (layout.url_bar.height - control_height) / 2;
+
+  layout.method_dropdown = (Rectangle){
+    .x = layout.url_bar.x + padding,
+    .y = control_y,
+    .width = dropdown_width,
+    .height = control_height
+  };
+
+  layout.url_input = (Rectangle){
+    .x = layout.method_dropdown.x + layout.method_dropdown.width + url_bar_inner_padding,
+    .y = control_y,
+    .width = layout.url_bar.width - dropdown_width - button_width - (4 * padding) - (2 * url_bar_inner_padding),
+    .height = control_height
+  };
+
+  layout.send_button = (Rectangle){
+    .x = layout.url_input.x + layout.url_input.width + url_bar_inner_padding,
+    .y = control_y,
+    .width = button_width,
+    .height = control_height
+  };
+
+  // Body area (below URL bar)
+  layout.body_area = (Rectangle){
+    .x = layout.content.x,
+    .y = layout.url_bar.y + layout.url_bar.height + padding,
+    .width = layout.content.width,
+    .height = layout.content.height - layout.url_bar.height - (3 * padding)
+  };
 
-Rectangle AddPaddingHorizontal(Rectangle rect, float padding)
-{
-  return (Rectangle){
-    rect.x + padding,
-    rect.y,
-    rect.width - (2 * padding),
-    rect.height
+  // Split body into input (left) and result (right) panels
+  float split_ratio = 0.5f;
+  layout.input_panel = (Rectangle){
+    .x = layout.body_area.x,
+    .y = layout.body_area.y,
+    .width = layout.body_area.width * split_ratio,
+    .height = layout.body_area.height
+  };
+
+  layout.result_panel = (Rectangle){
+    .x = layout.body_area.x + layout.input_panel.width,
+    .y = layout.body_area.y,
+    .width = layout.body_area.width - layout.input_panel.width,
+    .height = layout.body_area.height
+  };
+
+  // Input panel internals
+  float tab_height = 40;
+  layout.input_tabs = (Rectangle){
+    .x = layout.input_panel.x + padding,
+    .y = layout.input_panel.y + padding,
+    .width = layout.input_panel.width - (2 * padding),
+    .height = tab_height
   };
+
+  layout.input_body = (Rectangle){
+    .x = layout.input_panel.x + padding,
+    .y = layout.input_tabs.y + layout.input_tabs.height,
+    .width = layout.input_panel.width - (2 * padding),
+    .height = layout.input_panel.height - tab_height - (2 * padding)
+  };
+
+  // Result panel internals
+  layout.result_tabs = (Rectangle){
+    .x = layout.result_panel.x + padding,
+    .y = layout.result_panel.y + padding,
+    .width = layout.result_panel.width - (2 * padding),
+    .height = tab_height
+  };
+
+  layout.result_body = (Rectangle){
+    .x = layout.result_panel.x + padding,
+    .y = layout.result_tabs.y + layout.result_tabs.height,
+    .width = layout.result_panel.width - (2 * padding),
+    .height = layout.result_panel.height - tab_height - (2 * padding)
+  };
+
+  return layout;
 }
 
-Rectangle AddPaddingVertical(Rectangle rect, float padding)
+// ============================================================================
+// DRAWING FUNCTIONS - Separated for clarity
+// ============================================================================
+
+static void DrawSidebar(UILayout *layout, Texture2D logo_texture, float padding)
 {
-  return (Rectangle){
-    rect.x,
-    rect.y + padding,
-    rect.width,
-    rect.height - (2 * padding)
-  };
-}
+  // Sidebar background with rounded left corners
+  DrawRectangleRec(layout->sidebar, g_colors.primary);
 
-// Layout helper functions
-Rectangle RightOf(Rectangle ref, float padding)
-{
-  return (Rectangle){
-    .x = ref.x + ref.width + padding,
-    .y = ref.y,
-    .width = 0,
-    .height = ref.height
+  // Logo
+  Rectangle logo_inner = AddPadding(layout->logo_area, padding);
+  float logo_size = logo_inner.height < logo_inner.width ? logo_inner.height : logo_inner.width;
+  Rectangle dest_rect = {
+    .x = logo_inner.x + (logo_inner.width - logo_size) / 2,
+    .y = logo_inner.y,
+    .width = logo_size,
+    .height = logo_size
   };
+  Rectangle source_rect = {0, 0, logo_texture.width, logo_texture.height};
+  DrawTexturePro(logo_texture, source_rect, dest_rect, (Vector2){0, 0}, 0.0f, WHITE);
 }
 
-Rectangle Below(Rectangle ref, float padding)
+static void DrawHistoryList(UILayout *layout, Vector2 mouse_pos, float padding, float scroll_offset)
 {
-  return (Rectangle){
-    .x = ref.x,
-    .y = ref.y + ref.height + padding,
-    .width = ref.width,
-    .height = 0
-  };
-}
+  int32 new_len = Dowa_Array_Length(new_history_items);
+  int32 history_len = Dowa_Array_Length(history_items);
+  int32 total = new_len + history_len;
+  float item_height = 70;
+
+  BeginScissorMode(layout->history_list.x, layout->history_list.y,
+           layout->history_list.width, layout->history_list.height);
+
+  int32 visible_index = 0;
+  for (int i = 0; i < total; i++)
+  {
+    HistoryItem *item = i < new_len ?
+      &new_history_items[new_len - i - 1] : &history_items[i - new_len];
+
+    if (item->deleted) continue;
+
+    // Calculate item rectangle
+    Rectangle item_rect = {
+      .x = layout->history_list.x,
+      .y = layout->history_list.y + (padding + item_height) * visible_index + scroll_offset,
+      .width = layout->history_list.width,
+      .height = item_height
+    };
+    item->rect = item_rect;
+    visible_index++;
+
+    // Skip if not visible
+    if (item_rect.y + item_rect.height < layout->history_list.y ||
+      item_rect.y > layout->history_list.y + layout->history_list.height)
+      continue;
+
+    // Draw item background
+    DrawRectangleRounded(item_rect, 0.3f, 8, g_colors.secondary);
+
+    // Title area (top 60%)
+    Rectangle title_rect = item_rect;
+    title_rect.height = item_rect.height * 0.55f;
+
+    // Draw title text
+    Rectangle title_text_rect = AddPadding(title_rect, 8);
+    if (item->title)
+    {
+      DrawTextEx(GuiGetFont(), item->title, (Vector2){ .x=title_text_rect.x, .y=title_text_rect.y + 4 },
+           GuiGetStyle(DEFAULT, TEXT_SIZE), TEXT_SIZE_DEFAULT/GuiGetStyle(DEFAULT, TEXT_SIZE), g_colors.text);
+    }
 
-Rectangle LeftColumn(Rectangle container, float ratio, float padding)
-{
-  return (Rectangle){
-    .x = container.x + padding,
-    .y = container.y + padding,
-    .width = (container.width * ratio) - padding,
-    .height = container.height - (2 * padding)
-  };
+    // Button area (bottom 40%)
+    Rectangle button_area = {
+      .x = item_rect.x,
+      .y = title_rect.y + title_rect.height,
+      .width = item_rect.width,
+      .height = item_rect.height - title_rect.height
+    };
+
+    Rectangle btn_view = {
+      .x = button_area.x + padding,
+      .y = button_area.y,
+      .width = (button_area.width - 3 * padding) / 2,
+      .height = button_area.height - padding
+    };
+
+    Rectangle btn_delete = {
+      .x = btn_view.x + btn_view.width + padding,
+      .y = btn_view.y,
+      .width = btn_view.width,
+      .height = btn_view.height
+    };
+
+    // Hover cursor for buttons
+    if (CheckCollisionPointRec(mouse_pos, btn_view) ||
+      CheckCollisionPointRec(mouse_pos, btn_delete))
+      SetMouseCursor(MOUSE_CURSOR_POINTING_HAND);
+
+    if (GuiButton(btn_view, "View"))
+      PostDog_Load_File(item->filename);
+
+    if (GuiButton(btn_delete, "Delete"))
+    {
+      char delete_path[512];
+      if (!remove(PostDog_Construct_URL(item->filename, delete_path, sizeof(delete_path))))
+        item->deleted = TRUE;
+      else
+        fprintf(stderr, "Couldn't delete file: %s\n", item->filename);
+    }
+  }
+
+  EndScissorMode();
+
+  // Scrollbar
+  float content_height = visible_index * (item_height + padding);
+  if (content_height > layout->history_list.height)
+  {
+    float scrollbar_height = (layout->history_list.height / content_height) * layout->history_list.height;
+    float scrollbar_y = layout->history_list.y - (scroll_offset / content_height) * layout->history_list.height;
+    Rectangle scrollbar_rect = {
+      layout->history_list.x + layout->history_list.width - 6,
+      scrollbar_y,
+      4,
+      scrollbar_height
+    };
+    DrawRectangleRounded(scrollbar_rect, 0.5f, 4, Fade(g_colors.text_light, 0.5f));
+  }
 }
 
-Rectangle RightColumn(Rectangle container, Rectangle leftCol, float padding)
+static void DrawURLBar(UILayout *layout, UIState *state, float padding)
 {
-  return (Rectangle){
-    .x = leftCol.x + leftCol.width + padding,
-    .y = container.y + padding,
-    .width = container.width - leftCol.width - (3 * padding),
-    .height = container.height - (2 * padding)
+  // URL bar background
+  Rectangle url_bar_bg = AddPadding(layout->url_bar, padding / 2);
+  DrawRectangleRounded(url_bar_bg, 0.3f, 8, g_colors.secondary);
+
+  // Combined method dropdown + URL input with rounded corners
+  Rectangle combined_input = {
+    .x = layout->method_dropdown.x,
+    .y = layout->method_dropdown.y,
+    .width = layout->url_input.x + layout->url_input.width - layout->method_dropdown.x,
+    .height = layout->method_dropdown.height
+  };
+
+  // Method colors: GET=Green, POST=Blue, PUT=Orange, DELETE=Red
+  Color method_colors[] = {
+    (Color){76, 175, 80, 255},   // GET - Green
+    (Color){33, 150, 243, 255},  // POST - Blue
+    (Color){255, 152, 0, 255},   // PUT - Orange
+    (Color){244, 67, 54, 255}    // DELETE - Red
   };
+
+  // Method dropdown + URL input combined component
+  DropdownTextBoxConfig url_config = {
+    .dropdown_items = "GET;POST;PUT;DELETE",
+    .dropdown_active = &active_method_dropdown,
+    .dropdown_edit_mode = &state->method_edit_mode,
+    .dropdown_width = layout->method_dropdown.width,
+    .text_buffer = url_input_text,
+    .text_buffer_size = URL_TEXT_BUFFER_LENGTH,
+    .text_edit_mode = &state->url_edit_mode,
+    .corner_radius = 0.2f,
+    .background_color = g_colors.background,
+    .border_color = g_colors.border,
+    .text_color = g_colors.text,
+    .item_colors = method_colors,
+    .item_count = 4
+  };
+  PostDog_DropdownTextBox(combined_input, url_config);
+
+  // Handle Enter key in URL input
+  if (state->url_edit_mode && IsKeyPressed(KEY_ENTER))
+  {
+    PostDog_Http_Thread_Request();
+    state->url_edit_mode = FALSE;
+  }
+
+  // Send button
+  DrawRectangleRounded(layout->send_button, 0.3f, 8, g_colors.primary);
+  Rectangle btn_text_rect = layout->send_button;
+  char *btn_text = "Send";
+  int text_width = MeasureText(btn_text, GuiGetStyle(DEFAULT, TEXT_SIZE));
+  DrawTextEx(GuiGetFont(),
+    btn_text,
+    (Vector2){ .x=btn_text_rect.x + (btn_text_rect.width - text_width) / 2, .y=btn_text_rect.y + (btn_text_rect.height - GuiGetStyle(DEFAULT, TEXT_SIZE)) / 2 },
+    GuiGetStyle(DEFAULT, TEXT_SIZE), TEXT_SIZE_DEFAULT/GuiGetStyle(DEFAULT, TEXT_SIZE), g_colors.text_light);
+
+  if (CheckCollisionPointRec(GetMousePosition(), layout->send_button) &&
+    IsMouseButtonPressed(MOUSE_BUTTON_LEFT))
+    PostDog_Http_Thread_Request();
 }
 
-Rectangle HorizontalSplit(Rectangle container, float ratio)
+static void DrawBodyPanels(UILayout *layout, UIState *state, float padding)
 {
-  return (Rectangle){
-    .x = container.x,
-    .y = container.y,
-    .width = container.width * ratio,
-    .height = container.height
-  };
+  // Draw split panel background (input left, result right)
+  DrawRectangleSelectiveRounded(layout->input_panel, 12, 8, g_colors.background,
+                  TRUE, FALSE, FALSE, TRUE);
+  DrawRectangleSelectiveRounded(layout->result_panel, 12, 8,
+                  LOADING ? Fade(g_colors.error, 0.3f) : g_colors.secondary,
+                  FALSE, TRUE, TRUE, FALSE);
+
+  // Tab labels
+  const char *tab_labels[] = {"Headers", "Body", "Params", "WebSocket"};
+  int tab_count = 4;
+
+  // Draw custom tabs
+  PostDog_TabBarSimple(layout->input_tabs, tab_labels, tab_count, &active_input_tab);
+
+  // Input body text area
+  int text_area_id = TEXT_AREA_ID_INPUT_HEADER + active_input_tab;
+  if (GuiTextArea(text_area_id, layout->input_body, input_body_array[active_input_tab],
+          BODY_BUFFER_LENGTH, state->input_body_edit_mode, TRUE, FALSE, g_text_area_arena))
+    state->input_body_edit_mode = !state->input_body_edit_mode;
+
+  // WebSocket send button (only on WebSocket tab)
+  if (active_input_tab == TAB_WEBSOCKET)
+  {
+    WS_BREAK = FALSE;
+    Rectangle ws_send_btn = {
+      .x = layout->input_body.x + layout->input_body.width - 90 - padding,
+      .y = layout->input_body.y + layout->input_body.height - 35 - padding,
+      .width = 90,
+      .height = 35
+    };
+
+    DrawRectangleRounded(ws_send_btn, 0.3f, 8, g_colors.highlight);
+    char *ws_text = !ws ? "Connect" : "Send";
+    int ws_text_width = MeasureText(ws_text, GuiGetStyle(DEFAULT, TEXT_SIZE));
+    DrawTextEx(GuiGetFont(),
+      ws_text,
+      (Vector2) { .x=ws_send_btn.x + (ws_send_btn.width - ws_text_width) / 2, .y=ws_send_btn.y + (ws_send_btn.height - GuiGetStyle(DEFAULT, TEXT_SIZE)) / 2 },
+      GuiGetStyle(DEFAULT, TEXT_SIZE), 
+      TEXT_SIZE_DEFAULT/GuiGetStyle(DEFAULT, TEXT_SIZE),
+      g_colors.text);
+
+    if ((CheckCollisionPointRec(GetMousePosition(), ws_send_btn) &&
+       IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) ||
+      (state->input_body_edit_mode && IsKeyDown(KEY_LEFT_SHIFT) && IsKeyPressed(KEY_ENTER)))
+    {
+      if (!ws)
+        websocket_thread_id = PostDog_Websocket_Start_Thread();
+      usleep(10000);
+      PostDog_Websocket_Send();
+    }
+  }
+  else
+  {
+    WS_BREAK = TRUE;
+  }
+
+  // Result tabs
+  const char *result_tab_labels[] = {"Body", "Headers"};
+  int result_tab_count = 2;
+  PostDog_TabBarSimple(layout->result_tabs, result_tab_labels, result_tab_count, &active_result_tab);
+
+  // Result body text area
+  int result_text_area_id = TEXT_AREA_ID_RESULT_BODY + active_result_tab;
+  // Result text area is readonly (selectable/copyable but not editable)
+  if (GuiTextArea(result_text_area_id, layout->result_body, result_body_array[active_result_tab],
+          RESULT_BUFFER_LENGTH, state->result_body_edit_mode, TRUE, TRUE, g_text_area_arena))
+    state->result_body_edit_mode = !state->result_body_edit_mode;
+
+  PostDog_Update_URL(state->url_edit_mode);
 }
 
+// ============================================================================
+// MAIN FUNCTION
+// ============================================================================
 int main()
 {
-  // -- initizlied --//
+  // ========================================================================
+  // INITIALIZATION
+  // ========================================================================
+  SetConfigFlags(FLAG_WINDOW_UNDECORATED);
+
+  // Initialize libuv
+  main_loop = uv_default_loop();
+  uv_mutex_init(&history_mutex);
+
+  // Initialize color scheme
+  PostDog_InitColorScheme();
+
+  // Initialize window
   InitWindow(SCREEN_WIDTH, SCREEN_HEIGHT, "PostDog");
   SetWindowState(FLAG_WINDOW_RESIZABLE);
   SetTargetFPS(60);
 
+  // Load resources
   Font customFont = LoadFontEx("postdog/Roboto-Regular.ttf", 20, 0, 0);
   GuiSetFont(customFont);
-  GuiSetStyle(DEFAULT, TEXT_SIZE, 10);
-  Image logo_original = LoadImage("postdog/epi_all_colors.png");
-  ImageResize(&logo_original, 60, 60);
+  GuiSetStyle(DEFAULT, TEXT_SIZE, TEXT_SIZE_DEFAULT);
+
+  Image logo_original = LoadImage("postdog/logo_bigger.png");
+  ImageResize(&logo_original, 200, 200);
   SetWindowIcon(logo_original);
-  Texture2D logo_texture = LoadTextureFromImage(logo_original); 
+  Texture2D logo_texture = LoadTextureFromImage(logo_original);
+  // ToggleBorderlessWindowed();
   UnloadImage(logo_original);
 
-  // -- Starting pos ---//
-  Rectangle history_sidebar = { 0 };
+  // Initialize text area arena
+  g_text_area_arena = Dowa_Arena_Create(1024 * 1024 * 4);
+
+  // Initialize history
   Dowa_Array_Reserve(history_items, 10);
   Dowa_Array_Reserve(new_history_items, 10);
   PostDog_History_Load(&history_items);
-  int32 *history_deleted_items = NULL;
 
-  Rectangle url_area = { 0 };
-  Rectangle textBounds = { 0 };
-  Rectangle url_input_bounds = { 0 };
-  bool url_input_edit = false;
-  Rectangle url_text_bounds = { 0 };
-  Rectangle url_enter_button = { 0 };
-  Rectangle method_dropdown = { 0 };
-
+  // Initialize text buffers
+  url_input_text = (char *)malloc(sizeof(char) * URL_TEXT_BUFFER_LENGTH);
+  snprintf(url_input_text, URL_TEXT_BUFFER_LENGTH, URL_TEXT_DEFAULT);
 
-  // Initialize global UI state
-  url_input_text = (char *)malloc(sizeof(char) * URL_TEXT_BUFFER_LENGTH);
-  snprintf(url_input_text, URL_TEXT_BUFFER_LENGTH, "https://httpbin.org/get");
-
-  Dowa_Array_Push(url_body_map, (char *)malloc(sizeof(char) * HEADER_BUFFER_LENGTH));
-  Dowa_Array_Push(url_body_map, (char *)malloc(sizeof(char) * BODY_BUFFER_LENGTH));
-  Dowa_Array_Push(url_body_map, (char *)malloc(sizeof(char) * DEFAULT_TEXT_BUFFER_LENGTH));
-  Dowa_Array_Push(url_body_map, (char *)malloc(sizeof(char) * DEFAULT_TEXT_BUFFER_LENGTH));
-
-  snprintf(url_body_map[TAB_HEADER], HEADER_BUFFER_LENGTH, "Content-Type: application/json");
-  snprintf(url_body_map[TAB_BODY], HEADER_BUFFER_LENGTH, "");
-
-  url_result_text = (char *)malloc(sizeof(char) * RESULT_BUFFER_LENGTH);
+  Dowa_Array_Push(input_body_array, (char *)malloc(sizeof(char) * HEADER_BUFFER_LENGTH));
+  Dowa_Array_Push(input_body_array, (char *)malloc(sizeof(char) * BODY_BUFFER_LENGTH));
+  Dowa_Array_Push(input_body_array, (char *)malloc(sizeof(char) * DEFAULT_TEXT_BUFFER_LENGTH));
+  Dowa_Array_Push(input_body_array, (char *)malloc(sizeof(char) * DEFAULT_TEXT_BUFFER_LENGTH));
 
-  bool method_edit = false;
-
-  int sendRequest;
-
-  // -- input --//
-  Rectangle input_area = { 0 };
-  Rectangle input_tab = { 0 };
-  Rectangle input_tab_item = { 0 };
-  Rectangle input_body = { 0 };
-  bool input_body_bool = false;
+  snprintf(input_body_array[TAB_HEADER], HEADER_BUFFER_LENGTH, HEADER_TEXT_DEFAULT);
+  snprintf(input_body_array[TAB_BODY], BODY_BUFFER_LENGTH, BODY_TEXT_DEFAULT);
+  snprintf(input_body_array[TAB_GET_PARAMS], DEFAULT_TEXT_BUFFER_LENGTH, GET_PARAM_TEXT_DEFAULT);
+  snprintf(input_body_array[TAB_WEBSOCKET], DEFAULT_TEXT_BUFFER_LENGTH, GET_PARAM_TEXT_DEFAULT);
 
-  // -- result --//
-  Rectangle result_area = { 0 };
-  Rectangle result_body = { 0 };
+  // Initialize result buffers (body and headers tabs)
+  Dowa_Array_Push(result_body_array, (char *)malloc(sizeof(char) * RESULT_BUFFER_LENGTH));
+  Dowa_Array_Push(result_body_array, (char *)malloc(sizeof(char) * RESULT_BUFFER_LENGTH));
+  result_body_array[RESULT_TAB_BODY][0] = '\0';
+  result_body_array[RESULT_TAB_HEADERS][0] = '\0';
 
-  // General styling.
-  float padding = 10; // TODO make it % based?
-  int active_input_tab = 0;
-  int prev_input_tab = 0;
+  // Initialize UI state
+  UIState ui_state = {0};
+  float padding = 10.0f;
 
-  // Scroll offsets
-  float history_scroll_offset = 0;
-  float input_body_scroll_offset = 0;
-  float result_body_scroll_offset = 0;
-
+  // ========================================================================
+  // MAIN LOOP
+  // ========================================================================
   while (!WindowShouldClose())
   {
+    // Process libuv events (non-blocking)
+    uv_run(main_loop, UV_RUN_NOWAIT);
+
+    // Handle keyboard shortcuts
+    DefaultBehaviours();
+
+    // ====================================================================
+    // LAYOUT CALCULATION
+    // ====================================================================
     int screen_width = GetScreenWidth();
     int screen_height = GetScreenHeight();
-
-    if ((IsKeyDown(KEY_LEFT_SUPER) || IsKeyDown(KEY_LEFT_CONTROL)) && IsKeyDown(KEY_EQUAL))
-      GuiSetStyle(DEFAULT, TEXT_SIZE, GuiGetStyle(DEFAULT, TEXT_SIZE) + 1);
-
-    if ((IsKeyDown(KEY_LEFT_SUPER) || IsKeyDown(KEY_LEFT_CONTROL)) && IsKeyDown(KEY_MINUS))
-      GuiSetStyle(DEFAULT, TEXT_SIZE, GuiGetStyle(DEFAULT, TEXT_SIZE) - 1);
-
-    Rectangle screen = { 0, 0, screen_width, screen_height };
-
-    // -- Side bar --//
-    history_sidebar = LeftColumn(screen, 0.15, padding);
-    Rectangle content_area = RightColumn(screen, history_sidebar, padding);
-    Rectangle logo_area = (Rectangle){
-      .x = history_sidebar.x,
-      .y = history_sidebar.y,
-      .width = history_sidebar.width,
-      .height = 80 
-    };
-
-    Rectangle history_list_area = Below(logo_area, padding);
-    history_list_area.width = history_sidebar.width;
-    history_list_area.height = history_sidebar.height - logo_area.height - padding;
-
-    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;
-    float item_height = history_list_area.height * 0.10;
-
-    int32 number_of_skipped_items = 0;
-    for (int i = 0; i < total; i++)
-    {
-      HistoryItem *curr_history_items = i < new_history_items_length ?
-        &new_history_items[i - new_history_items_length - 1] : &history_items[i - new_history_items_length];
-
-      if (curr_history_items->deleted)
-      {
-        number_of_skipped_items++;
-        continue;
-      }
-
-      curr_history_items->rect = (Rectangle){
-        .x = history_list_area.x,
-        .y = history_list_area.y + (padding + item_height) * (i - number_of_skipped_items) + history_scroll_offset,
-        .width = history_list_area.width,
-        .height = item_height
-      };
-    }
+    UILayout layout = CalculateLayout(screen_width, screen_height, padding);
 
-    // --- URL --- //
-    url_area = (Rectangle){
-      .x = content_area.x,
-      .y = content_area.y,
-      .width = content_area.width,
-      .height = content_area.height * 0.1
-    };
-
-    float url_control_y = url_area.y + (url_area.height - TEXT_SIZE * 2) / 2;
-
-    url_text_bounds = (Rectangle){
-      .x = url_area.x + padding,
-      .y = url_control_y,
-      .width = 7 * (TEXT_SIZE / 2),
-      .height = TEXT_SIZE * 2
-    };
-
-    url_input_bounds = RightOf(url_text_bounds, padding);
-    url_input_bounds.width = url_area.width * 0.7;
-    url_input_bounds.height = TEXT_SIZE * 2;
-
-    url_enter_button = RightOf(url_input_bounds, padding);
-    url_enter_button.width = url_area.width * 0.1;
-    url_enter_button.height = TEXT_SIZE * 2;
-
-    method_dropdown = RightOf(url_enter_button, padding);
-    method_dropdown.width = url_area.width * 0.1;
-    method_dropdown.height = TEXT_SIZE * 2;
-
-    // -- Body -- //
-    Rectangle body_area = Below(url_area, 0);
-    body_area.height = content_area.height - url_area.height;
-
-    input_area = HorizontalSplit(body_area, 0.5);
-    result_area = RightOf(input_area, 0);
-    result_area.width = body_area.width - input_area.width;
-
-    input_tab = (Rectangle){
-      .x = input_area.x + padding,
-      .y = input_area.y + padding,
-      .width = input_area.width - (2 * padding),
-      .height = input_area.height * 0.1
-    };
-
-    input_tab_item = input_tab;
-    input_tab_item.width = input_tab.width / 4;
-
-    input_body = Below(input_tab, 0);
-    input_body.width = input_tab.width;
-    input_body.height = input_area.height - input_tab.height - (2 * padding);
-
-    // -- Result -- /
-    result_body = (Rectangle){
-      .x = result_area.x + padding,
-      .y = input_body.y,
-      .width = result_area.width - (2 * padding),
-      .height = input_body.height
-    };
-
-    Vector2 mouse_position = GetMousePosition();
+    // ====================================================================
+    // INPUT HANDLING
+    // ====================================================================
+    Vector2 mouse_pos = GetMousePosition();
     float mouse_wheel = GetMouseWheelMove();
 
-    // Reset input body scroll when tab changes
-    if (prev_input_tab != active_input_tab) {
-      input_body_scroll_offset = 0;
-      prev_input_tab = active_input_tab;
+    // Reset cursor
+    SetMouseCursor(MOUSE_CURSOR_DEFAULT);
+
+    // History scroll
+    if (CheckCollisionPointRec(mouse_pos, layout.history_list) && mouse_wheel != 0)
+    {
+      ui_state.history_scroll_offset += mouse_wheel * 30;
+      int32 total = Dowa_Array_Length(new_history_items) + Dowa_Array_Length(history_items);
+      float content_height = total * 80;
+      float max_scroll = content_height - layout.history_list.height;
+      if (ui_state.history_scroll_offset > 0) ui_state.history_scroll_offset = 0;
+      if (max_scroll > 0 && ui_state.history_scroll_offset < -max_scroll)
+        ui_state.history_scroll_offset = -max_scroll;
     }
 
-    // Handle scroll wheel for history
-    if (InArea(mouse_position, history_list_area) && mouse_wheel != 0) {
-      history_scroll_offset += mouse_wheel * 30;  // 30 pixels per wheel tick
-      // Clamp scroll offset
-      float max_scroll = (total * (item_height + padding)) - history_list_area.height;
-      if (history_scroll_offset > 0) history_scroll_offset = 0;
-      if (history_scroll_offset < -max_scroll && max_scroll > 0) history_scroll_offset = -max_scroll;
-    }
-
-    // Handle scroll wheel for input body
-    if (InArea(mouse_position, input_body) && mouse_wheel != 0) {
-      input_body_scroll_offset += mouse_wheel * 30;
-      if (input_body_scroll_offset > 0) input_body_scroll_offset = 0;
-    }
-
-    // Handle scroll wheel for result body
-    if (InArea(mouse_position, result_body) && mouse_wheel != 0) {
-      result_body_scroll_offset += mouse_wheel * 30;
-      if (result_body_scroll_offset > 0) result_body_scroll_offset = 0;
-    }
-
-    BeginDrawing();
-      ClearBackground(GetColor(GuiGetStyle(DEFAULT, BACKGROUND_COLOR)));
-
-      DrawRectangleRec(history_sidebar, Fade(GRAY, 0.1f));
-
-      // DrawRectangleRec(logo_area, Fade(BLUE, 0.2f));
-      Rectangle logo_image_rect = AddPadding(logo_area, padding);
-      // Fit logo to area while maintaining aspect ratio
-      float logo_size = logo_image_rect.height < logo_image_rect.width ? logo_image_rect.height : logo_image_rect.width;
-      Rectangle dest = {
-        .x = logo_image_rect.x + (logo_image_rect.width - logo_size) / 2,  // Center horizontally
-        .y = logo_image_rect.y,
-        .width = logo_size,
-        .height = logo_size
-      };
+    // Cursor changes for interactive areas
+    if (CheckCollisionPointRec(mouse_pos, layout.send_button) ||
+      CheckCollisionPointRec(mouse_pos, layout.input_tabs) ||
+      CheckCollisionPointRec(mouse_pos, layout.logo_area))
+      SetMouseCursor(MOUSE_CURSOR_POINTING_HAND);
 
-      Rectangle source = { 0, 0, logo_texture.width, logo_texture.height };
-      DrawTexturePro(logo_texture, source, dest, (Vector2){0, 0}, 0.0f, WHITE);
-
-      BeginScissorMode(history_list_area.x, history_list_area.y, history_list_area.width, history_list_area.height);
-        for (int i = 0; i < total; i++)
-        {
-          HistoryItem *curr_history_items = i < new_history_items_length ? 
-            &new_history_items[i - new_history_items_length - 1] : &history_items[i - new_history_items_length];
-
-          if (curr_history_items->deleted)
-            continue;
-
-          float diff = curr_history_items->rect.height*0.3;
-          // DrawRectangleRec(curr_history_items->rect, Fade(RED, 0.1f));
-          Rectangle filename_area = curr_history_items->rect;
-
-          filename_area.height -= diff;
-          Rectangle icon_area = Below(filename_area, 0);
-          icon_area.height = diff;
-
-          DrawRectangleRec(filename_area, Fade(BLUE, 0.1f));
-          DrawRectangleRec(icon_area, Fade(YELLOW, 0.1f));
-
-          Rectangle icon_area_left_column = LeftColumn(icon_area, 0.5, 0);
-          Rectangle icon_area_right_column = RightColumn(icon_area, icon_area_left_column, 0);
-
-          GuiDrawText(curr_history_items->title, AddPadding(filename_area, padding), TEXT_ALIGN_CENTER, RED);
-          if (GuiButton(AddPaddingHorizontal(icon_area_left_column, padding), "view"))
-            PostDog_Load_File(curr_history_items->filename);
-          if (GuiButton(AddPaddingHorizontal(icon_area_right_column,padding), "delete"))
-          {
-            if (!remove(PostDog_Construct_URL(curr_history_items->filename)))
-               curr_history_items->deleted = TRUE;
-            else
-              fprintf(stderr, "Wasn't able to delete file: %s \n", curr_history_items->filename);
-          }
-        }
-      EndScissorMode();
+    // Logo click resets
+    if (CheckCollisionPointRec(mouse_pos, layout.logo_area) &&
+      IsMouseButtonPressed(MOUSE_BUTTON_LEFT))
+      PostDog_Params_Reset();
 
-      if (total > 0)
-      {
-        float content_height = total * (item_height + padding);
-        if (content_height > history_list_area.height)
-        {
-          float scrollbar_height = (history_list_area.height / content_height) * history_list_area.height;
-          float scrollbar_y = history_list_area.y - (history_scroll_offset / content_height) * history_list_area.height;
-          Rectangle scrollbar = {
-            history_list_area.x + history_list_area.width - 5,
-            scrollbar_y,
-            5,
-            scrollbar_height
-          };
-          DrawRectangleRec(scrollbar, Fade(WHITE, 0.5f));
-        }
-      }
-
-      // 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_edit))
-        url_input_edit = !url_input_edit;
-
-      sendRequest = GuiButton(url_enter_button, "ENTER");
-      if (sendRequest)
-        PostDog_Http_Request();
-      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));
-      GuiSetStyle(TOGGLE, GROUP_PADDING, 0);
-      GuiDrawRectangle(input_body, 1, GetColor(GuiGetStyle(TEXTBOX, BORDER)), GetColor(GuiGetStyle(TEXTBOX, BASE_COLOR_PRESSED)));
-
-      BeginScissorMode(input_body.x, input_body.y, input_body.width, input_body.height);
-        Rectangle scrolled_input = AddPadding(input_body, padding * 2);
-        scrolled_input.y += input_body_scroll_offset;
-        scrolled_input.height = MAX_SCROLL_HEIGHT;
-        if (JUNE_GuiTextBox(scrolled_input, url_body_map[active_input_tab], DEFAULT_TEXT_BUFFER_LENGTH, input_body_bool))
-          input_body_bool = !input_body_bool;
-      EndScissorMode();
-
-      if (input_body_scroll_offset < 0) {
-        float scrollbar_height = 10; 
-        float scrollbar_y = input_body.y - (input_body_scroll_offset / MAX_SCROLL_HEIGHT) * (input_body.height - scrollbar_height);
-        Rectangle scrollbar = {
-          input_body.x + input_body.width - 5,
-          scrollbar_y,
-          5,
-          scrollbar_height
-        };
-        DrawRectangleRec(scrollbar, Fade(BLUE, 0.5f));
-      }
-
-      GuiToggleGroup(input_tab_item, "Header;Body;Get Param;Bar", &active_input_tab);
-
-      PostDog_Update_URL();
-
-      // Result Rect
-      DrawRectangleRec(result_area, Fade(GREEN, 0.1f));
-      DrawRectangleRec(result_body, Fade(DARKGREEN, 0.1f));
-
-      // Create scrollable result body with offset
-      BeginScissorMode(result_body.x, result_body.y, result_body.width, result_body.height);
-        Rectangle scrolled_result = result_body;
-        scrolled_result.y += result_body_scroll_offset;
-        scrolled_result.height = MAX_SCROLL_HEIGHT;
-        GuiTextBoxMulti(scrolled_result, url_result_text, RESULT_BUFFER_LENGTH, false);
-      EndScissorMode();
-
-      if (result_body_scroll_offset < 0) {
-        float scrollbar_height = 10;
-        float scrollbar_y = result_body.y - (result_body_scroll_offset / MAX_SCROLL_HEIGHT) * (result_body.height - scrollbar_height);
-        Rectangle scrollbar = {
-          result_body.x + result_body.width - 5,
-          scrollbar_y,
-          5,
-          scrollbar_height
-        };
-        DrawRectangleRec(scrollbar, Fade(GREEN, 0.5f));
-      }
-
-      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);
-      }
+    // ====================================================================
+    // DRAWING
+    // ====================================================================
+    BeginDrawing();
+      ClearBackground(g_colors.background);
+      DrawRectangleSelectiveRounded(layout.content, 12, 8, g_colors.secondary,
+                      FALSE, TRUE, TRUE, FALSE);
+      DrawSidebar(&layout, logo_texture, padding);
+      DrawHistoryList(&layout, mouse_pos, padding, ui_state.history_scroll_offset);
+      DrawBodyPanels(&layout, &ui_state, padding);
+      DrawURLBar(&layout, &ui_state, padding);
     EndDrawing();
   }
+
+  // ========================================================================
+  // CLEANUP
+  // ========================================================================
+  GuiTextAreaResetAllStates();
+  Dowa_Arena_Free(g_text_area_arena);
+
+  uv_mutex_destroy(&history_mutex);
+  uv_loop_close(main_loop);
+
+  UnloadTexture(logo_texture);
   CloseWindow();
+
   return 0;
 }