diff postdog/main.c @ 163:058de208e640

[Config] Adding os ignore files.
author June Park <parkjune1995@gmail.com>
date Mon, 19 Jan 2026 04:52:02 -0800
parents 87d8d3eb3491
children 0face9898d04
line wrap: on
line diff
--- a/postdog/main.c	Mon Jan 19 04:51:50 2026 -0800
+++ b/postdog/main.c	Mon Jan 19 04:52:02 2026 -0800
@@ -6,15 +6,15 @@
 #include <uv.h>
 
 #ifdef _WIN32
-    #include <direct.h>
-    #include <io.h>
-    #define mkdir(path, mode) _mkdir(path)
-    #define access _access
-    #define F_OK 0
+  #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>
+  #include <sys/stat.h>
+  #include <dirent.h>
+  #include <unistd.h>
 #endif
 
 
@@ -48,36 +48,50 @@
 // ============================================================================
 
 #define TEXT_SIZE_DEFAULT 10 // used to calcualte spacing
-#define TEXT_AREA_LINE_HEIGHT 20
+#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;
+  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
+  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
+  // 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;
+  // 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};
@@ -91,943 +105,1092 @@
 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];
-        }
+  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;
+  }
+  return NULL;
 }
 
 static TextAreaState* CreateTextAreaState(int id) {
-    if (g_text_area_state_count >= TEXT_AREA_MAX_INSTANCES) {
-        return &g_text_area_states[0]; // Reuse first slot
+  // 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;
     }
+  }
 
-    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;
+  // 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++;
-        }
+  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;
+  }
+  *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;
+  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++;
+  while (text[i] != '\0') {
+    if (current_line == line && current_col == column) {
+      return i;
     }
-    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;
+  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;
+  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++;
+  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 count;
+  }
+  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;
+  if (start >= end) return 0;
 
-    char temp[1024];
-    int len = TA_Min_Int(end - start, 1023);
-    strncpy(temp, text + start, len);
-    temp[len] = '\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;
+  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;
+                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;
+  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);
+  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;
+  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;
+  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');
+  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;
+    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;
+          }
+        }
 
-                    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;
-                }
+        if (current_visual_line == target_visual_line) {
+          float click_x = pos.x - content_x;
+          int best_pos = line_char_start;
+          float best_dist = 99999;
 
-                current_visual_line++;
-                line_char_start = (text[wrap_pos] == ' ') ? wrap_pos + 1 : wrap_pos;
-                i = line_char_start;
-                continue;
+          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;
         }
 
-        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++;
+        current_visual_line++;
+        line_char_start = (text[wrap_pos] == ' ') ? wrap_pos + 1 : wrap_pos;
+        i = line_char_start;
+        continue;
+      }
     }
 
-    return text_len;
-}
+    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;
 
-static Vector2 GetCursorScreenPos(const char *text, int cursor_pos, Rectangle bounds,
-                                   boolean wrap, float scroll_y, int font_size, int line_height) {
-    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;
+        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);
+      }
 
-    if (!text || cursor_pos == 0) {
-        return (Vector2){content_x, content_y};
+      current_visual_line++;
+      line_char_start = i + 1;
     }
 
-    int text_len = strlen(text);
-    cursor_pos = TA_Min_Int(cursor_pos, text_len);
+    i++;
+  }
+
+  return text_len;
+}
 
-    int visual_line = 0;
-    int line_char_start = 0;
+// 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);
 
-    for (int i = 0; i <= cursor_pos; i++) {
-        if (i == cursor_pos) {
-            float x = content_x + MeasureTextRange(text, line_char_start, cursor_pos, font_size);
-            float y = content_y + visual_line * line_height;
-            return (Vector2){x, y};
-        }
+  // 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 (text[i] == '\n') {
-            visual_line++;
-            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;
-                    }
-                }
+    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};
+  }
 
-                if (cursor_pos <= wrap_pos) {
-                    float x = content_x + MeasureTextRange(text, line_char_start, cursor_pos, font_size);
-                    float y = content_y + visual_line * line_height;
-                    return (Vector2){x, 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];
 
-                visual_line++;
-                line_char_start = (text[wrap_pos] == ' ') ? wrap_pos + 1 : wrap_pos;
-            }
-        }
+    // 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};
     }
+  }
 
-    float x = content_x + MeasureTextRange(text, line_char_start, cursor_pos, font_size);
-    float y = content_y + visual_line * 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;
+                 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);
+  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;
-            }
+  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;
+  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;
+  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;
-    }
+  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;
+  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--;
+  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;
+  if (state->undo_index <= 0) return FALSE;
 
-    state->undo_index--;
-    TextAreaUndoEntry *entry = &state->undo_stack[state->undo_index];
+  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;
+  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;
+  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;
+  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];
+  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;
+  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;
+  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);
+                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;
-    }
+  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);
+  memmove(text + cursor_pos + insert_len,
+      text + cursor_pos,
+      text_len - cursor_pos + 1);
 
-    memcpy(text + cursor_pos, insert_text, insert_len);
+  memcpy(text + cursor_pos, insert_text, insert_len);
 
-    *new_cursor_pos = cursor_pos + 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;
+  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);
+  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;
+  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;
+  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, Dowa_Arena *arena) {
-    boolean should_toggle_edit_mode = FALSE;
+          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);
+  }
 
-    // Get or create state for this text area
-    TextAreaState *state = GetTextAreaState(id);
-    if (!state) {
-        state = CreateTextAreaState(id);
-        state->cursor_pos = strlen(text);
-        state->last_blink_time = GetTime();
-        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;
     }
-
-    int text_len = strlen(text);
-    Vector2 mouse_pos = GetMousePosition();
-    boolean mouse_in_bounds = CheckCollisionPointRec(mouse_pos, bounds);
+  }
 
-    // Handle click to enter/exit edit 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);
 
-    // Content area
-    float content_height = GetContentHeight(text, bounds, should_text_wrap,
-                                            GuiGetStyle(DEFAULT, TEXT_SIZE), 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));
+  }
 
-    // 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;
     }
 
-    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
-        if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT) && mouse_in_bounds) {
-            int click_pos = GetCharIndexFromPos(text, bounds, mouse_pos, should_text_wrap,
-                                                 state->scroll_offset_y, GuiGetStyle(DEFAULT, TEXT_SIZE),
-                                                 TEXT_AREA_LINE_HEIGHT);
-            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 = GetCharIndexFromPos(text, bounds, mouse_pos, should_text_wrap,
-                                                state->scroll_offset_y, GuiGetStyle(DEFAULT, TEXT_SIZE),
-                                                TEXT_AREA_LINE_HEIGHT);
-            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
-        if (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
-        if (ctrl_pressed && IsKeyPressed(KEY_V)) {
-            const char *clipboard = GetClipboardText();
-            if (clipboard && 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
-        if (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;
-        }
+    // 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 (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;
+    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;
         }
-
-        // Text Input
-        if (!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();
-            }
-        }
-
-        // Enter key
-        if (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
-        if (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
-        if (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
-        if (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
-        Vector2 cursor_screen = GetCursorScreenPos(text, state->cursor_pos, bounds,
-                                                    should_text_wrap, state->scroll_offset_y,
-                                                    GuiGetStyle(DEFAULT, TEXT_SIZE), TEXT_AREA_LINE_HEIGHT);
-
-        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));
+        state->selection_end = drag_pos;
+      }
     }
 
-    // Drawing
-    DrawRectangleRounded(bounds, 0.2, 1, is_edit_mode ? DARKGRAY : (Color){40, 40, 40, 255});
-    // 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 ? WHITE : GRAY);
-
-    BeginScissorMode((int)bounds.x, (int)bounds.y, (int)bounds.width, (int)bounds.height);
+    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;
+        }
+      }
+    }
 
-    float content_x = bounds.x + TEXT_AREA_PADDING;
-    float content_y = bounds.y + TEXT_AREA_PADDING - state->scroll_offset_y;
-    float content_width = bounds.width - TEXT_AREA_PADDING * 2;
+    // Ctrl+A: Select All
+    if (ctrl_pressed && IsKeyPressed(KEY_A)) {
+      state->selection_start = 0;
+      state->selection_end = text_len;
+      state->cursor_pos = text_len;
+    }
 
-    text_len = strlen(text);
+    // 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);
+        }
+      }
+    }
 
-    // Draw selection highlight
-    if (state->selection_start >= 0 && state->selection_end >= 0 &&
+    // 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);
+        }
 
-        int line_char_start = 0;
-        int visual_line = 0;
+        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++;
+        }
 
-        for (int i = 0; i <= text_len; i++) {
-            boolean is_end = (i == text_len);
-            boolean is_newline = (!is_end && text[i] == '\n');
-            boolean should_draw_line = is_end || is_newline;
+        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;
+        }
+      }
+    }
 
-            if (should_text_wrap && !is_end && !is_newline) {
-                int line_width = MeasureTextRange(text, line_char_start, i + 1, GuiGetStyle(DEFAULT, TEXT_SIZE));
-                if (line_width > content_width && i > line_char_start) {
-                    should_draw_line = 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 (should_draw_line) {
-                int line_end = i;
+    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 (sel_min < line_end && sel_max > line_char_start) {
-                    int highlight_start = TA_Max_Int(sel_min, line_char_start);
-                    int highlight_end = TA_Min_Int(sel_max, line_end);
+    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;
+    }
 
-                    float x1 = content_x + MeasureTextRange(text, line_char_start, highlight_start, GuiGetStyle(DEFAULT, TEXT_SIZE));
-                    float x2 = content_x + MeasureTextRange(text, line_char_start, highlight_end, GuiGetStyle(DEFAULT, TEXT_SIZE));
-                    float y = content_y + visual_line * TEXT_AREA_LINE_HEIGHT;
+    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;
+    }
 
-                    DrawRectangle((int)x1, (int)y, (int)(x2 - x1), TEXT_AREA_LINE_HEIGHT,
-                                  Fade(SKYBLUE, 0.5f));
-                }
+    // 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;
+    }
 
-                visual_line++;
-                line_char_start = i + 1;
-            }
+    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;
     }
 
-    // Draw text with wrapping support
-    if (should_text_wrap) {
-        int line_char_start = 0;
-        int visual_line = 0;
-
-        for (int i = 0; i <= text_len; i++) {
-            boolean is_end = (i == text_len);
-            boolean is_newline = (!is_end && text[i] == '\n');
-            boolean should_draw_line = is_end || is_newline;
+    // 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);
+          }
 
-            if (!is_end && !is_newline) {
-                int line_width = MeasureTextRange(text, line_char_start, i + 1, GuiGetStyle(DEFAULT, TEXT_SIZE));
-                if (line_width > content_width && i > line_char_start) {
-                    should_draw_line = TRUE;
-                }
-            }
-
-            if (should_draw_line && i > line_char_start) {
-                char line_buffer[1024];
-                int line_len = TA_Min_Int(i - line_char_start, 1023);
-                strncpy(line_buffer, text + line_char_start, line_len);
-                line_buffer[line_len] = '\0';
-
-                Vector2 draw_text_vector = {
-                  .x = content_x,
-                  .y = content_y + visual_line * TEXT_AREA_LINE_HEIGHT
-                };
-                DrawTextEx(GuiGetFont(), line_buffer, draw_text_vector,
-                        GuiGetStyle(DEFAULT, TEXT_SIZE), TEXT_SIZE_DEFAULT/GuiGetStyle(DEFAULT, TEXT_SIZE), WHITE);
+          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) {}
+    }
 
-                visual_line++;
-                line_char_start = is_newline ? i + 1 : i;
-            } else if (is_newline) {
-                visual_line++;
-                line_char_start = i + 1;
-            }
-        }
-    } else {
-        int line_start = 0;
-        int visual_line = 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;
+    }
 
-        for (int i = 0; i <= text_len; i++) {
-            if (i == text_len || text[i] == '\n') {
-                if (i > line_start) {
-                    char line_buffer[1024];
-                    int line_len = TA_Min_Int(i - line_start, 1023);
-                    strncpy(line_buffer, text + line_start, line_len);
-                    line_buffer[line_len] = '\0';
+    // 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;
+      }
+    }
 
-                    Vector2 draw_text_vector = {
-                      .x = content_x,
-                      .y = content_y + visual_line * TEXT_AREA_LINE_HEIGHT
-                    };
-                    DrawTextEx(GuiGetFont(), line_buffer, draw_text_vector ,
-                            GuiGetStyle(DEFAULT, TEXT_SIZE), TEXT_SIZE_DEFAULT/GuiGetStyle(DEFAULT, TEXT_SIZE), WHITE);
-                }
-                visual_line++;
-                line_start = i + 1;
-            }
-        }
+    // 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;
+      }
     }
 
-    // Draw cursor
-    if (is_edit_mode && state->cursor_visible) {
-        Vector2 cursor_pos = GetCursorScreenPos(text, state->cursor_pos, bounds,
-                                                 should_text_wrap, state->scroll_offset_y,
-                                                 GuiGetStyle(DEFAULT, TEXT_SIZE), TEXT_AREA_LINE_HEIGHT);
+    // 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);
+      }
 
-        DrawRectangle((int)cursor_pos.x, (int)cursor_pos.y,
-                      TEXT_AREA_CURSOR_WIDTH, TEXT_AREA_LINE_HEIGHT, WHITE);
+      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;
     }
 
-    EndScissorMode();
+    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;
 
-    // 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)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';
 
-        DrawRectangle((int)(bounds.x + bounds.width - 8), (int)scrollbar_y,
-                      6, (int)scrollbar_height, Fade(WHITE, 0.3f));
+      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);
     }
+  }
 
-    return should_toggle_edit_mode;
+  // 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;
-        }
-        state->is_initialized = FALSE;
+  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;
-        }
-        g_text_area_states[i].is_initialized = FALSE;
+  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;
     }
-    g_text_area_state_count = 0;
+    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;
 }
 
 // ============================================================================
@@ -1063,12 +1226,19 @@
   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_BODY   2
 #define TEXT_AREA_ID_INPUT_PARAMS   3
-#define TEXT_AREA_ID_INPUT_WS       4
-#define TEXT_AREA_ID_RESULT         5
+#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;
@@ -1078,10 +1248,11 @@
 
 // Global UI state
 char *url_input_text = NULL;
-char *result_text = 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;
@@ -1100,13 +1271,13 @@
   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);
-    free(title);
-    return strdup(filename);
+  fclose(file);
+  free(title);
+  return strdup(filename);
   }
   fclose(file);
 
@@ -1148,18 +1319,18 @@
   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
@@ -1169,7 +1340,7 @@
 
   int count = Dowa_Array_Length(file_arr);
   if (count > 1) {
-    qsort(file_arr, count, sizeof(HistoryItem), CompareHistoryItemsByDate);
+  qsort(file_arr, count, sizeof(HistoryItem), CompareHistoryItemsByDate);
   }
 }
 
@@ -1182,6 +1353,7 @@
       return -1;
     return 0;
   }
+
   printf("Directory '%s' already exists.\n", POSTDOG_PATHS);
   PostDog_List_Directory(POSTDOG_PATHS, p_history_files);
   return 0;
@@ -1190,10 +1362,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
   );
 }
 
@@ -1208,10 +1380,10 @@
 {
   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;
 }
@@ -1229,8 +1401,8 @@
   FILE *file = fopen(full_file_path, "w");
   if (!file)
   {
-    fprintf(stderr, "Failed to create a file: %s\n", full_file_path);
-    return FALSE;
+  fprintf(stderr, "Failed to create a file: %s\n", full_file_path);
+  return FALSE;
   }
   fwrite(values, 1, strlen(values), file);
   fclose(file);
@@ -1246,43 +1418,43 @@
   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,
-      input_body_array[TAB_HEADER],
-      input_body_array[TAB_BODY],
-      input_body_array[TAB_GET_PARAMS],
-      input_body_array[TAB_WEBSOCKET],
-      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);
@@ -1293,12 +1465,12 @@
 
   if (PostDog_History_CreateFile(filename, new_file))
   {
-    HistoryItem item = {0};
-    item.filename = strdup(filename);
-    item.title = strdup(title);
-    item.deleted = FALSE;
+  HistoryItem item = {0};
+  item.filename = strdup(filename);
+  item.title = strdup(title);
+  item.deleted = FALSE;
 
-    Dowa_Array_Push(new_history_items, item);
+  Dowa_Array_Push(new_history_items, item);
   }
 
   uv_mutex_unlock(&history_mutex);
@@ -1317,33 +1489,46 @@
       if (p_msg->opcode == SEOBEO_WS_OPCODE_TEXT)
       {
         printf("Response: %.*s\n", (int)p_msg->length, (char*)p_msg->data);
-        snprintf(result_text + strlen(result_text),  RESULT_BUFFER_LENGTH - strlen(result_text),
-            "\n%s", (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);
-    printf("Listening\n");
   }
   return;
 }
 
 void PostDog_Websocket_Connect(void)
 {
-  ws = Seobeo_WebSocket_Connect(url_input_text);
-  memset(result_text, 0, strlen(result_text));
+  // 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';
+
+  // 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)
   {
-    snprintf(result_text + strlen(result_text),  RESULT_BUFFER_LENGTH - strlen(result_text),
-        "Failed to send message\n");
+    snprintf(body + strlen(body), RESULT_BUFFER_LENGTH - strlen(body),
+      "Failed to send message\n");
     return -1;
   }
-  snprintf(result_text + strlen(result_text),  RESULT_BUFFER_LENGTH - strlen(result_text),
-      "\n%s", input_body_array[active_input_tab]);
+  snprintf(body + strlen(body), RESULT_BUFFER_LENGTH - strlen(body),
+    "\n%s", input_body_array[active_input_tab]);
   return 0;
 }
 
@@ -1365,9 +1550,9 @@
 
   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;
+  perror("Failed to create thread");
+  memset(&thread_id, 0, sizeof(thread_id));
+  return thread_id;
   }
 
   return thread_id;
@@ -1376,6 +1561,7 @@
 int PostDog_Http_Request(void)
 {
   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)
   {
@@ -1401,32 +1587,54 @@
     }
   }
 
+  if (input_body_array[TAB_HEADER] && strlen(input_body_array[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(input_body_array[TAB_HEADER]);
-      char *line = strtok(headersCopy, "\n");
-      while (line != NULL)
-      {
-        while (*line == ' ' || *line == '\t') line++;
-        if (strlen(line) > 0)
-          Seobeo_Client_Request_Add_Header_Array(req, line);
-        line = strtok(NULL, "\n");
+      while (*line == ' ' || *line == '\t') line++;
+      if (strlen(line) > 0)
+        Seobeo_Client_Request_Add_Header_Array(req, line);
+      line = strtok(NULL, "\n");
+    }
+
+  }
+  Seobeo_Client_Request_Set_Follow_Redirects(req, TRUE, 5); // TODO: remove magic number;
+  res = Seobeo_Client_Request_Execute(req);
+
+  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]);
+
+    // 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);
       }
-
     }
-    Seobeo_Client_Request_Set_Follow_Redirects(req, TRUE, 5); // TODO: remove magic number;
-    res = Seobeo_Client_Request_Execute(req);
-
-    if (res == NULL)
-      snprintf(result_text, RESULT_BUFFER_LENGTH, "Error: Failed to send the request\n");
-    else
-      snprintf(result_text, RESULT_BUFFER_LENGTH, "HTTP Status: %d\n\n%s",
-               res->status_code, res->body ? res->body : "");
   }
+  printf("Body: %s\n", res ? res->body : "NULL");
   Seobeo_Client_Request_Destroy(req);
   Seobeo_Client_Response_Destroy(res);
   PostDog_Request_SaveFile();
+
+  // Reset result text area states so scroll/cursor don't carry over
+  GuiTextAreaResetState(TEXT_AREA_ID_RESULT_BODY);
+  GuiTextAreaResetState(TEXT_AREA_ID_RESULT_HEADERS);
+
   return 0;
 }
 
@@ -1447,78 +1655,108 @@
   uv_work_t *work_req = malloc(sizeof(uv_work_t));
   if (!work_req)
   {
-    perror("Failed to allocate work request");
-    return;
+  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;
+  perror("Failed to queue work");
+  free(work_req);
+  LOADING = FALSE;
   }
 }
 
-void PostDog_Update_URL(void)
+void PostDog_Update_URL(boolean is_url_updated)
 {
-  // Save existing query string if present
-  char *question_mark = strchr(url_input_text, '?');
-  if (question_mark)
-    *question_mark = '\0';
+  char *params_start = strchr(url_input_text, '?');
+  if (is_url_updated)
+  {
+    if (!params_start)
+      return;
+    params_start++;
+    Dowa_Arena *arena = Dowa_Arena_Create(1024*1024);
 
-  int get_params_length = (int)strlen(input_body_array[TAB_GET_PARAMS]);
-  if (get_params_length == 0)
-    return;
+    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);
 
-  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++)
+      if (Dowa_Array_Length(key_value) < 2)
+        break;
+      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
   {
-    char *line = lines[i];
-    char **key_value = Dowa_String_Split(line, " ", (int)strlen(line), 1, arena);
+    if (params_start)
+    {
+      size_t length = params_start - url_input_text;
+      url_input_text[length] = '\0';
+    }
+    int get_params_length = (int)strlen(input_body_array[TAB_GET_PARAMS]);
+    if (get_params_length == 0) 
+      return;
 
-    if (Dowa_Array_Length(key_value) < 2)
-      break;
+    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);
 
-    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]);
+
+      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 = "&";
     }
-    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';
-  result_text[0] = '\0';
   active_method_dropdown = 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();
 }
@@ -1529,7 +1767,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);
@@ -1548,42 +1786,45 @@
     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:  // Websocket (TAB_WEBSOCKET)
       {
-        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--)
+      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 (input_body_array[map_index][j] == '\n')
-          {
-            input_body_array[map_index][j] = '\0';
-            break;
-          }
+        input_body_array[map_index][j] = '\0';
+        break;
         }
-        break;
+      }
+      break;
       }
 
-      default:  // Response (index 7+)
-        snprintf(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();
 
@@ -1591,46 +1832,482 @@
   Dowa_Arena_Free(split_arena);
 }
 
+// ============================================================================
+// 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)
+{
+  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)
+  };
+
+  // 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;
+}
+
+// ============================================================================
+// DRAWING FUNCTIONS - Separated for clarity
+// ============================================================================
+
+static void DrawSidebar(UILayout *layout, Texture2D logo_texture, float padding)
+{
+  // Sidebar background with rounded left corners
+  DrawRectangleRec(layout->sidebar, g_colors.primary);
+
+  // 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);
+}
+
+static void DrawHistoryList(UILayout *layout, Vector2 mouse_pos, float padding, float scroll_offset)
+{
+  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);
+    }
+
+    // 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));
+  }
+}
+
+static void DrawURLBar(UILayout *layout, UIState *state, float 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;
+  const 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();
+}
+
+static void DrawBodyPanels(UILayout *layout, UIState *state, float padding)
+{
+  // 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);
+    const char *ws_text = "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;
+    if (websocket_thread_id)
+      PostDog_Websocket_Destroy(websocket_thread_id);
+  }
+
+  // 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()
 {
-  // -- initialize libuv --//
+  // ========================================================================
+  // INITIALIZATION
+  // ========================================================================
+
+  // Initialize libuv
   main_loop = uv_default_loop();
   uv_mutex_init(&history_mutex);
 
-  // -- initizlied --//
+  // 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, 15);
+  GuiSetStyle(DEFAULT, TEXT_SIZE, 16);
+
   Image logo_original = LoadImage("postdog/logo_bigger.png");
-  ImageResize(&logo_original, 200, 600);
+  ImageResize(&logo_original, 200, 200);
   SetWindowIcon(logo_original);
   Texture2D logo_texture = LoadTextureFromImage(logo_original);
+  // ToggleBorderlessWindowed();
   UnloadImage(logo_original);
 
-  // Arena for text area undo states
+  // Initialize text area arena
   g_text_area_arena = Dowa_Arena_Create(1024 * 1024 * 4);
 
-  // -- Starting pos ---//
-  Rectangle full_screen = { 0 };
-  Rectangle history_sidebar_rect = { 0 };
+  // 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_rect = { 0 };
-  Rectangle url_input_bounds_rect = { 0 };
-  bool url_input_edit = false;
-  Rectangle url_text_bounds_rect = { 0 };
-  Rectangle url_enter_button_rect = { 0 };
-  Rectangle method_dropdown_rect = { 0 };
-
-
-  // Initialize global UI state
+  // 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);
 
@@ -1640,353 +2317,95 @@
   Dowa_Array_Push(input_body_array, (char *)malloc(sizeof(char) * DEFAULT_TEXT_BUFFER_LENGTH));
 
   snprintf(input_body_array[TAB_HEADER], HEADER_BUFFER_LENGTH, HEADER_TEXT_DEFAULT);
-  snprintf(input_body_array[TAB_BODY], HEADER_BUFFER_LENGTH, BODY_TEXT_DEFAULT);
-  snprintf(input_body_array[TAB_GET_PARAMS], HEADER_BUFFER_LENGTH, GET_PARAM_TEXT_DEFAULT);
-
-  result_text = (char *)malloc(sizeof(char) * RESULT_BUFFER_LENGTH);
-  result_text[0] = '\0';
-
-  bool method_edit = false;
-
-  int sendRequest;
+  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);
 
-  // -- input --//
-  Rectangle input_area_rect = { 0 };
-  Rectangle input_tab_rect = { 0 };
-  Rectangle input_tab_item_rect = { 0 };
-  Rectangle input_body_rect = { 0 };
-  bool input_body_edit_mode = false;
+  // 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';
 
-  // -- result --//
-  Rectangle result_area_rect = { 0 };
-  Rectangle result_body_rect = { 0 };
-  bool result_body_edit_mode = false;
+  // Initialize UI state
+  UIState ui_state = {0};
+  float padding = 10.0f;
 
-  // General styling.
-  float padding = 10; // TODO make it % based?
-  int prev_input_tab = 0;
-
-  // Scroll offsets
-  float history_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_rect = { 0, 0, screen_width, screen_height };
+    UILayout layout = CalculateLayout(screen_width, screen_height, padding);
 
-    // -- Side bar --//
-    history_sidebar_rect = LeftColumn(screen_rect, 0.20, padding);
-    Rectangle content_area_rect = RightColumn(screen_rect, history_sidebar_rect, padding);
-    Rectangle logo_area_rect = (Rectangle){
-      .x = history_sidebar_rect.x,
-      .y = history_sidebar_rect.y,
-      .width = history_sidebar_rect.width,
-      .height = 300
-    };
+    // ====================================================================
+    // INPUT HANDLING
+    // ====================================================================
+    Vector2 mouse_pos = GetMousePosition();
+    float mouse_wheel = GetMouseWheelMove();
 
-
-    DrawRectangleSelectiveRounded(history_sidebar_rect, 10, 10, ORANGE, TRUE, FALSE, FALSE, TRUE);
-    DrawRectangleSelectiveRounded(content_area_rect, 10, 10, WHITE, FALSE, TRUE, TRUE, FALSE);
+    // Reset cursor
+    SetMouseCursor(MOUSE_CURSOR_DEFAULT);
 
-    Rectangle history_list_area_rect = Below(logo_area_rect, padding);
-    history_list_area_rect.x += padding;
-    history_list_area_rect.width = history_sidebar_rect.width -  (2 * padding);
-    history_list_area_rect.height = history_sidebar_rect.height - logo_area_rect.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_rect.height * 0.10;
-
-    int32 number_of_skipped_items = 0;
-    for (int i = 0; i < total; i++)
+    // History scroll
+    if (CheckCollisionPointRec(mouse_pos, layout.history_list) && mouse_wheel != 0)
     {
-      HistoryItem *curr_history_items = i < new_history_items_length ?
-        &new_history_items[new_history_items_length - i - 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_rect.x,
-        .y = history_list_area_rect.y + (padding + item_height) * (i - number_of_skipped_items) + history_scroll_offset,
-        .width = history_list_area_rect.width,
-        .height = item_height
-      };
+      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;
     }
 
-    // --- URL --- //
-    url_area_rect = (Rectangle){
-      .x = content_area_rect.x,
-      .y = content_area_rect.y,
-      .width = content_area_rect.width,
-      .height = content_area_rect.height * 0.1
-    };
-
-    float url_control_y = url_area_rect.y + (url_area_rect.height - TEXT_SIZE * 2) / 2;
-
-    url_text_bounds_rect = (Rectangle){
-      .x = url_area_rect.x + padding,
-      .y = url_control_y,
-      .width = 10 * (TEXT_SIZE / 2),
-      .height = TEXT_SIZE * 2
-    };
-
-    url_input_bounds_rect = RightOf(url_text_bounds_rect, padding);
-    url_input_bounds_rect.width = url_area_rect.width * 0.7;
-    url_input_bounds_rect.height = TEXT_SIZE * 2.5;
-
-    url_enter_button_rect = RightOf(url_input_bounds_rect, padding);
-    url_enter_button_rect.width = url_area_rect.width * 0.1;
-    url_enter_button_rect.height = TEXT_SIZE * 2;
-
-    method_dropdown_rect = RightOf(url_enter_button_rect, padding);
-    method_dropdown_rect.width = url_area_rect.width * 0.1;
-    method_dropdown_rect.height = TEXT_SIZE * 2;
-
-    // -- Body -- //
-    Rectangle body_area_rect = Below(url_area_rect, 0);
-    body_area_rect.height = content_area_rect.height - url_area_rect.height;
-
-    input_area_rect = HorizontalSplit(body_area_rect, 0.5);
-    result_area_rect = RightOf(input_area_rect, 0);
-    result_area_rect.width = body_area_rect.width - input_area_rect.width;
-
-    input_tab_rect = (Rectangle){
-      .x = input_area_rect.x + padding,
-      .y = input_area_rect.y + padding,
-      .width = input_area_rect.width - (2 * padding),
-      .height = input_area_rect.height * 0.1
-    };
-
-    input_tab_item_rect = input_tab_rect;
-    input_tab_item_rect.width = input_tab_rect.width / 4;
-
-    input_body_rect = Below(input_tab_rect, 0);
-    input_body_rect.width = input_tab_rect.width;
-    input_body_rect.height = input_area_rect.height - input_tab_rect.height - (2 * padding);
-
-    // -- Result -- /
-    result_body_rect = (Rectangle){
-      .x = result_area_rect.x + padding,
-      .y = input_body_rect.y,
-      .width = result_area_rect.width - (2 * padding),
-      .height = input_body_rect.height
-    };
-
-    Vector2 mouse_position = GetMousePosition();
-    float mouse_wheel = GetMouseWheelMove();
-
-    // Reset text area state when tab changes
-    if (prev_input_tab != active_input_tab)
-    {
-      prev_input_tab = active_input_tab;
-    }
-
-    // Handle scroll wheel for history
-    if (InArea(mouse_position, history_list_area_rect) && 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_rect.height;
-      if (history_scroll_offset > 0) history_scroll_offset = 0;
-      if (history_scroll_offset < -max_scroll && max_scroll > 0) history_scroll_offset = -max_scroll;
-    }
-
-    // Reset
-    SetMouseCursor(MOUSE_CURSOR_DEFAULT);
-
-    // TODO: Move all for loop rect up here so it does not flicker.
-    if (
-        InArea(mouse_position, result_area_rect) ||
-        InArea(mouse_position, input_tab_rect) ||
-        InArea(mouse_position, url_input_bounds_rect) ||
-        InArea(mouse_position, url_enter_button_rect) ||
-        InArea(mouse_position, method_dropdown_rect) ||
-        InArea(mouse_position, logo_area_rect)
-    )
+    // 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);
 
-    if (Clicked(mouse_position, logo_area_rect))
+    // Logo click resets
+    if (CheckCollisionPointRec(mouse_pos, layout.logo_area) &&
+      IsMouseButtonPressed(MOUSE_BUTTON_LEFT))
       PostDog_Params_Reset();
-    
-    // --- Begin Drawing --- //
-    BeginDrawing();
-      ClearBackground(GetColor(GuiGetStyle(DEFAULT, BACKGROUND_COLOR)));
-
-      // DrawRectangleRec(history_sidebar_rect, Fade(GRAY, 0.1f));
-      DrawRectangleRounded(history_sidebar_rect, 0.5, 1, Fade(BLUE, 0.1f));
-
-      // DrawRectangleRec(logo_area_rect, Fade(BLUE, 0.2f));
-      Rectangle logo_image_rect = AddPadding(logo_area_rect, 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_rect = {
-        .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
-      };
-
-      Rectangle source_rect = { 0, 0, logo_texture.width, logo_texture.height };
-      DrawTexturePro(logo_texture, source_rect, dest_rect, (Vector2){0, 0}, 0.0f, WHITE);
-
-      BeginScissorMode(history_list_area_rect.x, history_list_area_rect.y, history_list_area_rect.width, history_list_area_rect.height);
-        for (int i = 0; i < total; i++)
-        {
-          HistoryItem *curr_history_items = i < new_history_items_length ?
-            &new_history_items[new_history_items_length - i - 1] : &history_items[i - new_history_items_length];
-
-          if (curr_history_items->deleted)
-            continue;
-
-          float diff = curr_history_items->rect.height*0.6;
-          DrawRectangleRounded(curr_history_items->rect, 0.5, 1, Fade(BLACK, 0.1f));
-          Rectangle filename_area_rect = curr_history_items->rect;
-
-          filename_area_rect.height -= diff;
-          Rectangle icon_area = Below(filename_area_rect, 0);
-          icon_area.height = diff;
-
-          // DrawRectangleRec(filename_area_rect, Fade(BLUE, 0.1f));
-          // DrawRectangleRec(icon_area, Fade(YELLOW, 0.1f));
-          // DrawRectangleRec(AddPadding(filename_area_rect, 5), Fade(BLUE, 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);
-
-          Rectangle temp = AddPadding(filename_area_rect, padding);
-
-          // ADD this back
-          // GuiDrawText(curr_history_items->title, temp, TEXT_ALIGN_CENTER, BLACK);
-          if (
-            InArea(mouse_position, icon_area_left_column) ||
-            InArea(mouse_position, icon_area_right_column)
-          )
-            SetMouseCursor(MOUSE_CURSOR_POINTING_HAND);
-
-          if (GuiButton(AddPadding(icon_area_left_column, padding), "view"))
-            PostDog_Load_File(curr_history_items->filename);
-          if (GuiButton(AddPadding(icon_area_right_column, padding), "delete"))
-          {
-            char delete_path[512];
-            if (!remove(PostDog_Construct_URL(curr_history_items->filename, delete_path, sizeof(delete_path))))
-               curr_history_items->deleted = TRUE;
-            else
-              fprintf(stderr, "Wasn't able to delete file: %s \n", curr_history_items->filename);
-          }
-        }
-      EndScissorMode();
 
-      if (total > 0)
-      {
-        float content_height = total * (item_height + padding);
-        if (content_height > history_list_area_rect.height)
-        {
-          float scrollbar_height = (history_list_area_rect.height / content_height) * history_list_area_rect.height;
-          float scrollbar_y = history_list_area_rect.y - (history_scroll_offset / content_height) * history_list_area_rect.height;
-          Rectangle scrollbar_rect = {
-            history_list_area_rect.x + history_list_area_rect.width - 5,
-            scrollbar_y,
-            5,
-            scrollbar_height
-          };
-          DrawRectangleRec(scrollbar_rect, Fade(WHITE, 0.5f));
-        }
-      }
-
-      // URL area Rect
-      DrawRectangleRec(url_area_rect, Fade(BLACK, 0.1f));
-
-      if (GuiTextArea(21, url_input_bounds_rect, url_input_text, DEFAULT_TEXT_BUFFER_LENGTH, url_input_edit, FALSE, g_text_area_arena))
-        url_input_edit = !url_input_edit;
-
-      if (url_input_edit)
-      {
-        if (IsKeyPressed(KEY_ENTER))
-        {
-          PostDog_Http_Thread_Request();
-          url_input_edit = !url_input_edit;
-        }
-      }
-
-      if (GuiButtonRounded(url_enter_button_rect, "New Request", 0.2, 1))
-        PostDog_Http_Thread_Request();
-
-      DrawRectangleSelectiveRounded(body_area_rect, 10, 10, ORANGE, TRUE, FALSE, FALSE, TRUE);
-      DrawRectangleSelectiveRounded(result_area_rect, 10, 10, WHITE, FALSE, TRUE, TRUE, FALSE);
-
-      // Input Tabs Rect
-      DrawRectangleRec(input_area_rect, Fade(BLUE, 0.1f));
-      DrawRectangleRec(input_tab_rect,  Fade(DARKBLUE, 0.1f));
-      GuiSetStyle(TOGGLE, GROUP_PADDING, 0);
+    // ====================================================================
+    // 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);
 
-      int text_area_id = TEXT_AREA_ID_INPUT_HEADER + active_input_tab;
-      if (GuiTextArea(text_area_id, input_body_rect, input_body_array[active_input_tab],
-                                   BODY_BUFFER_LENGTH, input_body_edit_mode, TRUE, g_text_area_arena))
-        input_body_edit_mode = !input_body_edit_mode;
-
-      if (active_input_tab != TAB_WEBSOCKET)
-      {
-        WS_BREAK = TRUE;
-        if (websocket_thread_id)
-          PostDog_Websocket_Destroy(websocket_thread_id);
-      }
-      else
-      {
-        WS_BREAK = FALSE;
-        Rectangle temp = {
-          .x = input_body_rect.x + input_body_rect.width - (3 * padding) - 100,
-          .y = input_body_rect.y + input_body_rect.height - (3 * padding) - 20,
-          .width = 100,
-          .height = 40
-        };
-        if (GuiButton(temp, "Send") || (input_body_edit_mode && IsKeyDown(KEY_LEFT_SHIFT) && IsKeyDown(KEY_ENTER)))
-        {
-          if (!ws)
-            websocket_thread_id = PostDog_Websocket_Start_Thread();
-          usleep(10000);
-          PostDog_Websocket_Send();
-        }
-      }
-
-
-      GuiToggleGroup(input_tab_item_rect, "Header;Body;Get Param;Websocket", &active_input_tab);
-      PostDog_Update_URL();
-
-      // TODO: Add animations.
-      DrawRectangleRec(result_area_rect, LOADING ? Fade(RED, 0.1f) : Fade(GREEN, 0.1f));
-      boolean result_toggle = GuiTextArea(TEXT_AREA_ID_RESULT, result_body_rect, result_text,
-                                          RESULT_BUFFER_LENGTH, result_body_edit_mode, TRUE, g_text_area_arena);
-
-      if (result_toggle)
-        result_body_edit_mode = !result_body_edit_mode;
-
-      if (GuiDropdownBoxRounded(url_text_bounds_rect, "GET;POST;PUT;DELETE", &active_method_dropdown, method_edit, 0.2, 1))
-        method_edit = !method_edit;
     EndDrawing();
   }
 
+  // ========================================================================
+  // CLEANUP
+  // ========================================================================
   GuiTextAreaResetAllStates();
   Dowa_Arena_Free(g_text_area_arena);
 
-  // Cleanup libuv
   uv_mutex_destroy(&history_mutex);
   uv_loop_close(main_loop);
 
+  UnloadTexture(logo_texture);
   CloseWindow();
+
   return 0;
 }