#
tokens: 48180/50000 11/104 files (page 2/8)
lines: off (toggle) GitHub
raw markdown copy
This is page 2 of 8. Use http://codebase.md/moisnx/arc?page={x} to view the full context.

# Directory Structure

```
├── .clang-format
├── .config
│   └── arceditor
│       ├── config.yaml
│       ├── keybinds.conf
│       └── themes
│           ├── catppuccin-mocha.theme
│           ├── cyberpunk-neon.theme
│           ├── default.theme
│           ├── dracula.theme
│           ├── github_dark.theme
│           ├── gruvbox_dark.theme
│           ├── gruvbox_light.theme
│           ├── high_constrast_dark.theme
│           ├── monokai.theme
│           ├── onedark.theme
│           ├── solarized_dark.theme
│           ├── solarized_light.theme
│           ├── tokyo_night.theme
│           └── vscode_light.theme
├── .github
│   └── assets
│       └── screenshot.gif
├── .gitignore
├── .gitmessage
├── .gitmodules
├── build.md
├── CMakeLists.txt
├── deps
│   └── tree-sitter-markdown
│       ├── .editorconfig
│       ├── .gitattributes
│       ├── .github
│       │   ├── screenshot.png
│       │   └── workflows
│       │       ├── ci.yml
│       │       ├── publish.yml
│       │       └── release.yml
│       ├── .gitignore
│       ├── binding.gyp
│       ├── bindings
│       │   ├── go
│       │   │   ├── binding_test.go
│       │   │   ├── markdown_inline.go
│       │   │   └── markdown.go
│       │   ├── node
│       │   │   ├── binding_test.js
│       │   │   ├── binding.cc
│       │   │   ├── index.d.ts
│       │   │   ├── index.js
│       │   │   └── inline.js
│       │   ├── python
│       │   │   ├── tests
│       │   │   │   └── test_binding.py
│       │   │   └── tree_sitter_markdown
│       │   │       ├── __init__.py
│       │   │       ├── __init__.pyi
│       │   │       ├── binding.c
│       │   │       └── py.typed
│       │   ├── rust
│       │   │   ├── benchmark.rs
│       │   │   ├── build.rs
│       │   │   ├── lib.rs
│       │   │   └── parser.rs
│       │   └── swift
│       │       ├── .gitignore
│       │       └── TreeSitterMarkdownTests
│       │           └── TreeSitterMarkdownTests.swift
│       ├── Cargo.toml
│       ├── CMakeLists.txt
│       ├── common
│       │   ├── common.js
│       │   ├── common.mak
│       │   └── html_entities.json
│       ├── CONTRIBUTING.md
│       ├── go.mod
│       ├── LICENSE
│       ├── Makefile
│       ├── package-lock.json
│       ├── package.json
│       ├── Package.resolved
│       ├── Package.swift
│       ├── pyproject.toml
│       ├── README.md
│       ├── scripts
│       │   ├── build.js
│       │   └── test.js
│       ├── setup.py
│       ├── tree-sitter-markdown
│       │   ├── bindings
│       │   │   ├── c
│       │   │   │   ├── tree-sitter-markdown.h
│       │   │   │   └── tree-sitter-markdown.pc.in
│       │   │   └── swift
│       │   │       └── TreeSitterMarkdown
│       │   │           └── markdown.h
│       │   ├── CMakeLists.txt
│       │   ├── grammar.js
│       │   ├── Makefile
│       │   ├── package.json
│       │   ├── queries
│       │   │   ├── highlights.scm
│       │   │   └── injections.scm
│       │   ├── src
│       │   │   ├── grammar.json
│       │   │   ├── node-types.json
│       │   │   ├── parser.c
│       │   │   ├── scanner.c
│       │   │   └── tree_sitter
│       │   │       ├── alloc.h
│       │   │       ├── array.h
│       │   │       └── parser.h
│       │   └── test
│       │       └── corpus
│       │           ├── extension_minus_metadata.txt
│       │           ├── extension_pipe_table.txt
│       │           ├── extension_plus_metadata.txt
│       │           ├── extension_task_list.txt
│       │           ├── failing.txt
│       │           ├── issues.txt
│       │           └── spec.txt
│       ├── tree-sitter-markdown-inline
│       │   ├── bindings
│       │   │   ├── c
│       │   │   │   ├── tree-sitter-markdown-inline.h
│       │   │   │   └── tree-sitter-markdown-inline.pc.in
│       │   │   └── swift
│       │   │       └── TreeSitterMarkdownInline
│       │   │           └── markdown_inline.h
│       │   ├── CMakeLists.txt
│       │   ├── grammar.js
│       │   ├── Makefile
│       │   ├── package.json
│       │   ├── queries
│       │   │   ├── highlights.scm
│       │   │   └── injections.scm
│       │   ├── src
│       │   │   ├── grammar.json
│       │   │   ├── node-types.json
│       │   │   ├── parser.c
│       │   │   ├── scanner.c
│       │   │   └── tree_sitter
│       │   │       ├── alloc.h
│       │   │       ├── array.h
│       │   │       └── parser.h
│       │   └── test
│       │       └── corpus
│       │           ├── extension_latex.txt
│       │           ├── extension_strikethrough.txt
│       │           ├── extension_wikilink.txt
│       │           ├── failing.txt
│       │           ├── issues.txt
│       │           ├── spec.txt
│       │           └── tags.txt
│       └── tree-sitter.json
├── LICENSE
├── Makefile
├── quickstart.md
├── README.md
├── src
│   ├── core
│   │   ├── buffer.cpp
│   │   ├── buffer.h
│   │   ├── config_manager.cpp
│   │   ├── config_manager.h
│   │   ├── editor_delta.h
│   │   ├── editor_validation.h
│   │   ├── editor.cpp
│   │   └── editor.h
│   ├── features
│   │   ├── markdown_state.h
│   │   ├── syntax_config_loader.cpp
│   │   ├── syntax_config_loader.h
│   │   ├── syntax_highlighter.cpp
│   │   └── syntax_highlighter.h
│   ├── main.cpp
│   └── ui
│       ├── input_handler.cpp
│       ├── input_handler.h
│       ├── renderer.cpp
│       ├── renderer.h
│       ├── style_manager.cpp
│       └── style_manager.h
└── treesitter
    ├── languages.yaml
    └── queries
        ├── _javascript
        │   ├── highlights.scm
        │   ├── locals.scm
        │   └── tags.scm
        ├── _jsx
        │   ├── highlights.scm
        │   ├── indents.scm
        │   └── textobjects.scm
        ├── _typescript
        │   ├── highlights.scm
        │   ├── indents.scm
        │   ├── locals.scm
        │   ├── tags.scm
        │   └── textobjects.scm
        ├── bash
        │   ├── highlights.scm
        │   ├── indents.scm
        │   ├── injections.scm
        │   ├── rainbows.scm
        │   ├── tags.scm
        │   └── textobjects.scm
        ├── c
        │   ├── highlights.scm
        │   ├── indents.scm
        │   ├── injections.scm
        │   ├── locals.scm
        │   ├── rainbows.scm
        │   ├── tags.scm
        │   └── textobjects.scm
        ├── cpp
        │   ├── highlights.scm
        │   ├── indents.scm
        │   ├── injections.scm
        │   ├── rainbows.scm
        │   ├── tags.scm
        │   └── textobjects.scm
        ├── css
        │   ├── highlights.scm
        │   ├── indents.scm
        │   ├── injections.scm
        │   └── rainbows.scm
        ├── ecma
        │   ├── highlights.scm
        │   ├── indents.scm
        │   ├── injections.scm
        │   ├── locals.scm
        │   ├── rainbows.scm
        │   ├── README.md
        │   └── textobjects.scm
        ├── go
        │   ├── highlights.scm
        │   ├── indents.scm
        │   ├── injections.scm
        │   ├── locals.scm
        │   ├── rainbows.scm
        │   ├── tags.scm
        │   └── textobjects.scm
        ├── javascript
        │   ├── highlights.scm
        │   ├── indents.scm
        │   ├── injections.scm
        │   ├── locals.scm
        │   ├── rainbows.scm
        │   ├── tags.scm
        │   └── textobjects.scm
        ├── markdown
        │   ├── highlights.scm
        │   ├── injections.scm
        │   └── tags.scm
        ├── markdown.inline
        │   ├── highlights.scm
        │   └── injections.scm
        ├── python
        │   ├── highlights.scm
        │   ├── indents.scm
        │   ├── injections.scm
        │   ├── locals.scm
        │   ├── rainbows.scm
        │   ├── tags.scm
        │   └── textobjects.scm
        ├── rust
        │   ├── highlights.scm
        │   ├── indents.scm
        │   ├── injections.scm
        │   ├── locals.scm
        │   ├── rainbows.scm
        │   ├── tags.scm
        │   └── textobjects.scm
        ├── toml
        │   ├── highlights.scm
        │   ├── injections.scm
        │   ├── rainbows.scm
        │   └── textobjects.scm
        ├── tsx
        │   ├── highlights.scm
        │   ├── indents.scm
        │   ├── injections.scm
        │   ├── locals.scm
        │   ├── rainbows.scm
        │   ├── tags.scm
        │   └── textobjects.scm
        ├── typescript
        │   ├── highlights.scm
        │   ├── indents.scm
        │   ├── injections.scm
        │   ├── locals.scm
        │   ├── rainbows.scm
        │   ├── tags.scm
        │   └── textobjects.scm
        ├── yaml
        │   ├── highlights.scm
        │   ├── indents.scm
        │   ├── injections.scm
        │   ├── rainbows.scm
        │   └── textobjects.scm
        └── zig
            ├── highlights.scm
            ├── indents.scm
            ├── injections.scm
            └── textobjects.scm
```

# Files

--------------------------------------------------------------------------------
/src/ui/renderer.cpp:
--------------------------------------------------------------------------------

```cpp
#include "renderer.h"
#include "src/ui/style_manager.h"
#include <algorithm>
#include <cstring>
#include <iostream>

Renderer::Renderer() : tab_size_(4) {}

Renderer::~Renderer() { restoreDefaultCursor(); }

void Renderer::renderEditor(const EditorState &state, const GapBuffer &buffer,
                            const SyntaxHighlighter *highlighter)
{
  // Set background color for entire screen
  setDefaultColors();
  clear();

  // Render main content area
  renderContent(state, buffer, highlighter);

  // Render status bar
  renderStatusBar(state, buffer);

  // Position cursor
  positionCursor(state.cursor, state.viewport);

  // Update cursor style based on mode
  updateCursorStyle(state.mode);

  refresh();
}

void Renderer::renderContent(const EditorState &state, const GapBuffer &buffer,
                             const SyntaxHighlighter *highlighter)
{
  const ViewportInfo &viewport = state.viewport;

  int line_num_width = calculateLineNumberWidth(buffer.getLineCount());
  int end_line =
      std::min(viewport.top + viewport.height, buffer.getLineCount());

  // Pre-calculate syntax highlighting for visible lines
  std::vector<std::vector<ColorSpan>> line_spans(end_line - viewport.top);
  if (highlighter)
  {
    for (int i = viewport.top; i < end_line; i++)
    {
      try
      {
        std::string line = buffer.getLine(i);
        std::string expanded_line = expandTabs(line, tab_size_);
        line_spans[i - viewport.top] =
            highlighter->getHighlightSpans(expanded_line, i, buffer);
      }
      catch (const std::exception &e)
      {
        std::cerr << "Syntax highlighting error on line " << i << ": "
                  << e.what() << std::endl;
        line_spans[i - viewport.top].clear();
      }
    }
  }

  // Render each visible line
  for (int i = viewport.top; i < end_line; i++)
  {
    int screen_row = i - viewport.top;
    move(screen_row, 0);

    // Set background for entire line
    setDefaultColors();

    // Render line number
    bool is_current_line = (state.cursor.line == i);
    renderLineNumbers(i, i + 1, state.cursor.line, line_num_width,
                      viewport.top);

    // Render line content
    std::string line = buffer.getLine(i);
    std::string expanded_line = expandTabs(line, tab_size_);
    const std::vector<ColorSpan> &spans =
        highlighter ? line_spans[i - viewport.top] : std::vector<ColorSpan>();

    renderLine(expanded_line, i, spans, viewport, state);

    // Clear to end of line with proper background
    setDefaultColors();
    clrtoeol();
  }

  // Fill remaining screen with background color
  setDefaultColors();
  for (int i = end_line - viewport.top; i < viewport.height; i++)
  {
    move(i, 0);
    clrtoeol();
  }
}

void Renderer::renderLine(const std::string &line, int line_number,
                          const std::vector<ColorSpan> &spans,
                          const ViewportInfo &viewport,
                          const EditorState &state)
{
  int content_width = viewport.width - viewport.content_start_col;

  for (int screen_col = 0; screen_col < content_width; screen_col++)
  {
    int file_col = viewport.left + screen_col;

    bool char_exists =
        (file_col >= 0 && file_col < static_cast<int>(line.length()));
    char ch = char_exists ? line[file_col] : ' ';

    // Filter non-printable characters
    if (char_exists && (ch < 32 || ch > 126))
    {
      ch = ' ';
    }

    // Check for selection
    bool is_selected = isPositionSelected(line_number, file_col, state);

    if (is_selected)
    {
      attron(COLOR_PAIR(SELECTION) | A_REVERSE);
      addch(ch);
      attroff(COLOR_PAIR(SELECTION) | A_REVERSE);
    }
    else
    {
      // Apply syntax highlighting
      bool color_applied = false;

      if (char_exists && !spans.empty())
      {
        for (const auto &span : spans)
        {
          if (file_col >= span.start && file_col < span.end)
          {
            if (span.colorPair >= 0 && span.colorPair < COLOR_PAIRS)
            {
              applyColorSpan(span, ch);
              color_applied = true;
              break;
            }
          }
        }
      }

      if (!color_applied)
      {
        setDefaultColors();
        addch(ch);
      }
    }
  }
}

void Renderer::renderStatusBar(const EditorState &state,
                               const GapBuffer &buffer)
{
  int rows, cols;
  getmaxyx(stdscr, rows, cols);
  int status_row = rows - 1;

  move(status_row, 0);

  // Set status bar background
  attrset(COLOR_PAIR(STATUS_BAR));
  clrtoeol();

  move(status_row, 0);

  // Render mode
  renderStatusMode(state.mode);

  // Render file info
  renderStatusFile(state.filename, state.is_modified);

  // Render position info (right-aligned)
  renderStatusPosition(state.cursor, buffer.getLineCount(), state.has_selection,
                       0); // TODO: calculate selection size
}

void Renderer::renderStatusMode(EditorMode mode)
{
  std::string mode_str;
  int mode_color;

  switch (mode)
  {
  case EditorMode::NORMAL:
    mode_str = " NORMAL ";
    mode_color = STATUS_BAR;
    break;
  case EditorMode::INSERT:
    mode_str = " INSERT ";
    mode_color = STATUS_BAR_ACTIVE;
    break;
  case EditorMode::VISUAL:
    mode_str = " VISUAL ";
    mode_color = STATUS_BAR_ACTIVE;
    break;
  }

  attron(COLOR_PAIR(mode_color) | A_BOLD);
  printw("%s", mode_str.c_str());
  attroff(COLOR_PAIR(mode_color) | A_BOLD);

  attron(COLOR_PAIR(STATUS_BAR));
  printw(" ");
}

void Renderer::renderStatusFile(const std::string &filename, bool is_modified)
{
  attron(COLOR_PAIR(STATUS_BAR_CYAN) | A_BOLD);
  if (filename.empty())
  {
    printw("[No Name]");
  }
  else
  {
    size_t last_slash = filename.find_last_of("/\\");
    std::string display_name = (last_slash != std::string::npos)
                                   ? filename.substr(last_slash + 1)
                                   : filename;
    printw("%s", display_name.c_str());
  }
  attroff(COLOR_PAIR(STATUS_BAR_CYAN) | A_BOLD);

  if (is_modified)
  {
    attron(COLOR_PAIR(STATUS_BAR_ACTIVE) | A_BOLD);
    printw(" [+]");
    attroff(COLOR_PAIR(STATUS_BAR_ACTIVE) | A_BOLD);
  }
}

void Renderer::renderStatusPosition(const CursorInfo &cursor, int total_lines,
                                    bool has_selection, int selection_size)
{
  int rows, cols;
  getmaxyx(stdscr, rows, cols);
  int status_row = rows - 1;

  char position_info[256];
  int percentage =
      total_lines == 0 ? 0 : ((cursor.line + 1) * 100 / total_lines);

  if (has_selection)
  {
    snprintf(position_info, sizeof(position_info),
             "[selection] %d:%d %d/%d %d%% ", cursor.line + 1, cursor.col + 1,
             cursor.line + 1, total_lines, percentage);
  }
  else
  {
    snprintf(position_info, sizeof(position_info), "%d:%d %d/%d %d%% ",
             cursor.line + 1, cursor.col + 1, cursor.line + 1, total_lines,
             percentage);
  }

  int info_len = strlen(position_info);
  int current_pos = getcurx(stdscr);
  int right_start = cols - info_len;

  if (right_start <= current_pos)
  {
    right_start = current_pos + 2;
  }

  // Fill middle space
  attron(COLOR_PAIR(STATUS_BAR));
  for (int i = current_pos; i < right_start && i < cols; i++)
  {
    move(status_row, i);
    addch(' ');
  }

  // Render position info
  if (right_start < cols)
  {
    move(status_row, right_start);
    attron(COLOR_PAIR(STATUS_BAR_YELLOW) | A_BOLD);
    printw("%s", position_info);
    attroff(COLOR_PAIR(STATUS_BAR_YELLOW) | A_BOLD);
  }
}

void Renderer::renderLineNumbers(int start_line, int end_line, int current_line,
                                 int line_num_width, int viewport_top)
{
  int line_index = start_line - viewport_top;
  bool is_current = (start_line == current_line);

  int color_pair = is_current ? LINE_NUMBERS_ACTIVE : LINE_NUMBERS;
  attron(COLOR_PAIR(color_pair));
  printw("%*d ", line_num_width, start_line + 1);
  attroff(COLOR_PAIR(color_pair));

  // Separator
  attron(COLOR_PAIR(LINE_NUMBERS_DIM));
  addch(' ');
  attroff(COLOR_PAIR(LINE_NUMBERS_DIM));
  addch(' ');
}

void Renderer::positionCursor(const CursorInfo &cursor,
                              const ViewportInfo &viewport)
{
  int screen_row = cursor.line - viewport.top;
  if (screen_row >= 0 && screen_row < viewport.height)
  {
    int screen_col = viewport.content_start_col + cursor.col - viewport.left;

    int rows, cols;
    getmaxyx(stdscr, rows, cols);

    if (screen_col >= viewport.content_start_col && screen_col < cols)
    {
      move(screen_row, screen_col);
    }
    else
    {
      move(screen_row, viewport.content_start_col);
    }
  }
}

void Renderer::updateCursorStyle(EditorMode mode)
{
  switch (mode)
  {
  case EditorMode::NORMAL:
    printf("\033[2 q"); // Block cursor
    break;
  case EditorMode::INSERT:
    printf("\033[6 q"); // Vertical bar cursor
    break;
  case EditorMode::VISUAL:
    printf("\033[4 q"); // Underline cursor
    break;
  }
  fflush(stdout);
}

void Renderer::restoreDefaultCursor()
{
  printf("\033[0 q");
  fflush(stdout);
}

void Renderer::clear() { ::clear(); }

void Renderer::refresh() { ::refresh(); }

void Renderer::handleResize()
{
  clear();
  refresh();
}

Renderer::ViewportInfo Renderer::calculateViewport() const
{
  ViewportInfo viewport;
  int rows, cols;
  getmaxyx(stdscr, rows, cols);

  viewport.height = rows - 1; // Reserve last row for status bar
  viewport.width = cols;

  // Calculate content start column (after line numbers)
  int max_lines = 1000; // Reasonable default, should be passed as parameter
  int line_num_width = calculateLineNumberWidth(max_lines);
  viewport.content_start_col = line_num_width + 3; // +3 for space and separator

  return viewport;
}

int Renderer::calculateLineNumberWidth(int max_line) const
{
  if (max_line <= 0)
    return 1;

  int width = 0;
  int num = max_line;
  while (num > 0)
  {
    width++;
    num /= 10;
  }
  return std::max(width, 3); // Minimum width of 3
}

bool Renderer::isPositionSelected(int line, int col,
                                  const EditorState &state) const
{
  if (!state.has_selection)
  {
    return false;
  }

  int start_line = state.selection_start_line;
  int start_col = state.selection_start_col;
  int end_line = state.selection_end_line;
  int end_col = state.selection_end_col;

  // Normalize selection
  if (start_line > end_line || (start_line == end_line && start_col > end_col))
  {
    std::swap(start_line, end_line);
    std::swap(start_col, end_col);
  }

  if (line < start_line || line > end_line)
  {
    return false;
  }

  if (start_line == end_line)
  {
    return col >= start_col && col < end_col;
  }
  else if (line == start_line)
  {
    return col >= start_col;
  }
  else if (line == end_line)
  {
    return col < end_col;
  }
  else
  {
    return true;
  }
}

std::string Renderer::expandTabs(const std::string &line, int tab_size) const
{
  std::string result;
  for (char c : line)
  {
    if (c == '\t')
    {
      int spaces_to_add = tab_size - (result.length() % tab_size);
      result.append(spaces_to_add, ' ');
    }
    else if (c >= 32 && c <= 126)
    {
      result += c;
    }
    else
    {
      result += ' ';
    }
  }
  return result;
}

void Renderer::applyColorSpan(const ColorSpan &span, char ch)
{
  int attrs = COLOR_PAIR(span.colorPair);
  if (span.attribute != 0)
  {
    attrs |= span.attribute;
  }

  attron(attrs);
  addch(ch);
  attroff(attrs);
}

void Renderer::setDefaultColors()
{
  attrset(COLOR_PAIR(0)); // Use default background
}
```

--------------------------------------------------------------------------------
/src/core/buffer.cpp:
--------------------------------------------------------------------------------

```cpp
#include "buffer.h"
#include <algorithm>
#include <cstring>
#include <fstream>
#include <iostream>
#include <stdexcept>
#include <vector>

const size_t GapBuffer::DEFAULT_GAP_SIZE;
const size_t GapBuffer::MIN_GAP_SIZE;
GapBuffer::GapBuffer()
    : gapStart(0), gapSize(DEFAULT_GAP_SIZE), lineIndexDirty(true)
{
  buffer.resize(DEFAULT_GAP_SIZE);
}

GapBuffer::GapBuffer(const std::string &initialText) : GapBuffer()
{
  if (!initialText.empty())
  {
    insertText(0, initialText);
  }
}

bool GapBuffer::loadFromFile(const std::string &filename)
{
  // --- Step 1: Fast I/O (Read entire file into memory) ---
  std::ifstream file(filename, std::ios::binary | std::ios::ate);
  if (!file.is_open())
  {
    return false;
  }

  std::streamsize fileSize = file.tellg();
  file.seekg(0, std::ios::beg);

  // Handle empty file case
  if (fileSize <= 0)
  {
    clear();
    insertChar(0, '\n');
    return true;
  }

  std::vector<char> content(fileSize);
  if (!file.read(content.data(), fileSize))
  {
    return false;
  }
  file.close();

  // --- Step 2: Check if normalization is needed ---
  const char *src = content.data();
  bool has_carriage_returns = false;

  // OPTIMIZATION: Quick scan for \r (memchr is highly optimized)
  if (std::memchr(src, '\r', fileSize) != nullptr)
  {
    has_carriage_returns = true;
  }

  // --- Step 3: Clear and Initial Buffer Sizing ---
  clear();

  if (!has_carriage_returns)
  {
    // FAST PATH: No normalization needed, direct copy
    buffer.resize(fileSize + DEFAULT_GAP_SIZE);
    char *dest = buffer.data() + DEFAULT_GAP_SIZE;

    std::memcpy(dest, src, fileSize);

    buffer.resize(fileSize + DEFAULT_GAP_SIZE);
    gapStart = 0;
    gapSize = DEFAULT_GAP_SIZE;
    invalidateLineIndex();

    return true;
  }

  // SLOW PATH: Normalization required
  buffer.resize(fileSize + DEFAULT_GAP_SIZE);
  char *dest = buffer.data() + DEFAULT_GAP_SIZE;
  size_t actualTextSize = 0;

  for (std::streamsize i = 0; i < fileSize; ++i)
  {
    char c = src[i];

    if (c == '\r')
    {
      // Windows \r\n sequence: skip \r, write \n
      if (i + 1 < fileSize && src[i + 1] == '\n')
      {
        c = '\n';
        i++; // Skip next \n
      }
      else
      {
        // Old Mac/Lone \r: Replace with \n
        c = '\n';
      }
    }

    *dest++ = c;
    actualTextSize++;
  }

  // --- Step 4: Finalize Gap Buffer State ---
  buffer.resize(actualTextSize + DEFAULT_GAP_SIZE);
  gapStart = 0;
  gapSize = DEFAULT_GAP_SIZE;
  invalidateLineIndex();

  return true;
}

bool GapBuffer::saveToFile(const std::string &filename) const
{
  std::ofstream file(filename);
  if (!file.is_open())
  {
    return false;
  }

  file << getText();
  return file.good();
}

void GapBuffer::loadFromString(const std::string &content)
{
  clear();

  if (content.empty())
  {
    insertChar(0, '\n');
    return;
  }

  // Resize buffer to hold content + gap
  size_t content_size = content.length();
  buffer.resize(content_size + DEFAULT_GAP_SIZE);

  // Copy content directly after gap
  std::memcpy(buffer.data() + DEFAULT_GAP_SIZE, content.data(), content_size);

  gapStart = 0;
  gapSize = DEFAULT_GAP_SIZE;

  // Mark index as dirty - will rebuild on first access
  invalidateLineIndex();
}

void GapBuffer::clear()
{
  buffer.clear();
  buffer.resize(DEFAULT_GAP_SIZE);
  gapStart = 0;
  gapSize = DEFAULT_GAP_SIZE;
  invalidateLineIndex();
}

int GapBuffer::getLineCount() const
{
  if (lineIndexDirty)
  {
    rebuildLineIndex();
  }
  return static_cast<int>(lineIndex.size());
}

std::string GapBuffer::getLine(int lineNum) const
{
  if (lineIndexDirty)
  {
    rebuildLineIndex();
  }

  if (lineNum < 0 || lineNum >= static_cast<int>(lineIndex.size()))
  {
    return "";
  }

  size_t lineStart = lineIndex[lineNum];
  size_t lineEnd;

  if (lineNum + 1 < static_cast<int>(lineIndex.size()))
  {
    lineEnd = lineIndex[lineNum + 1] - 1; // -1 to exclude newline
  }
  else
  {
    lineEnd = textSize();
  }

  if (lineEnd <= lineStart)
  {
    return "";
  }

  std::string result;
  result.reserve(lineEnd - lineStart);

  for (size_t pos = lineStart; pos < lineEnd; ++pos)
  {
    result += charAt(pos);
  }

  return result;
}

size_t GapBuffer::getLineLength(int lineNum) const
{
  if (lineIndexDirty)
  {
    rebuildLineIndex();
  }

  if (lineNum < 0 || lineNum >= static_cast<int>(lineIndex.size()))
  {
    return 0;
  }

  size_t lineStart = lineIndex[lineNum];
  size_t lineEnd;

  if (lineNum + 1 < static_cast<int>(lineIndex.size()))
  {
    lineEnd = lineIndex[lineNum + 1] - 1; // -1 to exclude the newline
  }
  else
  {
    lineEnd = textSize();
  }

  return (lineEnd > lineStart) ? lineEnd - lineStart : 0;
}

bool GapBuffer::isEmpty() const { return textSize() == 0; }

size_t GapBuffer::lineColToPos(int line, int col) const
{
  if (lineIndexDirty)
  {
    rebuildLineIndex();
  }

  if (line < 0 || line >= static_cast<int>(lineIndex.size()))
  {
    return textSize();
  }

  size_t lineStart = lineIndex[line];
  size_t maxCol = getLineLength(line);

  size_t actualCol = std::min(static_cast<size_t>(std::max(0, col)), maxCol);
  return lineStart + actualCol;
}

std::pair<int, int> GapBuffer::posToLineCol(size_t pos) const
{
  if (lineIndexDirty)
  {
    rebuildLineIndex();
  }

  pos = std::min(pos, textSize());

  // Binary search to find the line
  int line = 0;
  for (int i = static_cast<int>(lineIndex.size()) - 1; i >= 0; --i)
  {
    if (pos >= lineIndex[i])
    {
      line = i;
      break;
    }
  }

  int col = static_cast<int>(pos - lineIndex[line]);
  return std::make_pair(line, col);
}

void GapBuffer::insertChar(size_t pos, char c)
{
  pos = std::min(pos, textSize());
  moveGapTo(pos);

  if (gapSize == 0)
  {
    expandGap();
  }

  buffer[gapStart] = c;
  gapStart++;
  gapSize--;

  if (c == '\n')
  {
    invalidateLineIndex();
  }
}

void GapBuffer::insertText(size_t pos, const std::string &text)
{
  if (text.empty())
    return;

  pos = std::min(pos, textSize());
  moveGapTo(pos);

  if (gapSize < text.size())
  {
    expandGap(text.size());
  }

  bool hasNewlines = false;
  for (char c : text)
  {
    buffer[gapStart] = c;
    gapStart++;
    gapSize--;

    if (c == '\n')
    {
      hasNewlines = true;
    }
  }

  if (hasNewlines)
  {
    invalidateLineIndex();
  }
}

void GapBuffer::deleteChar(size_t pos) { deleteRange(pos, 1); }

void GapBuffer::deleteRange(size_t start, size_t length)
{
  if (length == 0)
    return;

  start = std::min(start, textSize());
  length = std::min(length, textSize() - start);

  if (length == 0)
    return;

  // Check if we're deleting newlines
  bool hasNewlines = false;
  for (size_t i = start; i < start + length; ++i)
  {
    if (charAt(i) == '\n')
    {
      hasNewlines = true;
      break;
    }
  }

  moveGapTo(start);
  gapSize += length;

  if (hasNewlines)
  {
    invalidateLineIndex();
  }
}

void GapBuffer::insertLine(int lineNum, const std::string &line)
{
  size_t pos = lineColToPos(lineNum, 0);
  size_t lineLength = line.length() + 1; // +1 for newline

  // Insert the text
  insertText(pos, line + "\n");

  // OPTIMIZATION: Update line index incrementally instead of rebuilding
  if (!lineIndexDirty && lineNum < static_cast<int>(lineIndex.size()))
  {
    // Insert new line offset at position lineNum + 1
    lineIndex.insert(lineIndex.begin() + lineNum + 1, pos + lineLength);

    // Shift all subsequent offsets by the length of inserted content
    for (size_t i = lineNum + 2; i < lineIndex.size(); ++i)
    {
      lineIndex[i] += lineLength;
    }
  }
  else
  {
    // Fallback: mark index as dirty for rebuild
    invalidateLineIndex();
  }
}

void GapBuffer::deleteLine(int lineNum)
{
  if (lineNum < 0 || lineNum >= getLineCount())
    return;

  size_t lineStart = lineColToPos(lineNum, 0);
  size_t lineLength = getLineLength(lineNum);

  // Include the newline character if it exists
  bool has_newline = (lineNum < getLineCount() - 1);
  if (has_newline)
  {
    lineLength++;
  }

  // Delete the range
  deleteRange(lineStart, lineLength);

  // OPTIMIZATION: Update line index incrementally
  if (!lineIndexDirty && lineNum + 1 < static_cast<int>(lineIndex.size()))
  {
    // Remove line offset at position lineNum + 1
    lineIndex.erase(lineIndex.begin() + lineNum + 1);

    // Shift all subsequent offsets back by the deleted length
    for (size_t i = lineNum + 1; i < lineIndex.size(); ++i)
    {
      lineIndex[i] -= lineLength;
    }
  }
  else
  {
    // Fallback: mark index as dirty for rebuild
    invalidateLineIndex();
  }
}

void GapBuffer::replaceLine(int lineNum, const std::string &newLine)
{
  if (lineNum < 0 || lineNum >= getLineCount())
    return;

  size_t lineStart = lineColToPos(lineNum, 0);
  size_t oldLineLength = getLineLength(lineNum);

  // Check if the new line contains newlines
  bool newLineHasNewlines = (newLine.find('\n') != std::string::npos);

  if (newLineHasNewlines)
  {
    // Complex case: line split - must rebuild index
    moveGapTo(lineStart);
    if (gapSize < newLine.length())
    {
      expandGap(newLine.length());
    }

    gapSize += oldLineLength;
    for (char c : newLine)
    {
      buffer[gapStart] = c;
      gapStart++;
      gapSize--;
    }

    invalidateLineIndex();
    return;
  }

  // Simple case: Same line, just different text
  moveGapTo(lineStart);

  if (gapSize < newLine.length())
  {
    expandGap(newLine.length());
  }

  gapSize += oldLineLength;

  for (char c : newLine)
  {
    buffer[gapStart] = c;
    gapStart++;
    gapSize--;
  }

  // CRITICAL OPTIMIZATION: Update line index incrementally
  if (!lineIndexDirty && lineNum + 1 < static_cast<int>(lineIndex.size()))
  {
    // Adjust all subsequent line offsets by the length difference
    int lengthDiff =
        static_cast<int>(newLine.length()) - static_cast<int>(oldLineLength);

    if (lengthDiff != 0)
    {
      for (size_t i = lineNum + 1; i < lineIndex.size(); ++i)
      {
        lineIndex[i] += lengthDiff;
      }
    }
    // Line index is now up-to-date, no need to invalidate!
  }
  else if (lineIndexDirty)
  {
    // Already dirty, no change needed
  }
}

// std::string GapBuffer::getText() const
// {
//   std::string result;
//   result.reserve(textSize());

//   // Add text before gap
//   for (size_t i = 0; i < gapStart; ++i)
//   {
//     result += buffer[i];
//   }

//   // Add text after gap
//   for (size_t i = gapEnd(); i < buffer.size(); ++i)
//   {
//     result += buffer[i];
//   }

//   return result;
// }

// In buffer.cpp, inside GapBuffer::getText()

std::string GapBuffer::getText() const
{
  // Build line index ONCE if needed (for future getLine() calls)
  if (lineIndexDirty)
  {
    rebuildLineIndex();
  }

  // The size of the final string *must* match textSize()
  size_t text_size = textSize();
  std::string result;
  result.reserve(text_size);

  // Append text before gap
  result.append(buffer.data(), gapStart);

  // Append text after gap
  size_t afterGapLength = text_size - gapStart;
  if (afterGapLength > 0 && gapEnd() < buffer.size())
  {
    result.append(buffer.data() + gapEnd(), afterGapLength);
  }

  return result;
}

std::string GapBuffer::getTextRange(size_t start, size_t length) const
{
  start = std::min(start, textSize());
  length = std::min(length, textSize() - start);

  std::string result;
  result.reserve(length);

  for (size_t i = 0; i < length; ++i)
  {
    result += charAt(start + i);
  }

  return result;
}

size_t GapBuffer::size() const { return textSize(); }

size_t GapBuffer::getBufferSize() const { return buffer.size(); }

// In buffer.cpp

void GapBuffer::moveGapTo(size_t pos)
{
  if (pos == gapStart)
    return;

  // Get the base pointer once
  char *base_ptr = buffer.data();

  if (pos < gapStart)
  {
    // Move gap left - move text from before new gap position to after gap
    size_t moveSize = gapStart - pos;
    size_t gapEndPos = gapEnd();

    // Move text from [pos] to [gapEndPos - moveSize]
    // This moves the text segment that was at pos and pushes it to the end of
    // the text segment after the gap
    std::memmove(base_ptr + gapEndPos - moveSize, base_ptr + pos, moveSize);

    gapStart = pos;
  }
  else
  {
    // Move gap right - move text from after gap to before new gap position
    size_t moveSize = pos - gapStart;
    size_t gapEndPos = gapEnd();

    // Move text from [gapEndPos] to [gapStart]
    // This pulls the text segment that was after the gap and fills the newly
    // created gap space
    std::memmove(base_ptr + gapStart, base_ptr + gapEndPos, moveSize);

    gapStart = pos;
  }

  // IMPORTANT: The gap size must remain the same, but its position is now
  // correct
}

void GapBuffer::expandGap(size_t minSize)
{
  size_t newGapSize = std::max(minSize, std::max(gapSize * 2, MIN_GAP_SIZE));
  size_t oldBufferSize = buffer.size();
  size_t newBufferSize = oldBufferSize + newGapSize - gapSize;

  // Create new buffer
  std::vector<char> newBuffer(newBufferSize);

  // Copy text before gap
  std::copy(buffer.begin(), buffer.begin() + gapStart, newBuffer.begin());

  // Copy text after gap
  size_t afterGapSize = oldBufferSize - gapEnd();
  std::copy(buffer.begin() + gapEnd(), buffer.end(),
            newBuffer.begin() + gapStart + newGapSize);

  buffer = std::move(newBuffer);
  gapSize = newGapSize;
}

void GapBuffer::rebuildLineIndex() const
{
  lineIndex.clear();
  lineIndex.push_back(0); // First line always starts at position 0

  for (size_t pos = 0; pos < textSize(); ++pos)
  {
    if (charAt(pos) == '\n')
    {
      lineIndex.push_back(pos + 1);
    }
  }

  // If the buffer doesn't end with a newline, we still have the last line
  // The line index is already correct as we added positions after each newline

  lineIndexDirty = false;
}

void GapBuffer::invalidateLineIndex() { lineIndexDirty = true; }

char GapBuffer::charAt(size_t pos) const
{
  if (pos >= textSize())
  {
    return '\0';
  }

  if (pos < gapStart)
  {
    return buffer[pos];
  }
  else
  {
    // Position is after gap, so add gap size to skip over it
    return buffer[pos + gapSize];
  }
}
```

--------------------------------------------------------------------------------
/src/main.cpp:
--------------------------------------------------------------------------------

```cpp
// src/main.cpp
#include "src/core/config_manager.h"
#include "src/core/editor.h"
#include "src/features/syntax_highlighter.h"
#include "src/ui/input_handler.h"
#include "src/ui/style_manager.h"
#include <algorithm>
#include <cstdlib>
#include <filesystem>
#include <iostream>
#include <vector>

#ifdef _WIN32
#include <curses.h>
#else
#include <csignal>
#include <ncurses.h>
#include <termios.h>
#include <unistd.h>
#endif

// void disableXonXoff();
// void disableSignalHandling();
// void restoreSignalHandling();
bool initializeNcurses();
bool initializeThemes();
void setupMouse();
void cleanupMouse();

enum class BenchmarkMode
{
  NONE,
  STARTUP_ONLY,        // Just ncurses + theme init (your current --quit)
  STARTUP_INTERACTIVE, // Full init to first input ready (RECOMMENDED)
  FILE_LOAD,           // Measure file loading separately
  FULL_CYCLE           // Complete startup + single operation + exit
};

struct BenchmarkResult
{
  std::chrono::milliseconds init_time;
  std::chrono::milliseconds theme_time;
  std::chrono::milliseconds editor_creation_time;
  std::chrono::milliseconds file_load_time;
  std::chrono::milliseconds first_render_time;
  std::chrono::milliseconds syntax_highlight_time;
  std::chrono::milliseconds total_time;

  void print(std::ostream &os) const
  {
    os << "=== Benchmark Results ===" << std::endl;
    os << "Init (ncurses):        " << init_time.count() << "ms" << std::endl;
    os << "Theme load:            " << theme_time.count() << "ms" << std::endl;
    os << "Editor creation:       " << editor_creation_time.count() << "ms"
       << std::endl;
    os << "File load:             " << file_load_time.count() << "ms"
       << std::endl;
    os << "Syntax highlighting:   " << syntax_highlight_time.count() << "ms"
       << std::endl;
    os << "First render:          " << first_render_time.count() << "ms"
       << std::endl;
    os << "------------------------" << std::endl;
    os << "TOTAL (user-perceived): " << total_time.count() << "ms" << std::endl;
  }
};

BenchmarkResult runStartupInteractiveBenchmark(const std::string &filename,
                                               bool enable_syntax_highlighting)
{

  BenchmarkResult result{};
  auto start = std::chrono::high_resolution_clock::now();

  // Phase 1: Initialize ncurses
  //   disableXonXoff();
  // #ifndef _win32
  //   disableSignalHandling();
  // #endif
  if (!initializeNcurses())
  {
    throw std::runtime_error("ncurses init failed");
  }
  if (!initializeThemes())
  {
    endwin();
    throw std::runtime_error("theme init failed");
  }

  auto after_init = std::chrono::high_resolution_clock::now();
  result.init_time =
      std::chrono::duration_cast<std::chrono::milliseconds>(after_init - start);

  // Phase 2: Load theme
  std::string active_theme = ConfigManager::getActiveTheme();
  std::string theme_file = ConfigManager::getThemeFile(active_theme);
  if (!theme_file.empty())
  {
    g_style_manager.load_theme_from_file(theme_file);
  }

  auto after_theme = std::chrono::high_resolution_clock::now();
  result.theme_time = std::chrono::duration_cast<std::chrono::milliseconds>(
      after_theme - after_init);

  // Phase 3: Create syntax highlighter (if enabled)
  SyntaxHighlighter syntaxHighlighter;
  SyntaxHighlighter *highlighterPtr = nullptr;

  if (enable_syntax_highlighting)
  {
    std::string syntax_dir = ConfigManager::getSyntaxRulesDir();
    if (syntaxHighlighter.initialize(syntax_dir))
    {
      highlighterPtr = &syntaxHighlighter;
    }
  }

  auto after_highlighter_init = std::chrono::high_resolution_clock::now();

  // Phase 4: Create editor and load file
  Editor editor(highlighterPtr);

  auto after_editor_creation = std::chrono::high_resolution_clock::now();
  result.editor_creation_time =
      std::chrono::duration_cast<std::chrono::milliseconds>(
          after_editor_creation - after_theme);

  if (!editor.loadFile(filename))
  {
    endwin();
    throw std::runtime_error("Failed to load file");
  }

  auto after_file_load = std::chrono::high_resolution_clock::now();
  result.file_load_time = std::chrono::duration_cast<std::chrono::milliseconds>(
      after_file_load - after_editor_creation);

  // Phase 5: Initialize syntax highlighting for viewport
  if (highlighterPtr)
  {
    editor.initializeViewportHighlighting();
  }

  auto after_syntax = std::chrono::high_resolution_clock::now();
  result.syntax_highlight_time =
      std::chrono::duration_cast<std::chrono::milliseconds>(after_syntax -
                                                            after_file_load);

  // Phase 6: Render first display
  editor.setCursorMode();
  editor.display();
  wnoutrefresh(stdscr);

  auto after_render = std::chrono::high_resolution_clock::now();
  result.first_render_time =
      std::chrono::duration_cast<std::chrono::milliseconds>(after_render -
                                                            after_syntax);

  // This is the "interactive ready" point - user can now type
  result.total_time = std::chrono::duration_cast<std::chrono::milliseconds>(
      after_render - start);

  // Cleanup
  endwin();

  return result;
}

void flushInputQueue();

int main(int argc, char *argv[])
{
  if (argc < 2)
  {
    std::cerr << "Usage: " << argv[0] << " <filename> [options]" << std::endl;
    std::cerr << "\nBenchmark options:" << std::endl;
    std::cerr << "  --bench-startup          Benchmark startup to interactive"
              << std::endl;
    std::cerr
        << "  --bench-startup-nosyntax Same but without syntax highlighting"
        << std::endl;
    std::cerr << "  --bench-file-only        Benchmark only file loading"
              << std::endl;
    return 1;
  }

  // Parse command line arguments
  std::vector<std::string> args(argv, argv + argc);
  bool bench_quit = std::any_of(args.begin(), args.end(), [](const auto &arg)
                                { return arg == "--bench-quit"; });
  bool force_no_highlighting =
      std::any_of(args.begin(), args.end(),
                  [](const auto &arg) { return arg == "--none"; });
  bool quit_immediately =
      std::any_of(args.begin(), args.end(),
                  [](const auto &arg) { return arg == "--quit"; });

  //   Bechmark
  bool bench_startup = std::any_of(args.begin(), args.end(), [](const auto &arg)
                                   { return arg == "--bench-startup"; });
  bool bench_startup_nosyntax =
      std::any_of(args.begin(), args.end(), [](const auto &arg)
                  { return arg == "--bench-startup-nosyntax"; });

  std::string filename = std::filesystem::absolute(argv[1]).string();

  // Initialize config
  if (!ConfigManager::ensureConfigStructure())
  {
    std::cerr << "Warning: Failed to ensure config structure" << std::endl;
  }
  ConfigManager::copyProjectFilesToConfig();
  ConfigManager::loadConfig();

  if (bench_startup || bench_startup_nosyntax)
  {
    try
    {
      BenchmarkResult result =
          runStartupInteractiveBenchmark(filename, !bench_startup_nosyntax);

      result.print(std::cerr);
      return 0;
    }
    catch (const std::exception &e)
    {
      std::cerr << "Benchmark failed: " << e.what() << std::endl;
      return 1;
    }
  }
  // BENCHMARK PATH: Load file and quit immediately
  if (quit_immediately)
  {
    auto start = std::chrono::high_resolution_clock::now();

    // Initialize ncurses (users see this cost)
    //     disableXonXoff();
    // #ifndef _win32
    //     disableSignalHandling();
    // #endif
    if (!initializeNcurses())
      return 1;
    if (!initializeThemes())
      return 1;

    auto after_init = std::chrono::high_resolution_clock::now();

    // Load theme (users see this)
    std::string active_theme = ConfigManager::getActiveTheme();
    std::string theme_file = ConfigManager::getThemeFile(active_theme);
    if (!theme_file.empty())
    {
      g_style_manager.load_theme_from_file(theme_file);
    }

    auto after_theme = std::chrono::high_resolution_clock::now();

    // Render once (users see this)
    // editor.display();
    wnoutrefresh(stdscr);
    doupdate();
    auto after_render = std::chrono::high_resolution_clock::now();

    // Cleanup
    endwin();

    // Print timings to stderr (won't interfere with hyperfine)
    auto ms = [](auto s, auto e)
    {
      return std::chrono::duration_cast<std::chrono::milliseconds>(e - s)
          .count();
    };

    std::cerr << "Init: " << ms(start, after_init) << "ms, "
              << "Theme: " << ms(after_init, after_theme) << "ms, "
              << "Render: " << ms(after_theme, after_render) << "ms, "
              << "Total: " << ms(start, after_render) << "ms" << std::endl;

    return 0;
  }

  // Determine syntax mode (CLI args override config)
  SyntaxMode syntax_mode =
      force_no_highlighting ? SyntaxMode::NONE : ConfigManager::getSyntaxMode();

  // Create syntax highlighter
  SyntaxHighlighter syntaxHighlighter;
  SyntaxHighlighter *highlighterPtr = nullptr;

  if (syntax_mode != SyntaxMode::NONE)
  {
    std::string syntax_dir = ConfigManager::getSyntaxRulesDir();
    if (syntaxHighlighter.initialize(syntax_dir))
    {
      highlighterPtr = &syntaxHighlighter;
    }
    else
    {
      std::cerr << "Warning: Syntax highlighter init failed" << std::endl;
    }
  }

  // Create editor
  Editor editor(highlighterPtr);
  editor.setDeltaUndoEnabled(true);

  // Initialize delta group (even if disabled)
  editor.beginDeltaGroup();
  if (!editor.loadFile(filename))
  {
    std::cerr << "Warning: Could not open file " << filename << std::endl;
  }

  // Initialize ncurses
  //   disableXonXoff();
  // #ifndef _win32
  //   disableSignalHandling();
  // #endif
  if (!initializeNcurses())
  {
    return 1;
  }

  if (!initializeThemes())
  {
    endwin();
    return 1;
  }

  // Load theme
  std::string active_theme = ConfigManager::getActiveTheme();
  std::string theme_file = ConfigManager::getThemeFile(active_theme);
  if (!theme_file.empty())
  {
    if (!g_style_manager.load_theme_from_file(theme_file))
    {
      std::cerr << "FATAL: Theme load failed" << std::endl;
    }
  }

  setupMouse();

  if (highlighterPtr)
  {
    editor.initializeViewportHighlighting();
  }
  // Start editor
  InputHandler inputHandler(editor);
  editor.setCursorMode();
  editor.display();
  wnoutrefresh(stdscr);
  doupdate();
  curs_set(1); // Ensure cursor is visible

  // Handle --quit for testing
  if (quit_immediately)
  {
    cleanupMouse();
    attrset(A_NORMAL);
    curs_set(1);
    endwin();
    std::cerr << "First line: " << editor.getFirstLine() << std::endl;
    return 0;
  }

  if (highlighterPtr)
  {
    highlighterPtr->scheduleBackgroundParse(editor.getBuffer());
  }
  // Start config watching
  if (!ConfigManager::startWatchingConfig())
  {
    std::cerr << "Warning: Config watching failed" << std::endl;
  }

  // Main loop
  int key;
  bool running = true;
  while (running)
  {
    if (ConfigManager::isReloadPending())
    {
      curs_set(0); // Hide cursor
      editor.display();
      wnoutrefresh(stdscr);
      doupdate();
      editor.positionCursor(); // Position AFTER flush
      curs_set(1);             // Show cursor
    }

    key = getch();

    // if (key == 'q' || key == 'Q')
    // {
    //   //   if (editor.getMode() == EditorMode::NORMAL)
    //   //   {
    //   running = false;
    //   continue;
    //   //   }
    // }

    InputHandler::KeyResult result = inputHandler.handleKey(key);

    switch (result)
    {
    case InputHandler::KeyResult::QUIT:
      running = false;
      break;
    case InputHandler::KeyResult::REDRAW:
    case InputHandler::KeyResult::HANDLED:
      curs_set(0); // Hide during render
      editor.display();
      wnoutrefresh(stdscr);
      doupdate();
      editor.positionCursor(); // Position AFTER flush
      curs_set(1);             // Show immediately
      break;
    case InputHandler::KeyResult::NOT_HANDLED:
      //   std::cerr << "Input not handled" << std::endl;
      break;
    }
    // if (result != InputHandler::KeyResult::NOT_HANDLED)
    // {
    //   doupdate();
    // }
  }

  // Cleanup
  cleanupMouse();
  attrset(A_NORMAL);
  curs_set(1);
  endwin();
  return 0;
}

bool initializeNcurses()
{
  initscr();
  //   cbreak();
  raw();
  keypad(stdscr, TRUE); // This MUST be set for arrow keys
  noecho();
  curs_set(1);

#ifdef _WIN32
  // CRITICAL: Don't use timeout on Windows - causes ERR spam
  nodelay(stdscr, FALSE); // Blocking mode

  PDC_set_blink(FALSE);
  PDC_return_key_modifiers(TRUE);
  PDC_save_key_modifiers(TRUE);
  scrollok(stdscr, FALSE);
  leaveok(stdscr, FALSE);
  raw();
  meta(stdscr, TRUE);
  intrflush(stdscr, FALSE);
#else
  timeout(50);
#endif

  if (!has_colors())
  {
    endwin();
    std::cerr << "Error: Terminal does not support colors" << std::endl;
    return false;
  }

  if (start_color() == ERR)
  {
    endwin();
    std::cerr << "Error: Could not initialize colors" << std::endl;
    return false;
  }

  if (use_default_colors() == ERR)
  {
    assume_default_colors(COLOR_WHITE, COLOR_BLACK);
  }

  return true;
}

bool initializeThemes()
{
  g_style_manager.initialize();

  // Register the callback for future config changes
  ConfigManager::registerReloadCallback(
      []()
      {
        // 1. Get current theme from the *newly loaded* config
        std::string active_theme = ConfigManager::getActiveTheme();
        std::string theme_file = ConfigManager::getThemeFile(active_theme);

        if (!theme_file.empty())
        {
          // 2. Load the theme
          if (!g_style_manager.load_theme_from_file(theme_file))
          {
            std::cerr << "ERROR: Theme reload failed for: " << active_theme
                      << std::endl;
          }
        }
      });

  // NOTE: Initial theme load logic removed from here
  return true;
}

void flushInputQueue()
{
  nodelay(stdscr, TRUE); // Make getch() non-blocking
  while (getch() != ERR)
  {
    // Drain all pending input
  }
  nodelay(stdscr, FALSE); // Restore blocking mode
  timeout(50);            // Restore your timeout
}

void setupMouse()
{
  mousemask(ALL_MOUSE_EVENTS | REPORT_MOUSE_POSITION, NULL);
#ifndef _WIN32
  printf("\033[?1003h");
  fflush(stdout);
#endif
}

void cleanupMouse()
{
#ifndef _WIN32
  printf("\033[?1003l");
  fflush(stdout);
#endif
}
```

--------------------------------------------------------------------------------
/deps/tree-sitter-markdown/tree-sitter-markdown-inline/src/node-types.json:
--------------------------------------------------------------------------------

```json
[
  {
    "type": "backslash_escape",
    "named": true,
    "fields": {}
  },
  {
    "type": "code_span",
    "named": true,
    "fields": {},
    "children": {
      "multiple": true,
      "required": true,
      "types": [
        {
          "type": "code_span_delimiter",
          "named": true
        }
      ]
    }
  },
  {
    "type": "collapsed_reference_link",
    "named": true,
    "fields": {},
    "children": {
      "multiple": false,
      "required": false,
      "types": [
        {
          "type": "link_text",
          "named": true
        }
      ]
    }
  },
  {
    "type": "emphasis",
    "named": true,
    "fields": {},
    "children": {
      "multiple": true,
      "required": true,
      "types": [
        {
          "type": "backslash_escape",
          "named": true
        },
        {
          "type": "code_span",
          "named": true
        },
        {
          "type": "collapsed_reference_link",
          "named": true
        },
        {
          "type": "email_autolink",
          "named": true
        },
        {
          "type": "emphasis",
          "named": true
        },
        {
          "type": "emphasis_delimiter",
          "named": true
        },
        {
          "type": "entity_reference",
          "named": true
        },
        {
          "type": "full_reference_link",
          "named": true
        },
        {
          "type": "hard_line_break",
          "named": true
        },
        {
          "type": "html_tag",
          "named": true
        },
        {
          "type": "image",
          "named": true
        },
        {
          "type": "inline_link",
          "named": true
        },
        {
          "type": "latex_block",
          "named": true
        },
        {
          "type": "numeric_character_reference",
          "named": true
        },
        {
          "type": "shortcut_link",
          "named": true
        },
        {
          "type": "strikethrough",
          "named": true
        },
        {
          "type": "strong_emphasis",
          "named": true
        },
        {
          "type": "uri_autolink",
          "named": true
        }
      ]
    }
  },
  {
    "type": "full_reference_link",
    "named": true,
    "fields": {},
    "children": {
      "multiple": true,
      "required": true,
      "types": [
        {
          "type": "link_label",
          "named": true
        },
        {
          "type": "link_text",
          "named": true
        }
      ]
    }
  },
  {
    "type": "hard_line_break",
    "named": true,
    "fields": {}
  },
  {
    "type": "html_tag",
    "named": true,
    "fields": {}
  },
  {
    "type": "image",
    "named": true,
    "fields": {},
    "children": {
      "multiple": true,
      "required": false,
      "types": [
        {
          "type": "image_description",
          "named": true
        },
        {
          "type": "link_destination",
          "named": true
        },
        {
          "type": "link_label",
          "named": true
        },
        {
          "type": "link_title",
          "named": true
        }
      ]
    }
  },
  {
    "type": "image_description",
    "named": true,
    "fields": {},
    "children": {
      "multiple": true,
      "required": false,
      "types": [
        {
          "type": "backslash_escape",
          "named": true
        },
        {
          "type": "code_span",
          "named": true
        },
        {
          "type": "collapsed_reference_link",
          "named": true
        },
        {
          "type": "email_autolink",
          "named": true
        },
        {
          "type": "emphasis",
          "named": true
        },
        {
          "type": "entity_reference",
          "named": true
        },
        {
          "type": "full_reference_link",
          "named": true
        },
        {
          "type": "hard_line_break",
          "named": true
        },
        {
          "type": "html_tag",
          "named": true
        },
        {
          "type": "image",
          "named": true
        },
        {
          "type": "inline_link",
          "named": true
        },
        {
          "type": "latex_block",
          "named": true
        },
        {
          "type": "numeric_character_reference",
          "named": true
        },
        {
          "type": "shortcut_link",
          "named": true
        },
        {
          "type": "strikethrough",
          "named": true
        },
        {
          "type": "strong_emphasis",
          "named": true
        },
        {
          "type": "uri_autolink",
          "named": true
        }
      ]
    }
  },
  {
    "type": "inline",
    "named": true,
    "root": true,
    "fields": {},
    "children": {
      "multiple": true,
      "required": false,
      "types": [
        {
          "type": "backslash_escape",
          "named": true
        },
        {
          "type": "code_span",
          "named": true
        },
        {
          "type": "collapsed_reference_link",
          "named": true
        },
        {
          "type": "email_autolink",
          "named": true
        },
        {
          "type": "emphasis",
          "named": true
        },
        {
          "type": "entity_reference",
          "named": true
        },
        {
          "type": "full_reference_link",
          "named": true
        },
        {
          "type": "hard_line_break",
          "named": true
        },
        {
          "type": "html_tag",
          "named": true
        },
        {
          "type": "image",
          "named": true
        },
        {
          "type": "inline_link",
          "named": true
        },
        {
          "type": "latex_block",
          "named": true
        },
        {
          "type": "numeric_character_reference",
          "named": true
        },
        {
          "type": "shortcut_link",
          "named": true
        },
        {
          "type": "strikethrough",
          "named": true
        },
        {
          "type": "strong_emphasis",
          "named": true
        },
        {
          "type": "uri_autolink",
          "named": true
        }
      ]
    }
  },
  {
    "type": "inline_link",
    "named": true,
    "fields": {},
    "children": {
      "multiple": true,
      "required": false,
      "types": [
        {
          "type": "link_destination",
          "named": true
        },
        {
          "type": "link_text",
          "named": true
        },
        {
          "type": "link_title",
          "named": true
        }
      ]
    }
  },
  {
    "type": "latex_block",
    "named": true,
    "fields": {},
    "children": {
      "multiple": true,
      "required": true,
      "types": [
        {
          "type": "backslash_escape",
          "named": true
        },
        {
          "type": "latex_span_delimiter",
          "named": true
        }
      ]
    }
  },
  {
    "type": "link_destination",
    "named": true,
    "fields": {},
    "children": {
      "multiple": true,
      "required": false,
      "types": [
        {
          "type": "backslash_escape",
          "named": true
        },
        {
          "type": "entity_reference",
          "named": true
        },
        {
          "type": "numeric_character_reference",
          "named": true
        }
      ]
    }
  },
  {
    "type": "link_label",
    "named": true,
    "fields": {},
    "children": {
      "multiple": true,
      "required": false,
      "types": [
        {
          "type": "backslash_escape",
          "named": true
        },
        {
          "type": "entity_reference",
          "named": true
        },
        {
          "type": "numeric_character_reference",
          "named": true
        }
      ]
    }
  },
  {
    "type": "link_text",
    "named": true,
    "fields": {},
    "children": {
      "multiple": true,
      "required": false,
      "types": [
        {
          "type": "backslash_escape",
          "named": true
        },
        {
          "type": "code_span",
          "named": true
        },
        {
          "type": "email_autolink",
          "named": true
        },
        {
          "type": "emphasis",
          "named": true
        },
        {
          "type": "entity_reference",
          "named": true
        },
        {
          "type": "hard_line_break",
          "named": true
        },
        {
          "type": "html_tag",
          "named": true
        },
        {
          "type": "image",
          "named": true
        },
        {
          "type": "latex_block",
          "named": true
        },
        {
          "type": "numeric_character_reference",
          "named": true
        },
        {
          "type": "strikethrough",
          "named": true
        },
        {
          "type": "strong_emphasis",
          "named": true
        },
        {
          "type": "uri_autolink",
          "named": true
        }
      ]
    }
  },
  {
    "type": "link_title",
    "named": true,
    "fields": {},
    "children": {
      "multiple": true,
      "required": false,
      "types": [
        {
          "type": "backslash_escape",
          "named": true
        },
        {
          "type": "entity_reference",
          "named": true
        },
        {
          "type": "numeric_character_reference",
          "named": true
        }
      ]
    }
  },
  {
    "type": "shortcut_link",
    "named": true,
    "fields": {},
    "children": {
      "multiple": false,
      "required": true,
      "types": [
        {
          "type": "link_text",
          "named": true
        }
      ]
    }
  },
  {
    "type": "strikethrough",
    "named": true,
    "fields": {},
    "children": {
      "multiple": true,
      "required": true,
      "types": [
        {
          "type": "backslash_escape",
          "named": true
        },
        {
          "type": "code_span",
          "named": true
        },
        {
          "type": "collapsed_reference_link",
          "named": true
        },
        {
          "type": "email_autolink",
          "named": true
        },
        {
          "type": "emphasis",
          "named": true
        },
        {
          "type": "emphasis_delimiter",
          "named": true
        },
        {
          "type": "entity_reference",
          "named": true
        },
        {
          "type": "full_reference_link",
          "named": true
        },
        {
          "type": "hard_line_break",
          "named": true
        },
        {
          "type": "html_tag",
          "named": true
        },
        {
          "type": "image",
          "named": true
        },
        {
          "type": "inline_link",
          "named": true
        },
        {
          "type": "latex_block",
          "named": true
        },
        {
          "type": "numeric_character_reference",
          "named": true
        },
        {
          "type": "shortcut_link",
          "named": true
        },
        {
          "type": "strikethrough",
          "named": true
        },
        {
          "type": "strong_emphasis",
          "named": true
        },
        {
          "type": "uri_autolink",
          "named": true
        }
      ]
    }
  },
  {
    "type": "strong_emphasis",
    "named": true,
    "fields": {},
    "children": {
      "multiple": true,
      "required": true,
      "types": [
        {
          "type": "backslash_escape",
          "named": true
        },
        {
          "type": "code_span",
          "named": true
        },
        {
          "type": "collapsed_reference_link",
          "named": true
        },
        {
          "type": "email_autolink",
          "named": true
        },
        {
          "type": "emphasis",
          "named": true
        },
        {
          "type": "emphasis_delimiter",
          "named": true
        },
        {
          "type": "entity_reference",
          "named": true
        },
        {
          "type": "full_reference_link",
          "named": true
        },
        {
          "type": "hard_line_break",
          "named": true
        },
        {
          "type": "html_tag",
          "named": true
        },
        {
          "type": "image",
          "named": true
        },
        {
          "type": "inline_link",
          "named": true
        },
        {
          "type": "latex_block",
          "named": true
        },
        {
          "type": "numeric_character_reference",
          "named": true
        },
        {
          "type": "shortcut_link",
          "named": true
        },
        {
          "type": "strikethrough",
          "named": true
        },
        {
          "type": "strong_emphasis",
          "named": true
        },
        {
          "type": "uri_autolink",
          "named": true
        }
      ]
    }
  },
  {
    "type": "!",
    "named": false
  },
  {
    "type": "\"",
    "named": false
  },
  {
    "type": "#",
    "named": false
  },
  {
    "type": "$",
    "named": false
  },
  {
    "type": "%",
    "named": false
  },
  {
    "type": "&",
    "named": false
  },
  {
    "type": "'",
    "named": false
  },
  {
    "type": "(",
    "named": false
  },
  {
    "type": ")",
    "named": false
  },
  {
    "type": "*",
    "named": false
  },
  {
    "type": "+",
    "named": false
  },
  {
    "type": ",",
    "named": false
  },
  {
    "type": "-",
    "named": false
  },
  {
    "type": "-->",
    "named": false
  },
  {
    "type": ".",
    "named": false
  },
  {
    "type": "/",
    "named": false
  },
  {
    "type": ":",
    "named": false
  },
  {
    "type": ";",
    "named": false
  },
  {
    "type": "<",
    "named": false
  },
  {
    "type": "<!--",
    "named": false
  },
  {
    "type": "<![CDATA[",
    "named": false
  },
  {
    "type": "<?",
    "named": false
  },
  {
    "type": "=",
    "named": false
  },
  {
    "type": ">",
    "named": false
  },
  {
    "type": "?",
    "named": false
  },
  {
    "type": "?>",
    "named": false
  },
  {
    "type": "@",
    "named": false
  },
  {
    "type": "[",
    "named": false
  },
  {
    "type": "\\",
    "named": false
  },
  {
    "type": "]",
    "named": false
  },
  {
    "type": "]]>",
    "named": false
  },
  {
    "type": "^",
    "named": false
  },
  {
    "type": "_",
    "named": false
  },
  {
    "type": "`",
    "named": false
  },
  {
    "type": "code_span_delimiter",
    "named": true
  },
  {
    "type": "email_autolink",
    "named": true
  },
  {
    "type": "emphasis_delimiter",
    "named": true
  },
  {
    "type": "entity_reference",
    "named": true
  },
  {
    "type": "latex_span_delimiter",
    "named": true
  },
  {
    "type": "numeric_character_reference",
    "named": true
  },
  {
    "type": "uri_autolink",
    "named": true
  },
  {
    "type": "{",
    "named": false
  },
  {
    "type": "|",
    "named": false
  },
  {
    "type": "}",
    "named": false
  },
  {
    "type": "~",
    "named": false
  }
]
```

--------------------------------------------------------------------------------
/deps/tree-sitter-markdown/tree-sitter-markdown-inline/src/scanner.c:
--------------------------------------------------------------------------------

```cpp
#include "tree_sitter/parser.h"

#ifdef _MSC_VER
#define UNUSED __pragma(warning(suppress : 4101))
#else
#define UNUSED __attribute__((unused))
#endif

// For explanation of the tokens see grammar.js
typedef enum {
    ERROR,
    TRIGGER_ERROR,
    CODE_SPAN_START,
    CODE_SPAN_CLOSE,
    EMPHASIS_OPEN_STAR,
    EMPHASIS_OPEN_UNDERSCORE,
    EMPHASIS_CLOSE_STAR,
    EMPHASIS_CLOSE_UNDERSCORE,
    LAST_TOKEN_WHITESPACE,
    LAST_TOKEN_PUNCTUATION,
    STRIKETHROUGH_OPEN,
    STRIKETHROUGH_CLOSE,
    LATEX_SPAN_START,
    LATEX_SPAN_CLOSE,
    UNCLOSED_SPAN
} TokenType;

// Determines if a character is punctuation as defined by the markdown spec.
static bool is_punctuation(int32_t chr) {
    return (chr >= '!' && chr <= '/') || (chr >= ':' && chr <= '@') ||
           (chr >= '[' && chr <= '`') || (chr >= '{' && chr <= '~');
}

// State bitflags used with `Scanner.state`

// TODO
static UNUSED const uint8_t STATE_EMPHASIS_DELIMITER_MOD_3 = 0x3;
// Current delimiter run is opening
static const uint8_t STATE_EMPHASIS_DELIMITER_IS_OPEN = 0x1 << 2;

// Convenience function to emit the error token. This is done to stop invalid
// parse branches. Specifically:
// 1. When encountering a newline after a line break that ended a paragraph, and
// no new block
//    has been opened.
// 2. When encountering a new block after a soft line break.
// 3. When a `$._trigger_error` token is valid, which is used to stop parse
// branches through
//    normal tree-sitter grammar rules.
//
// See also the `$._soft_line_break` and `$._paragraph_end_newline` tokens in
// grammar.js
static bool error(TSLexer *lexer) {
    lexer->result_symbol = ERROR;
    return true;
}

typedef struct {
    // Parser state flags
    uint8_t state;
    uint8_t code_span_delimiter_length;
    uint8_t latex_span_delimiter_length;
    // The number of characters remaining in the currrent emphasis delimiter
    // run.
    uint8_t num_emphasis_delimiters_left;

} Scanner;

// Write the whole state of a Scanner to a byte buffer
static unsigned serialize(Scanner *s, char *buffer) {
    unsigned size = 0;
    buffer[size++] = (char)s->state;
    buffer[size++] = (char)s->code_span_delimiter_length;
    buffer[size++] = (char)s->latex_span_delimiter_length;
    buffer[size++] = (char)s->num_emphasis_delimiters_left;
    return size;
}

// Read the whole state of a Scanner from a byte buffer
// `serizalize` and `deserialize` should be fully symmetric.
static void deserialize(Scanner *s, const char *buffer, unsigned length) {
    s->state = 0;
    s->code_span_delimiter_length = 0;
    s->latex_span_delimiter_length = 0;
    s->num_emphasis_delimiters_left = 0;
    if (length > 0) {
        size_t size = 0;
        s->state = (uint8_t)buffer[size++];
        s->code_span_delimiter_length = (uint8_t)buffer[size++];
        s->latex_span_delimiter_length = (uint8_t)buffer[size++];
        s->num_emphasis_delimiters_left = (uint8_t)buffer[size++];
    }
}

static bool parse_leaf_delimiter(TSLexer *lexer, uint8_t *delimiter_length,
                                 const bool *valid_symbols,
                                 const char delimiter,
                                 const TokenType open_token,
                                 const TokenType close_token) {
    uint8_t level = 0;
    while (lexer->lookahead == delimiter) {
        lexer->advance(lexer, false);
        level++;
    }
    lexer->mark_end(lexer);
    if (level == *delimiter_length && valid_symbols[close_token]) {
        *delimiter_length = 0;
        lexer->result_symbol = close_token;
        return true;
    }
    if (valid_symbols[open_token]) {
        // Parse ahead to check if there is a closing delimiter
        size_t close_level = 0;
        while (!lexer->eof(lexer)) {
            if (lexer->lookahead == delimiter) {
                close_level++;
            } else {
                if (close_level == level) {
                    // Found a matching delimiter
                    break;
                }
                close_level = 0;
            }
            lexer->advance(lexer, false);
        }
        if (close_level == level) {
            *delimiter_length = level;
            lexer->result_symbol = open_token;
            return true;
        }
        if (valid_symbols[UNCLOSED_SPAN]) {
            lexer->result_symbol = UNCLOSED_SPAN;
            return true;
        }
    }
    return false;
}

static bool parse_backtick(Scanner *s, TSLexer *lexer,
                           const bool *valid_symbols) {
    return parse_leaf_delimiter(lexer, &s->code_span_delimiter_length,
                                valid_symbols, '`', CODE_SPAN_START,
                                CODE_SPAN_CLOSE);
}

static bool parse_dollar(Scanner *s, TSLexer *lexer,
                         const bool *valid_symbols) {
    return parse_leaf_delimiter(lexer, &s->latex_span_delimiter_length,
                                valid_symbols, '$', LATEX_SPAN_START,
                                LATEX_SPAN_CLOSE);
}

static bool parse_star(Scanner *s, TSLexer *lexer, const bool *valid_symbols) {
    lexer->advance(lexer, false);
    // If `num_emphasis_delimiters_left` is not zero then we already decided
    // that this should be part of an emphasis delimiter run, so interpret it as
    // such.
    if (s->num_emphasis_delimiters_left > 0) {
        // The `STATE_EMPHASIS_DELIMITER_IS_OPEN` state flag tells us wether it
        // should be open or close.
        if ((s->state & STATE_EMPHASIS_DELIMITER_IS_OPEN) &&
            valid_symbols[EMPHASIS_OPEN_STAR]) {
            s->state &= (~STATE_EMPHASIS_DELIMITER_IS_OPEN);
            lexer->result_symbol = EMPHASIS_OPEN_STAR;
            s->num_emphasis_delimiters_left--;
            return true;
        }
        if (valid_symbols[EMPHASIS_CLOSE_STAR]) {
            lexer->result_symbol = EMPHASIS_CLOSE_STAR;
            s->num_emphasis_delimiters_left--;
            return true;
        }
    }
    lexer->mark_end(lexer);
    // Otherwise count the number of stars
    uint8_t star_count = 1;
    while (lexer->lookahead == '*') {
        star_count++;
        lexer->advance(lexer, false);
    }
    bool line_end = lexer->lookahead == '\n' || lexer->lookahead == '\r' ||
                    lexer->eof(lexer);
    if (valid_symbols[EMPHASIS_OPEN_STAR] ||
        valid_symbols[EMPHASIS_CLOSE_STAR]) {
        // The desicion made for the first star also counts for all the
        // following stars in the delimiter run. Rembemer how many there are.
        s->num_emphasis_delimiters_left = star_count - 1;
        // Look ahead to the next symbol (after the last star) to find out if it
        // is whitespace punctuation or other.
        bool next_symbol_whitespace =
            line_end || lexer->lookahead == ' ' || lexer->lookahead == '\t';
        bool next_symbol_punctuation = is_punctuation(lexer->lookahead);
        // Information about the last token is in valid_symbols. See grammar.js
        // for these tokens for how this is done.
        if (valid_symbols[EMPHASIS_CLOSE_STAR] &&
            !valid_symbols[LAST_TOKEN_WHITESPACE] &&
            (!valid_symbols[LAST_TOKEN_PUNCTUATION] ||
             next_symbol_punctuation || next_symbol_whitespace)) {
            // Closing delimiters take precedence
            s->state &= ~STATE_EMPHASIS_DELIMITER_IS_OPEN;
            lexer->result_symbol = EMPHASIS_CLOSE_STAR;
            return true;
        }
        if (!next_symbol_whitespace && (!next_symbol_punctuation ||
                                        valid_symbols[LAST_TOKEN_PUNCTUATION] ||
                                        valid_symbols[LAST_TOKEN_WHITESPACE])) {
            s->state |= STATE_EMPHASIS_DELIMITER_IS_OPEN;
            lexer->result_symbol = EMPHASIS_OPEN_STAR;
            return true;
        }
    }
    return false;
}

static bool parse_tilde(Scanner *s, TSLexer *lexer, const bool *valid_symbols) {
    lexer->advance(lexer, false);
    // If `num_emphasis_delimiters_left` is not zero then we already decided
    // that this should be part of an emphasis delimiter run, so interpret it as
    // such.
    if (s->num_emphasis_delimiters_left > 0) {
        // The `STATE_EMPHASIS_DELIMITER_IS_OPEN` state flag tells us wether it
        // should be open or close.
        if ((s->state & STATE_EMPHASIS_DELIMITER_IS_OPEN) &&
            valid_symbols[STRIKETHROUGH_OPEN]) {
            s->state &= (~STATE_EMPHASIS_DELIMITER_IS_OPEN);
            lexer->result_symbol = STRIKETHROUGH_OPEN;
            s->num_emphasis_delimiters_left--;
            return true;
        }
        if (valid_symbols[STRIKETHROUGH_CLOSE]) {
            lexer->result_symbol = STRIKETHROUGH_CLOSE;
            s->num_emphasis_delimiters_left--;
            return true;
        }
    }
    lexer->mark_end(lexer);
    // Otherwise count the number of tildes
    uint8_t star_count = 1;
    while (lexer->lookahead == '~') {
        star_count++;
        lexer->advance(lexer, false);
    }
    bool line_end = lexer->lookahead == '\n' || lexer->lookahead == '\r' ||
                    lexer->eof(lexer);
    if (valid_symbols[STRIKETHROUGH_OPEN] ||
        valid_symbols[STRIKETHROUGH_CLOSE]) {
        // The desicion made for the first star also counts for all the
        // following stars in the delimiter run. Rembemer how many there are.
        s->num_emphasis_delimiters_left = star_count - 1;
        // Look ahead to the next symbol (after the last star) to find out if it
        // is whitespace punctuation or other.
        bool next_symbol_whitespace =
            line_end || lexer->lookahead == ' ' || lexer->lookahead == '\t';
        bool next_symbol_punctuation = is_punctuation(lexer->lookahead);
        // Information about the last token is in valid_symbols. See grammar.js
        // for these tokens for how this is done.
        if (valid_symbols[STRIKETHROUGH_CLOSE] &&
            !valid_symbols[LAST_TOKEN_WHITESPACE] &&
            (!valid_symbols[LAST_TOKEN_PUNCTUATION] ||
             next_symbol_punctuation || next_symbol_whitespace)) {
            // Closing delimiters take precedence
            s->state &= ~STATE_EMPHASIS_DELIMITER_IS_OPEN;
            lexer->result_symbol = STRIKETHROUGH_CLOSE;
            return true;
        }
        if (!next_symbol_whitespace && (!next_symbol_punctuation ||
                                        valid_symbols[LAST_TOKEN_PUNCTUATION] ||
                                        valid_symbols[LAST_TOKEN_WHITESPACE])) {
            s->state |= STATE_EMPHASIS_DELIMITER_IS_OPEN;
            lexer->result_symbol = STRIKETHROUGH_OPEN;
            return true;
        }
    }
    return false;
}

static bool parse_underscore(Scanner *s, TSLexer *lexer,
                             const bool *valid_symbols) {
    lexer->advance(lexer, false);
    // If `num_emphasis_delimiters_left` is not zero then we already decided
    // that this should be part of an emphasis delimiter run, so interpret it as
    // such.
    if (s->num_emphasis_delimiters_left > 0) {
        // The `STATE_EMPHASIS_DELIMITER_IS_OPEN` state flag tells us wether it
        // should be open or close.
        if ((s->state & STATE_EMPHASIS_DELIMITER_IS_OPEN) &&
            valid_symbols[EMPHASIS_OPEN_UNDERSCORE]) {
            s->state &= (~STATE_EMPHASIS_DELIMITER_IS_OPEN);
            lexer->result_symbol = EMPHASIS_OPEN_UNDERSCORE;
            s->num_emphasis_delimiters_left--;
            return true;
        }
        if (valid_symbols[EMPHASIS_CLOSE_UNDERSCORE]) {
            lexer->result_symbol = EMPHASIS_CLOSE_UNDERSCORE;
            s->num_emphasis_delimiters_left--;
            return true;
        }
    }
    lexer->mark_end(lexer);
    // Otherwise count the number of stars
    uint8_t underscore_count = 1;
    while (lexer->lookahead == '_') {
        underscore_count++;
        lexer->advance(lexer, false);
    }
    bool line_end = lexer->lookahead == '\n' || lexer->lookahead == '\r' ||
                    lexer->eof(lexer);
    if (valid_symbols[EMPHASIS_OPEN_UNDERSCORE] ||
        valid_symbols[EMPHASIS_CLOSE_UNDERSCORE]) {
        // The desicion made for the first underscore also counts for all the
        // following underscores in the delimiter run. Rembemer how many there are.
        s->num_emphasis_delimiters_left = underscore_count - 1;
        // Look ahead to the next symbol (after the last underscore) to find out if it
        // is whitespace punctuation or other.
        bool next_symbol_whitespace =
            line_end || lexer->lookahead == ' ' || lexer->lookahead == '\t';
        bool next_symbol_punctuation = is_punctuation(lexer->lookahead);
        // Information about the last token is in valid_symbols. See grammar.js
        // for these tokens for how this is done.
        if (valid_symbols[EMPHASIS_CLOSE_UNDERSCORE] &&
            !valid_symbols[LAST_TOKEN_WHITESPACE] &&
            (!valid_symbols[LAST_TOKEN_PUNCTUATION] ||
             next_symbol_punctuation || next_symbol_whitespace)) {
            // Closing delimiters take precedence
            s->state &= ~STATE_EMPHASIS_DELIMITER_IS_OPEN;
            lexer->result_symbol = EMPHASIS_CLOSE_UNDERSCORE;
            return true;
        }
        if (!next_symbol_whitespace && (!next_symbol_punctuation ||
                                        valid_symbols[LAST_TOKEN_PUNCTUATION] ||
                                        valid_symbols[LAST_TOKEN_WHITESPACE])) {
            s->state |= STATE_EMPHASIS_DELIMITER_IS_OPEN;
            lexer->result_symbol = EMPHASIS_OPEN_UNDERSCORE;
            return true;
        }
    }
    return false;
}

static bool scan(Scanner *s, TSLexer *lexer, const bool *valid_symbols) {
    // A normal tree-sitter rule decided that the current branch is invalid and
    // now "requests" an error to stop the branch
    if (valid_symbols[TRIGGER_ERROR]) {
        return error(lexer);
    }

    // Decide which tokens to consider based on the first non-whitespace
    // character
    switch (lexer->lookahead) {
        case '`':
            // A backtick could mark the beginning or ending of a code span or a
            // fenced code block.
            return parse_backtick(s, lexer, valid_symbols);
        case '$':
            return parse_dollar(s, lexer, valid_symbols);
        case '*':
            // A star could either mark the beginning or ending of emphasis, a
            // list item or thematic break. This code is similar to the code for
            // '_' and '+'.
            return parse_star(s, lexer, valid_symbols);
        case '_':
            return parse_underscore(s, lexer, valid_symbols);
        case '~':
            return parse_tilde(s, lexer, valid_symbols);
    }
    return false;
}

void *tree_sitter_markdown_inline_external_scanner_create() {
    Scanner *s = (Scanner *)malloc(sizeof(Scanner));
    deserialize(s, NULL, 0);
    return s;
}

bool tree_sitter_markdown_inline_external_scanner_scan(
    void *payload, TSLexer *lexer, const bool *valid_symbols) {
    Scanner *scanner = (Scanner *)payload;
    return scan(scanner, lexer, valid_symbols);
}

unsigned tree_sitter_markdown_inline_external_scanner_serialize(void *payload,
                                                                char *buffer) {
    Scanner *scanner = (Scanner *)payload;
    return serialize(scanner, buffer);
}

void tree_sitter_markdown_inline_external_scanner_deserialize(void *payload,
                                                              const char *buffer,
                                                              unsigned length) {
    Scanner *scanner = (Scanner *)payload;
    deserialize(scanner, buffer, length);
}

void tree_sitter_markdown_inline_external_scanner_destroy(void *payload) {
    Scanner *scanner = (Scanner *)payload;
    free(scanner);
}

```

--------------------------------------------------------------------------------
/deps/tree-sitter-markdown/tree-sitter-markdown/src/node-types.json:
--------------------------------------------------------------------------------

```json
[
  {
    "type": "atx_heading",
    "named": true,
    "fields": {
      "heading_content": {
        "multiple": false,
        "required": false,
        "types": [
          {
            "type": "inline",
            "named": true
          }
        ]
      }
    },
    "children": {
      "multiple": true,
      "required": true,
      "types": [
        {
          "type": "atx_h1_marker",
          "named": true
        },
        {
          "type": "atx_h2_marker",
          "named": true
        },
        {
          "type": "atx_h3_marker",
          "named": true
        },
        {
          "type": "atx_h4_marker",
          "named": true
        },
        {
          "type": "atx_h5_marker",
          "named": true
        },
        {
          "type": "atx_h6_marker",
          "named": true
        },
        {
          "type": "block_continuation",
          "named": true
        }
      ]
    }
  },
  {
    "type": "backslash_escape",
    "named": true,
    "fields": {}
  },
  {
    "type": "block_quote",
    "named": true,
    "fields": {},
    "children": {
      "multiple": true,
      "required": true,
      "types": [
        {
          "type": "block_continuation",
          "named": true
        },
        {
          "type": "block_quote",
          "named": true
        },
        {
          "type": "block_quote_marker",
          "named": true
        },
        {
          "type": "fenced_code_block",
          "named": true
        },
        {
          "type": "html_block",
          "named": true
        },
        {
          "type": "indented_code_block",
          "named": true
        },
        {
          "type": "link_reference_definition",
          "named": true
        },
        {
          "type": "list",
          "named": true
        },
        {
          "type": "paragraph",
          "named": true
        },
        {
          "type": "pipe_table",
          "named": true
        },
        {
          "type": "section",
          "named": true
        },
        {
          "type": "setext_heading",
          "named": true
        },
        {
          "type": "thematic_break",
          "named": true
        }
      ]
    }
  },
  {
    "type": "code_fence_content",
    "named": true,
    "fields": {},
    "children": {
      "multiple": true,
      "required": false,
      "types": [
        {
          "type": "block_continuation",
          "named": true
        }
      ]
    }
  },
  {
    "type": "document",
    "named": true,
    "root": true,
    "fields": {},
    "children": {
      "multiple": true,
      "required": false,
      "types": [
        {
          "type": "minus_metadata",
          "named": true
        },
        {
          "type": "plus_metadata",
          "named": true
        },
        {
          "type": "section",
          "named": true
        }
      ]
    }
  },
  {
    "type": "fenced_code_block",
    "named": true,
    "fields": {},
    "children": {
      "multiple": true,
      "required": true,
      "types": [
        {
          "type": "block_continuation",
          "named": true
        },
        {
          "type": "code_fence_content",
          "named": true
        },
        {
          "type": "fenced_code_block_delimiter",
          "named": true
        },
        {
          "type": "info_string",
          "named": true
        }
      ]
    }
  },
  {
    "type": "html_block",
    "named": true,
    "fields": {},
    "children": {
      "multiple": true,
      "required": false,
      "types": [
        {
          "type": "block_continuation",
          "named": true
        }
      ]
    }
  },
  {
    "type": "indented_code_block",
    "named": true,
    "fields": {},
    "children": {
      "multiple": true,
      "required": false,
      "types": [
        {
          "type": "block_continuation",
          "named": true
        }
      ]
    }
  },
  {
    "type": "info_string",
    "named": true,
    "fields": {},
    "children": {
      "multiple": true,
      "required": false,
      "types": [
        {
          "type": "backslash_escape",
          "named": true
        },
        {
          "type": "entity_reference",
          "named": true
        },
        {
          "type": "language",
          "named": true
        },
        {
          "type": "numeric_character_reference",
          "named": true
        }
      ]
    }
  },
  {
    "type": "inline",
    "named": true,
    "fields": {},
    "children": {
      "multiple": true,
      "required": false,
      "types": [
        {
          "type": "block_continuation",
          "named": true
        }
      ]
    }
  },
  {
    "type": "language",
    "named": true,
    "fields": {},
    "children": {
      "multiple": true,
      "required": false,
      "types": [
        {
          "type": "backslash_escape",
          "named": true
        },
        {
          "type": "entity_reference",
          "named": true
        },
        {
          "type": "numeric_character_reference",
          "named": true
        }
      ]
    }
  },
  {
    "type": "link_destination",
    "named": true,
    "fields": {},
    "children": {
      "multiple": true,
      "required": false,
      "types": [
        {
          "type": "backslash_escape",
          "named": true
        },
        {
          "type": "entity_reference",
          "named": true
        },
        {
          "type": "numeric_character_reference",
          "named": true
        }
      ]
    }
  },
  {
    "type": "link_label",
    "named": true,
    "fields": {},
    "children": {
      "multiple": true,
      "required": false,
      "types": [
        {
          "type": "backslash_escape",
          "named": true
        },
        {
          "type": "block_continuation",
          "named": true
        },
        {
          "type": "entity_reference",
          "named": true
        },
        {
          "type": "numeric_character_reference",
          "named": true
        }
      ]
    }
  },
  {
    "type": "link_reference_definition",
    "named": true,
    "fields": {},
    "children": {
      "multiple": true,
      "required": true,
      "types": [
        {
          "type": "block_continuation",
          "named": true
        },
        {
          "type": "link_destination",
          "named": true
        },
        {
          "type": "link_label",
          "named": true
        },
        {
          "type": "link_title",
          "named": true
        }
      ]
    }
  },
  {
    "type": "link_title",
    "named": true,
    "fields": {},
    "children": {
      "multiple": true,
      "required": false,
      "types": [
        {
          "type": "backslash_escape",
          "named": true
        },
        {
          "type": "block_continuation",
          "named": true
        },
        {
          "type": "entity_reference",
          "named": true
        },
        {
          "type": "numeric_character_reference",
          "named": true
        }
      ]
    }
  },
  {
    "type": "list",
    "named": true,
    "fields": {},
    "children": {
      "multiple": true,
      "required": false,
      "types": [
        {
          "type": "list_item",
          "named": true
        }
      ]
    }
  },
  {
    "type": "list_item",
    "named": true,
    "fields": {},
    "children": {
      "multiple": true,
      "required": true,
      "types": [
        {
          "type": "block_continuation",
          "named": true
        },
        {
          "type": "block_quote",
          "named": true
        },
        {
          "type": "fenced_code_block",
          "named": true
        },
        {
          "type": "html_block",
          "named": true
        },
        {
          "type": "indented_code_block",
          "named": true
        },
        {
          "type": "link_reference_definition",
          "named": true
        },
        {
          "type": "list",
          "named": true
        },
        {
          "type": "list_marker_dot",
          "named": true
        },
        {
          "type": "list_marker_minus",
          "named": true
        },
        {
          "type": "list_marker_parenthesis",
          "named": true
        },
        {
          "type": "list_marker_plus",
          "named": true
        },
        {
          "type": "list_marker_star",
          "named": true
        },
        {
          "type": "paragraph",
          "named": true
        },
        {
          "type": "pipe_table",
          "named": true
        },
        {
          "type": "section",
          "named": true
        },
        {
          "type": "setext_heading",
          "named": true
        },
        {
          "type": "task_list_marker_checked",
          "named": true
        },
        {
          "type": "task_list_marker_unchecked",
          "named": true
        },
        {
          "type": "thematic_break",
          "named": true
        }
      ]
    }
  },
  {
    "type": "list_marker_dot",
    "named": true,
    "fields": {}
  },
  {
    "type": "list_marker_minus",
    "named": true,
    "fields": {}
  },
  {
    "type": "list_marker_parenthesis",
    "named": true,
    "fields": {}
  },
  {
    "type": "list_marker_plus",
    "named": true,
    "fields": {}
  },
  {
    "type": "list_marker_star",
    "named": true,
    "fields": {}
  },
  {
    "type": "paragraph",
    "named": true,
    "fields": {},
    "children": {
      "multiple": true,
      "required": true,
      "types": [
        {
          "type": "block_continuation",
          "named": true
        },
        {
          "type": "inline",
          "named": true
        }
      ]
    }
  },
  {
    "type": "pipe_table",
    "named": true,
    "fields": {},
    "children": {
      "multiple": true,
      "required": true,
      "types": [
        {
          "type": "block_continuation",
          "named": true
        },
        {
          "type": "pipe_table_delimiter_row",
          "named": true
        },
        {
          "type": "pipe_table_header",
          "named": true
        },
        {
          "type": "pipe_table_row",
          "named": true
        }
      ]
    }
  },
  {
    "type": "pipe_table_cell",
    "named": true,
    "fields": {}
  },
  {
    "type": "pipe_table_delimiter_cell",
    "named": true,
    "fields": {},
    "children": {
      "multiple": true,
      "required": false,
      "types": [
        {
          "type": "pipe_table_align_left",
          "named": true
        },
        {
          "type": "pipe_table_align_right",
          "named": true
        }
      ]
    }
  },
  {
    "type": "pipe_table_delimiter_row",
    "named": true,
    "fields": {},
    "children": {
      "multiple": true,
      "required": true,
      "types": [
        {
          "type": "pipe_table_delimiter_cell",
          "named": true
        }
      ]
    }
  },
  {
    "type": "pipe_table_header",
    "named": true,
    "fields": {},
    "children": {
      "multiple": true,
      "required": true,
      "types": [
        {
          "type": "pipe_table_cell",
          "named": true
        }
      ]
    }
  },
  {
    "type": "pipe_table_row",
    "named": true,
    "fields": {},
    "children": {
      "multiple": true,
      "required": true,
      "types": [
        {
          "type": "pipe_table_cell",
          "named": true
        }
      ]
    }
  },
  {
    "type": "section",
    "named": true,
    "fields": {},
    "children": {
      "multiple": true,
      "required": false,
      "types": [
        {
          "type": "atx_heading",
          "named": true
        },
        {
          "type": "block_continuation",
          "named": true
        },
        {
          "type": "block_quote",
          "named": true
        },
        {
          "type": "fenced_code_block",
          "named": true
        },
        {
          "type": "html_block",
          "named": true
        },
        {
          "type": "indented_code_block",
          "named": true
        },
        {
          "type": "link_reference_definition",
          "named": true
        },
        {
          "type": "list",
          "named": true
        },
        {
          "type": "paragraph",
          "named": true
        },
        {
          "type": "pipe_table",
          "named": true
        },
        {
          "type": "section",
          "named": true
        },
        {
          "type": "setext_heading",
          "named": true
        },
        {
          "type": "thematic_break",
          "named": true
        }
      ]
    }
  },
  {
    "type": "setext_heading",
    "named": true,
    "fields": {
      "heading_content": {
        "multiple": false,
        "required": true,
        "types": [
          {
            "type": "paragraph",
            "named": true
          }
        ]
      }
    },
    "children": {
      "multiple": true,
      "required": true,
      "types": [
        {
          "type": "block_continuation",
          "named": true
        },
        {
          "type": "setext_h1_underline",
          "named": true
        },
        {
          "type": "setext_h2_underline",
          "named": true
        }
      ]
    }
  },
  {
    "type": "task_list_marker_checked",
    "named": true,
    "fields": {}
  },
  {
    "type": "task_list_marker_unchecked",
    "named": true,
    "fields": {}
  },
  {
    "type": "thematic_break",
    "named": true,
    "fields": {},
    "children": {
      "multiple": false,
      "required": false,
      "types": [
        {
          "type": "block_continuation",
          "named": true
        }
      ]
    }
  },
  {
    "type": "!",
    "named": false
  },
  {
    "type": "\"",
    "named": false
  },
  {
    "type": "#",
    "named": false
  },
  {
    "type": "$",
    "named": false
  },
  {
    "type": "%",
    "named": false
  },
  {
    "type": "&",
    "named": false
  },
  {
    "type": "'",
    "named": false
  },
  {
    "type": "(",
    "named": false
  },
  {
    "type": ")",
    "named": false
  },
  {
    "type": "*",
    "named": false
  },
  {
    "type": "+",
    "named": false
  },
  {
    "type": ",",
    "named": false
  },
  {
    "type": "-",
    "named": false
  },
  {
    "type": "-->",
    "named": false
  },
  {
    "type": ".",
    "named": false
  },
  {
    "type": "/",
    "named": false
  },
  {
    "type": ":",
    "named": false
  },
  {
    "type": ";",
    "named": false
  },
  {
    "type": "<",
    "named": false
  },
  {
    "type": "=",
    "named": false
  },
  {
    "type": ">",
    "named": false
  },
  {
    "type": "?",
    "named": false
  },
  {
    "type": "?>",
    "named": false
  },
  {
    "type": "@",
    "named": false
  },
  {
    "type": "[",
    "named": false
  },
  {
    "type": "\\",
    "named": false
  },
  {
    "type": "]",
    "named": false
  },
  {
    "type": "]]>",
    "named": false
  },
  {
    "type": "^",
    "named": false
  },
  {
    "type": "_",
    "named": false
  },
  {
    "type": "`",
    "named": false
  },
  {
    "type": "atx_h1_marker",
    "named": true
  },
  {
    "type": "atx_h2_marker",
    "named": true
  },
  {
    "type": "atx_h3_marker",
    "named": true
  },
  {
    "type": "atx_h4_marker",
    "named": true
  },
  {
    "type": "atx_h5_marker",
    "named": true
  },
  {
    "type": "atx_h6_marker",
    "named": true
  },
  {
    "type": "block_continuation",
    "named": true
  },
  {
    "type": "block_quote_marker",
    "named": true
  },
  {
    "type": "entity_reference",
    "named": true
  },
  {
    "type": "fenced_code_block_delimiter",
    "named": true
  },
  {
    "type": "minus_metadata",
    "named": true
  },
  {
    "type": "numeric_character_reference",
    "named": true
  },
  {
    "type": "pipe_table_align_left",
    "named": true
  },
  {
    "type": "pipe_table_align_right",
    "named": true
  },
  {
    "type": "plus_metadata",
    "named": true
  },
  {
    "type": "setext_h1_underline",
    "named": true
  },
  {
    "type": "setext_h2_underline",
    "named": true
  },
  {
    "type": "{",
    "named": false
  },
  {
    "type": "|",
    "named": false
  },
  {
    "type": "}",
    "named": false
  },
  {
    "type": "~",
    "named": false
  }
]
```

--------------------------------------------------------------------------------
/src/core/config_manager.cpp:
--------------------------------------------------------------------------------

```cpp
#include "config_manager.h"
#include <algorithm>
#include <cstdlib>
#include <filesystem>
#include <fstream>
#include <functional>
#include <iostream>
#include <memory>
#include <sstream>

#include <yaml-cpp/yaml.h>

// EFSW includes
#include <efsw/efsw.h>
#include <efsw/efsw.hpp>

namespace fs = std::filesystem;

// Static initialization
std::string ConfigManager::config_dir_cache_;
std::string ConfigManager::active_theme_ = "default";
std::vector<ConfigReloadCallback> ConfigManager::reload_callbacks_;
std::unique_ptr<efsw::FileWatchListener> ConfigManager::watcher_listener_ =
    nullptr;
std::unique_ptr<efsw::FileWatcher> ConfigManager::watcher_instance_ = nullptr;
std::atomic<bool> ConfigManager::reload_pending_ = false;
EditorConfig ConfigManager::editor_config_;
SyntaxConfig ConfigManager::syntax_config_;

// --- EFSW Listener Class ---

// We define a custom listener that efsw will use to communicate events.
class ConfigFileListener : public efsw::FileWatchListener
{
public:
  void handleFileAction(efsw::WatchID watchid, const std::string &dir,
                        const std::string &filename, efsw::Action action,
                        std::string oldFilename) override
  {
    // We are interested in Modification and Renaming (e.g., atomic save by
    // editor)
    if (filename == "config.yaml" &&
        (action == efsw::Action::Modified || action == efsw::Action::Moved))
    {
      // Call the static handler in ConfigManager
      ConfigManager::handleFileChange();
    }
  }
};

// -----------------------------------------------------------------
// CONFIG DIRECTORY AND PATH GETTERS
// -----------------------------------------------------------------

std::string ConfigManager::getConfigDir()
{
  // Return cached value if already determined
  if (!config_dir_cache_.empty())
  {
    return config_dir_cache_;
  }

  std::vector<std::string> search_paths;

#ifdef _WIN32
  // Windows: %APPDATA%\arceditor
  const char *appdata = std::getenv("APPDATA");
  if (appdata)
  {
    search_paths.push_back(std::string(appdata) + "\\arceditor");
  }
  // Fallback: %USERPROFILE%\.config\arceditor
  const char *userprofile = std::getenv("USERPROFILE");
  if (userprofile)
  {
    search_paths.push_back(std::string(userprofile) + "\\.config\\arceditor");
  }
#else
  // Linux/macOS: XDG_CONFIG_HOME or ~/.config
  const char *xdg_config = std::getenv("XDG_CONFIG_HOME");
  if (xdg_config)
  {
    search_paths.push_back(std::string(xdg_config) + "/arceditor");
  }

  const char *home = std::getenv("HOME");
  if (home)
  {
    search_paths.push_back(std::string(home) + "/.config/arceditor");
  }
#endif

  // Development fallback to use ./config/arceditor
  std::string cwd = fs::current_path().string();
  std::string dev_config_path = cwd + "/.config/arceditor";

  if (fs::exists(dev_config_path) && fs::is_directory(dev_config_path))
  {
    // Insert the local development path as the highest priority
    search_paths.insert(search_paths.begin(), dev_config_path);
  }

  // Find first existing config directory
  for (const auto &path : search_paths)
  {
    if (fs::exists(path) && fs::is_directory(path))
    {
      config_dir_cache_ = path;
      // std::cerr << "Using config directory: " << config_dir_cache_ <<
      // std::endl;
      return config_dir_cache_;
    }
  }

  // If no config dir exists, create one in the standard location (first in
  // search_paths)
  if (!search_paths.empty())
  {
    std::string target_dir = search_paths[0];
    try
    {
      fs::create_directories(target_dir);
      config_dir_cache_ = target_dir;
      std::cerr << "Created config directory: " << config_dir_cache_
                << std::endl;
      return config_dir_cache_;
    }
    catch (const fs::filesystem_error &e)
    {
      std::cerr << "Failed to create config directory: " << e.what()
                << std::endl;
    }
  }

  // Last resort: use current directory
  config_dir_cache_ = cwd;
  std::cerr << "Warning: Using current directory as config dir" << std::endl;
  return config_dir_cache_;
}

std::string ConfigManager::getThemesDir() { return getConfigDir() + "/themes"; }

std::string ConfigManager::getSyntaxRulesDir()
{
  return getConfigDir() + "/syntax_rules";
}

std::string ConfigManager::getConfigFile()
{
  return getConfigDir() + "/config.yaml";
}

bool ConfigManager::ensureConfigStructure()
{
  try
  {
    std::string config_dir = getConfigDir();

    // Create main config directory (already handled in getConfigDir, but safety
    // check)
    if (!fs::exists(config_dir))
    {
      fs::create_directories(config_dir);
    }

    // Create subdirectories
    std::string themes_dir = config_dir + "/themes";
    // std::string syntax_dir = config_dir + "/syntax_rules";

    if (!fs::exists(themes_dir))
    {
      fs::create_directories(themes_dir);
      std::cerr << "Created themes directory: " << themes_dir << std::endl;
    }

    // if (!fs::exists(syntax_dir))
    // {
    //   fs::create_directories(syntax_dir);
    //   std::cerr << "Created syntax_rules directory: " << syntax_dir
    //             << std::endl;
    // }

    // Create default config file if it doesn't exist
    std::string config_file = getConfigFile();
    if (!fs::exists(config_file))
    {
      createDefaultConfig(config_file);
    }

    return true;
  }
  catch (const fs::filesystem_error &e)
  {
    std::cerr << "Error ensuring config structure: " << e.what() << std::endl;
    return false;
  }
}

// -----------------------------------------------------------------
// CONFIGURATION (YAML) MANAGEMENT
// -----------------------------------------------------------------

bool ConfigManager::createDefaultConfig(const std::string &config_file)
{
  try
  {
    YAML::Node config;
    config["appearance"]["theme"] = "default";
    config["editor"]["tab_size"] = 4;
    config["editor"]["line_numbers"] = true;
    config["editor"]["cursor_style"] = "auto";
    config["syntax"]["highlighting"] = "viewport"; // Changed to string

    std::ofstream file(config_file);
    if (!file.is_open())
    {
      std::cerr << "Failed to create default config file" << std::endl;
      return false;
    }
    file << "# arceditor Configuration File\n";
    file << "# This file is automatically generated\n\n";
    file << config;
    file.close();

    std::cerr << "Created default config: " << config_file << std::endl;
    return true;
  }
  catch (const std::exception &e)
  {
    std::cerr << "Failed to create config: " << e.what() << std::endl;
    return false;
  }
}

bool ConfigManager::loadConfig()
{
  std::string config_file = getConfigFile();

  if (!fs::exists(config_file))
  {
    std::cerr << "Config file not found, using defaults" << std::endl;
    return false;
  }

  try
  {
    YAML::Node config = YAML::LoadFile(config_file);

    // Load appearance section
    if (config["appearance"] && config["appearance"]["theme"])
    {
      active_theme_ = config["appearance"]["theme"].as<std::string>();
    }

    // Load editor section
    if (config["editor"])
    {
      if (config["editor"]["tab_size"])
      {
        editor_config_.tab_size = config["editor"]["tab_size"].as<int>();
      }
      if (config["editor"]["line_numbers"])
      {
        editor_config_.line_numbers =
            config["editor"]["line_numbers"].as<bool>();
      }
      if (config["editor"]["cursor_style"])
      {
        editor_config_.cursor_style =
            config["editor"]["cursor_style"].as<std::string>();
      }
    }

    // Load syntax section
    if (config["syntax"] && config["syntax"]["highlighting"])
    {
      std::string mode_str = config["syntax"]["highlighting"].as<std::string>();
      syntax_config_.highlighting = parseSyntaxMode(mode_str);
    }

    return true;
  }
  catch (const YAML::Exception &e)
  {
    std::cerr << "Failed to load config: " << e.what() << std::endl;
    return false;
  }
}

bool ConfigManager::saveConfig()
{
  std::string config_file = getConfigFile();
  YAML::Node config;

  try
  {
    // Try to load existing config to preserve comments/structure
    config = YAML::LoadFile(config_file);
  }
  catch (const YAML::BadFile &)
  {
    // File doesn't exist, create new structure
  }

  // Update all sections
  config["appearance"]["theme"] = active_theme_;
  config["editor"]["tab_size"] = editor_config_.tab_size;
  config["editor"]["line_numbers"] = editor_config_.line_numbers;
  config["editor"]["cursor_style"] = editor_config_.cursor_style;
  config["syntax"]["highlighting"] =
      syntaxModeToString(syntax_config_.highlighting);

  try
  {
    std::ofstream file(config_file);
    if (!file.is_open())
    {
      std::cerr << "Failed to open config file for saving" << std::endl;
      return false;
    }
    file << "# arceditor Configuration File\n\n";
    file << config;
    file.close();
    return true;
  }
  catch (const std::exception &e)
  {
    std::cerr << "Failed to save config: " << e.what() << std::endl;
    return false;
  }
}

// -----------------------------------------------------------------
// LIVE RELOAD IMPLEMENTATION (EFSW)
// -----------------------------------------------------------------

void ConfigManager::registerReloadCallback(ConfigReloadCallback callback)
{
  reload_callbacks_.push_back(callback);
}

void ConfigManager::handleFileChange()
{
  std::cerr << "Config file modified. Attempting hot reload..." << std::endl;

  // 1. Re-read the configuration file
  if (!loadConfig())
  {
    std::cerr << "Failed to hot reload configuration. File may be invalid."
              << std::endl;
    return;
  }

  // 2. Notify all subscribed components
  for (const auto &callback : reload_callbacks_)
  {
    // NOTE: In a multi-threaded app, this should be marshaled to the main
    // thread.
    callback();
  }
  reload_pending_.store(true);
  // std::cerr << "Configuration hot reload complete." << std::endl;
}

bool ConfigManager::isReloadPending()
{
  // Atomically check the flag and reset it to false in one operation
  return reload_pending_.exchange(false);
}

bool ConfigManager::startWatchingConfig()
{
  // 1. Check if watching is already active
  if (watcher_instance_ && watcher_listener_)
  {
    return true; // Already watching
  }

  // 2. Instantiate the FileWatcher and Listener
  try
  {
    // The FileWatcher instance is heavy and should be long-lived
    watcher_instance_ = std::make_unique<efsw::FileWatcher>();

    // The listener is the custom class we defined earlier
    watcher_listener_ = std::make_unique<ConfigFileListener>();

    // 3. Get the directory to watch (the config directory)
    std::string configDir = getConfigDir();

    // 4. Add the watch. The 'true' is for recursive watching,
    // but the listener only cares about 'config.yaml' anyway.
    efsw::WatchID watchID = watcher_instance_->addWatch(
        configDir, watcher_listener_.get(), false // false for non-recursive
    );

    if (watchID < 0)
    {
      std::cerr << "Error: EFSW failed to add watch for config directory: "
                << configDir << std::endl;
      // Cleanup pointers if the watch failed
      watcher_instance_.reset();
      watcher_listener_.reset();
      return false;
    }

    // 5. Start the watcher thread
    watcher_instance_->watch(); // This starts the background thread

    // std::cerr << "Config watching started for: " << configDir << std::endl;
    return true;
  }
  catch (const std::exception &e)
  {
    std::cerr << "Fatal Error starting EFSW watcher: " << e.what() << std::endl;
    watcher_instance_.reset();
    watcher_listener_.reset();
    return false;
  }
}

// -----------------------------------------------------------------
// THEME AND SYNTAX MANAGEMENT
// -----------------------------------------------------------------

std::string ConfigManager::getThemeFile(const std::string &theme_name)
{
  std::string themes_dir = getThemesDir();
  std::string theme_file = themes_dir + "/" + theme_name + ".theme";

  if (fs::exists(theme_file))
  {
    return theme_file;
  }

  // Fallback: Check project's "themes" directory for default files
  std::string dev_theme = "themes/" + theme_name + ".theme";
  if (fs::exists(dev_theme))
  {
    return dev_theme;
  }

  std::cerr << "Theme file not found: " << theme_name << std::endl;
  return "";
}

std::string ConfigManager::getSyntaxFile(const std::string &language)
{
  std::string syntax_dir = getSyntaxRulesDir();
  std::string syntax_file = syntax_dir + "/" + language + ".yaml";

  if (fs::exists(syntax_file))
  {
    return syntax_file;
  }

  // Fallback: Check project's "syntax_rules" directory for default files
  std::string dev_syntax = "treesitter/" + language + ".yaml";
  if (fs::exists(dev_syntax))
  {
    return dev_syntax;
  }

  return ""; // Not found
}

std::string ConfigManager::getActiveTheme() { return active_theme_; }

bool ConfigManager::setActiveTheme(const std::string &theme_name)
{
  // Verify theme exists
  std::string theme_file = getThemeFile(theme_name);
  if (theme_file.empty())
  {
    std::cerr << "Cannot set theme, file not found: " << theme_name
              << std::endl;
    return false;
  }

  active_theme_ = theme_name;
  saveConfig(); // Persist the change
  return true;
}

bool ConfigManager::copyProjectFilesToConfig()
{
  try
  {
    std::string config_dir = getConfigDir();

    // Copy themes
    if (fs::exists("themes") && fs::is_directory("themes"))
    {
      std::string target_themes = config_dir + "/themes";
      fs::create_directories(target_themes);

      for (const auto &entry : fs::directory_iterator("themes"))
      {
        if (entry.is_regular_file() && entry.path().extension() == ".theme")
        {
          std::string filename = entry.path().filename().string();
          std::string target = target_themes + "/" + filename;

          // Only copy if doesn't exist (don't overwrite user themes)
          if (!fs::exists(target))
          {
            fs::copy_file(entry.path(), target);
            std::cerr << "Copied theme: " << filename << std::endl;
          }
        }
      }
    }

    // Copy syntax rules
    if (fs::exists("syntax_rules") && fs::is_directory("syntax_rules"))
    {
      std::string target_syntax = config_dir + "/syntax_rules";
      fs::create_directories(target_syntax);

      for (const auto &entry : fs::directory_iterator("syntax_rules"))
      {
        if (entry.is_regular_file() && entry.path().extension() == ".yaml")
        {
          std::string filename = entry.path().filename().string();
          std::string target = target_syntax + "/" + filename;

          // Only copy if doesn't exist
          if (!fs::exists(target))
          {
            fs::copy_file(entry.path(), target);
            std::cerr << "Copied syntax rule: " << filename << std::endl;
          }
        }
      }
    }

    return true;
  }
  catch (const fs::filesystem_error &e)
  {
    std::cerr << "Error copying project files: " << e.what() << std::endl;
    return false;
  }
}

SyntaxMode ConfigManager::parseSyntaxMode(const std::string &mode_str)
{
  std::string lower = mode_str;
  std::transform(lower.begin(), lower.end(), lower.begin(), ::tolower);

  if (lower == "none" || lower == "false" || lower == "off")
    return SyntaxMode::NONE;
  else if (lower == "viewport" || lower == "lazy" || lower == "dynamic")
    return SyntaxMode::VIEWPORT;
  else if (lower == "full" || lower == "immediate" || lower == "true")
    return SyntaxMode::FULL;

  std::cerr << "Unknown syntax mode '" << mode_str << "', using viewport"
            << std::endl;
  return SyntaxMode::VIEWPORT;
}

std::string ConfigManager::syntaxModeToString(SyntaxMode mode)
{
  switch (mode)
  {
  case SyntaxMode::NONE:
    return "none";
  case SyntaxMode::VIEWPORT:
    return "viewport";
  case SyntaxMode::FULL:
    return "full";
  default:
    return "viewport";
  }
}

// NEW: Setters
void ConfigManager::setTabSize(int size)
{
  if (size < 1)
    size = 1;
  if (size > 16)
    size = 16;
  editor_config_.tab_size = size;
  saveConfig();
}

void ConfigManager::setLineNumbers(bool enabled)
{
  editor_config_.line_numbers = enabled;
  saveConfig();
}

void ConfigManager::setCursorStyle(const std::string &style)
{
  editor_config_.cursor_style = style;
  saveConfig();
}

void ConfigManager::setSyntaxMode(SyntaxMode mode)
{
  syntax_config_.highlighting = mode;
  saveConfig();
}
```

--------------------------------------------------------------------------------
/CMakeLists.txt:
--------------------------------------------------------------------------------

```
cmake_minimum_required(VERSION 3.16)
project(arc VERSION 0.0.1 LANGUAGES CXX C)

set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# Define a central location for external dependencies
set(DEPS_DIR ${CMAKE_SOURCE_DIR}/deps)

# Initialize Tree-sitter as enabled by default
set(TREE_SITTER_ENABLED TRUE)

# Debug Delta
# add_compile_definitions(DEBUG_DELTA_UNDO)

# ----------------------------------------------------
# 1. Tree-sitter Core Library
# ----------------------------------------------------

# Manually list core source files
set(TS_CORE_SOURCES
    ${DEPS_DIR}/tree-sitter-core/lib/src/language.c
    ${DEPS_DIR}/tree-sitter-core/lib/src/lexer.c
    ${DEPS_DIR}/tree-sitter-core/lib/src/node.c
    ${DEPS_DIR}/tree-sitter-core/lib/src/parser.c
    ${DEPS_DIR}/tree-sitter-core/lib/src/query.c
    ${DEPS_DIR}/tree-sitter-core/lib/src/tree.c
    ${DEPS_DIR}/tree-sitter-core/lib/src/tree_cursor.c
    ${DEPS_DIR}/tree-sitter-core/lib/src/alloc.c
    ${DEPS_DIR}/tree-sitter-core/lib/src/get_changed_ranges.c
    ${DEPS_DIR}/tree-sitter-core/lib/src/stack.c
    ${DEPS_DIR}/tree-sitter-core/lib/src/subtree.c
    ${DEPS_DIR}/tree-sitter-core/lib/src/point.c
    ${DEPS_DIR}/tree-sitter-core/lib/src/wasm_store.c
)

# Check if Tree-sitter headers exist
if(NOT EXISTS ${DEPS_DIR}/tree-sitter-core/lib/include/tree_sitter/api.h)
    message(WARNING "Tree-sitter header files not found at ${DEPS_DIR}/tree-sitter-core/lib/include/tree_sitter/api.h")
    set(TREE_SITTER_ENABLED FALSE)
endif()

if(TREE_SITTER_ENABLED)
    # Verify core files exist
    set(MISSING_FILES "")
    foreach(file ${TS_CORE_SOURCES})
        if(NOT EXISTS ${file})
            list(APPEND MISSING_FILES ${file})
        endif()
    endforeach()

    if(MISSING_FILES)
        message(WARNING "Missing Tree-sitter core files: ${MISSING_FILES}")
        set(TREE_SITTER_ENABLED FALSE)
    else()
        message(STATUS "Tree-sitter core sources detected: ${TS_CORE_SOURCES}")
        message(STATUS "Building Tree-sitter core library.")

        add_library(tree-sitter-core STATIC ${TS_CORE_SOURCES})

        # Explicitly set these as C sources
        set_source_files_properties(${TS_CORE_SOURCES} PROPERTIES LANGUAGE C)

        # The public API headers are in lib/include/
        target_include_directories(tree-sitter-core PUBLIC
            ${DEPS_DIR}/tree-sitter-core/lib/include
        )
        # Explicitly set C standard for C files
        target_compile_features(tree-sitter-core PUBLIC c_std_99)

        # Force static runtime for Tree-sitter to match main executable
        if(MSVC)
            target_compile_options(tree-sitter-core PRIVATE
                $<$<CONFIG:Debug>:/MTd>
                $<$<CONFIG:Release>:/MT>
            )
        endif()

        set(TS_LIBRARIES tree-sitter-core)
        set(TS_INCLUDES ${DEPS_DIR}/tree-sitter-core/lib/include)

        message(STATUS "Tree-sitter enabled with include path: ${TS_INCLUDES}")
    endif()
else()
    message(WARNING "Tree-sitter core sources or headers not found. Disabling Tree-sitter features.")
    set(TREE_SITTER_ENABLED FALSE)
    set(TS_LIBRARIES "")
    set(TS_INCLUDES "")
endif()

# ----------------------------------------------------
# 2. Language Parsers (Auto-Discovery) - FIXED FOR WINDOWS
# ----------------------------------------------------

if(TREE_SITTER_ENABLED)
    message(STATUS "=== Tree-sitter Auto-Discovery ===")

    # Define parsers - use CMake lists instead of colon-separated strings
    # Format: list of pairs (lang_name, parser_path)
    set(PARSER_NAMES
        "python" "c" "cpp" "rust" "markdown" "javascript" "typescript" "tsx" "zig" "go"
    )
    set(PARSER_PATHS
        "${DEPS_DIR}/tree-sitter-python"
        "${DEPS_DIR}/tree-sitter-c"
        "${DEPS_DIR}/tree-sitter-cpp"
        "${DEPS_DIR}/tree-sitter-rust"
        "${DEPS_DIR}/tree-sitter-markdown/tree-sitter-markdown"
        "${DEPS_DIR}/tree-sitter-javascript"
        "${DEPS_DIR}/tree-sitter-typescript/typescript"
        "${DEPS_DIR}/tree-sitter-typescript/tsx"
        "${DEPS_DIR}/tree-sitter-zig"
        "${DEPS_DIR}/tree-sitter-go"
    )

    set(DISCOVERED_PARSERS "")

    # Iterate using indices
    list(LENGTH PARSER_NAMES parser_count)
    math(EXPR parser_count "${parser_count} - 1")

    foreach(i RANGE ${parser_count})
        list(GET PARSER_NAMES ${i} lang_name)
        list(GET PARSER_PATHS ${i} parser_dir)

        if(NOT EXISTS ${parser_dir})
            message(STATUS "  ✗ Skipping ${lang_name}: directory not found at ${parser_dir}")
            continue()
        endif()

        # Collect source files
        set(PARSER_SOURCES "")

        # Check for parser.c (required)
        if(EXISTS ${parser_dir}/src/parser.c)
            list(APPEND PARSER_SOURCES ${parser_dir}/src/parser.c)
        else()
            message(STATUS "  ✗ Skipping ${lang_name}: no parser.c at ${parser_dir}/src/")
            continue()
        endif()

        # Check for scanner files (optional)
        if(EXISTS ${parser_dir}/src/scanner.c)
            list(APPEND PARSER_SOURCES ${parser_dir}/src/scanner.c)
        endif()

        if(EXISTS ${parser_dir}/src/scanner.cc)
            list(APPEND PARSER_SOURCES ${parser_dir}/src/scanner.cc)
        endif()

        # Create library
        add_library(tree-sitter-${lang_name} STATIC ${PARSER_SOURCES})

        # Set as C sources
        set_source_files_properties(${PARSER_SOURCES} PROPERTIES LANGUAGE C)

        # Set C99 standard
        target_compile_features(tree-sitter-${lang_name} PUBLIC c_std_99)

        # Force static runtime for parsers to match main executable
        if(MSVC)
            target_compile_options(tree-sitter-${lang_name} PRIVATE
                $<$<CONFIG:Debug>:/MTd>
                $<$<CONFIG:Release>:/MT>
            )
        endif()

        # Include directories for scanner files
        target_include_directories(tree-sitter-${lang_name} PRIVATE
            ${parser_dir}/src
        )

        # Add to libraries list
        list(APPEND TS_LIBRARIES tree-sitter-${lang_name})
        list(APPEND DISCOVERED_PARSERS ${lang_name})

        message(STATUS "  ✓ Built parser: ${lang_name}")
    endforeach()

    # ----------------------------------------------------
    # 3. Generate Language Registry Header (Auto-registration)
    # ----------------------------------------------------

    if(DISCOVERED_PARSERS)
        set(LANG_REGISTRY_FILE "${CMAKE_BINARY_DIR}/generated/language_registry.h")
        file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/generated")

        # Build the header file content
        set(REGISTRY_CONTENT "// Auto-generated by CMake - DO NOT EDIT MANUALLY\n")
        string(APPEND REGISTRY_CONTENT "// Generated from: ${CMAKE_CURRENT_LIST_FILE}\n\n")
        string(APPEND REGISTRY_CONTENT "#pragma once\n\n")
        string(APPEND REGISTRY_CONTENT "#ifdef TREE_SITTER_ENABLED\n\n")
        string(APPEND REGISTRY_CONTENT "#include <tree_sitter/api.h>\n")
        string(APPEND REGISTRY_CONTENT "#include <unordered_map>\n")
        string(APPEND REGISTRY_CONTENT "#include <string>\n\n")

        # Extern declarations for all discovered languages
        string(APPEND REGISTRY_CONTENT "// External language function declarations\n")
        string(APPEND REGISTRY_CONTENT "extern \"C\" {\n")
        foreach(lang ${DISCOVERED_PARSERS})
            string(APPEND REGISTRY_CONTENT "  const TSLanguage *tree_sitter_${lang}();\n")
        endforeach()
        string(APPEND REGISTRY_CONTENT "}\n\n")

        # Registration function
        string(APPEND REGISTRY_CONTENT "// Auto-register all available languages\n")
        string(APPEND REGISTRY_CONTENT "inline void registerAllLanguages(std::unordered_map<std::string, const TSLanguage* (*)()>& registry) {\n")
        foreach(lang ${DISCOVERED_PARSERS})
            string(APPEND REGISTRY_CONTENT "  registry[\"${lang}\"] = tree_sitter_${lang};\n")
        endforeach()
        string(APPEND REGISTRY_CONTENT "}\n\n")

        # List of available languages as a comment
        string(APPEND REGISTRY_CONTENT "// Available languages: ")
        string(JOIN DISCOVERED_PARSERS ", " LANG_LIST)
        string(APPEND REGISTRY_CONTENT "${LANG_LIST}\n\n")

        string(APPEND REGISTRY_CONTENT "#endif // TREE_SITTER_ENABLED\n")

        # Write the file
        file(WRITE ${LANG_REGISTRY_FILE} "${REGISTRY_CONTENT}")

        message(STATUS "Generated language registry: ${LANG_REGISTRY_FILE}")
        message(STATUS "  Registered parsers: ${DISCOVERED_PARSERS}")
    else()
        message(WARNING "No parsers discovered, skipping registry generation")
        set(TREE_SITTER_ENABLED FALSE)
    endif()

    message(STATUS "=== End Tree-sitter Auto-Discovery ===")

endif()

if(NOT TREE_SITTER_ENABLED)
    message(STATUS "Tree-sitter disabled - using fallback syntax highlighting")
    set(TS_LIBRARIES "")
    set(TS_INCLUDES "")
endif()

# ----------------------------------------------------
# 4. EFSW (Event File System Watcher) - For Live Reloading
# ----------------------------------------------------
set(EFSW_BASE_DIR ${DEPS_DIR}/efsw)
set(EFSW_SOURCES "")

# List ALL required core files from the flattened structure (src/efsw/)
list(APPEND EFSW_SOURCES
    # Core Files (Unconditional)
    ${EFSW_BASE_DIR}/src/efsw/Debug.cpp
    ${EFSW_BASE_DIR}/src/efsw/DirWatcherGeneric.cpp
    ${EFSW_BASE_DIR}/src/efsw/DirectorySnapshot.cpp
    ${EFSW_BASE_DIR}/src/efsw/DirectorySnapshotDiff.cpp
    ${EFSW_BASE_DIR}/src/efsw/FileInfo.cpp
    ${EFSW_BASE_DIR}/src/efsw/FileSystem.cpp
    ${EFSW_BASE_DIR}/src/efsw/FileWatcher.cpp
    ${EFSW_BASE_DIR}/src/efsw/FileWatcherCWrapper.cpp
    ${EFSW_BASE_DIR}/src/efsw/FileWatcherImpl.cpp
    ${EFSW_BASE_DIR}/src/efsw/Log.cpp
    ${EFSW_BASE_DIR}/src/efsw/String.cpp
    ${EFSW_BASE_DIR}/src/efsw/System.cpp
    ${EFSW_BASE_DIR}/src/efsw/Watcher.cpp

    # CRITICAL FIX: Add Generic implementation for default constructors
    ${EFSW_BASE_DIR}/src/efsw/FileWatcherGeneric.cpp
    ${EFSW_BASE_DIR}/src/efsw/WatcherGeneric.cpp
)

# Conditionally add the correct platform backend
if(CMAKE_SYSTEM_NAME MATCHES "Linux")
    list(APPEND EFSW_SOURCES
        ${EFSW_BASE_DIR}/src/efsw/FileWatcherInotify.cpp
        ${EFSW_BASE_DIR}/src/efsw/WatcherInotify.cpp
        ${EFSW_BASE_DIR}/src/efsw/platform/posix/FileSystemImpl.cpp
        ${EFSW_BASE_DIR}/src/efsw/platform/posix/SystemImpl.cpp
    )
elseif(CMAKE_SYSTEM_NAME MATCHES "Darwin") # macOS
    list(APPEND EFSW_SOURCES
        ${EFSW_BASE_DIR}/src/efsw/FileWatcherFSEvents.cpp
        ${EFSW_BASE_DIR}/src/efsw/WatcherFSEvents.cpp
        ${EFSW_BASE_DIR}/src/efsw/platform/posix/FileSystemImpl.cpp
        ${EFSW_BASE_DIR}/src/efsw/platform/posix/SystemImpl.cpp
    )
elseif(WIN32)
    list(APPEND EFSW_SOURCES
        ${EFSW_BASE_DIR}/src/efsw/FileWatcherWin32.cpp
        ${EFSW_BASE_DIR}/src/efsw/WatcherWin32.cpp
        ${EFSW_BASE_DIR}/src/efsw/platform/win/FileSystemImpl.cpp
        ${EFSW_BASE_DIR}/src/efsw/platform/win/SystemImpl.cpp
    )
else()
    list(APPEND EFSW_SOURCES
        ${EFSW_BASE_DIR}/src/efsw/FileWatcherGeneric.cpp
        ${EFSW_BASE_DIR}/src/efsw/WatcherGeneric.cpp
    )
endif()

if(EFSW_SOURCES)
    add_library(efsw STATIC ${EFSW_SOURCES})

    target_include_directories(efsw PUBLIC
        ${EFSW_BASE_DIR}/include
        ${EFSW_BASE_DIR}/src
    )

    # Force static runtime for EFSW to match main executable
    if(MSVC)
        target_compile_options(efsw PRIVATE
            $<$<CONFIG:Debug>:/MTd>
            $<$<CONFIG:Release>:/MT>
        )
    endif()

    find_package(Threads REQUIRED)
    target_link_libraries(efsw PRIVATE Threads::Threads)

    if(CMAKE_SYSTEM_NAME MATCHES "Linux")
        target_link_libraries(efsw PRIVATE rt)
    elseif(CMAKE_SYSTEM_NAME MATCHES "Darwin")
        target_link_libraries(efsw PRIVATE CoreServices)
    endif()

    message(STATUS "Added EFSW from deps folder.")
    set(EFSW_LIBRARIES efsw)
else()
    message(WARNING "EFSW sources missing. Live reload feature disabled.")
    set(EFSW_LIBRARIES "")
endif()

# ----------------------------------------------------
# 5. Platform-Specific Settings
# ----------------------------------------------------

# Windows-specific optimizations
if(WIN32)
    set(VCPKG_APPLOCAL_DEPS OFF)
    set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS OFF)
    set(CMAKE_COLOR_MAKEFILE OFF)

    if(MINGW)
        set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static-libgcc -static-libstdc++ -static")
    endif()

    if(MSVC)
        set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
    endif()
endif()

# Find packages - platform-specific approach
if(WIN32)
    set(PDCURSESMOD_DIR ${DEPS_DIR}/PDCursesMod/wincon)

    if(EXISTS "${PDCURSESMOD_DIR}/pdcurses.lib")
        message(STATUS "Using locally built PDCursesMod from ${PDCURSESMOD_DIR}")

        add_library(pdcursesmod STATIC IMPORTED)
        set_target_properties(pdcursesmod PROPERTIES
            IMPORTED_LOCATION "${PDCURSESMOD_DIR}/pdcurses.lib"
            INTERFACE_INCLUDE_DIRECTORIES "${DEPS_DIR}/PDCursesMod"
        )

        set(CURSES_LIBRARIES pdcursesmod)
        set(CURSES_INCLUDE_DIRS "${DEPS_DIR}/PDCursesMod")
    else()
        message(FATAL_ERROR "PDCursesMod not found at ${PDCURSESMOD_DIR}. Please build it manually with nmake -f Makefile.vc HAVE_VT=Y")
    endif()

elseif(ANDROID)
    find_package(PkgConfig REQUIRED)
    pkg_check_modules(NCURSES REQUIRED ncurses)
    set(CURSES_LIBRARIES ${NCURSES_LIBRARIES})
    set(CURSES_INCLUDE_DIRS ${NCURSES_INCLUDE_DIRS})

else()
    find_package(Curses REQUIRED)
    set(CURSES_LIBRARIES ${CURSES_LIBRARIES})
    set(CURSES_INCLUDE_DIRS ${CURSES_INCLUDE_DIR})
endif()

find_package(yaml-cpp CONFIG REQUIRED)

# ----------------------------------------------------
# 6. Main Executable
# ----------------------------------------------------

# Source files
set(SOURCES
    src/main.cpp
    src/core/editor.cpp
    src/core/buffer.cpp
    src/core/config_manager.cpp
    src/ui/input_handler.cpp
    # src/ui/renderer.cpp
    src/ui/style_manager.cpp
    src/features/syntax_config_loader.cpp
    src/features/syntax_highlighter.cpp
)

# Create executable
add_executable(arc ${SOURCES})

# Conditionally add Tree-sitter compile definition
if(TREE_SITTER_ENABLED)
    target_compile_definitions(arc PRIVATE TREE_SITTER_ENABLED)
    message(STATUS "Compiling with Tree-sitter support enabled")
else()
    message(STATUS "Compiling without Tree-sitter support")
endif()

# Link libraries
target_link_libraries(arc PRIVATE
    yaml-cpp::yaml-cpp
    ${CURSES_LIBRARIES}
    ${TS_LIBRARIES}
    ${EFSW_LIBRARIES}
)

if(WIN32)
    # Link the Windows Multimedia library required by PDCursesMod's beep()
    target_link_libraries(arc PRIVATE winmm)
endif()

# Include directories
target_include_directories(arc PRIVATE
    .
    ${CURSES_INCLUDE_DIRS}
    ${TS_INCLUDES}
)

# Add generated headers directory if Tree-sitter is enabled
if(TREE_SITTER_ENABLED)
    target_include_directories(arc PRIVATE ${CMAKE_BINARY_DIR}/generated)
endif()

# Compiler flags with optimizations
if(MSVC)
    target_compile_options(arc PRIVATE /W4 /MP)
    target_compile_options(arc PRIVATE $<$<CONFIG:Debug>:/MTd> $<$<CONFIG:Release>:/MT>)
else()
    target_compile_options(arc PRIVATE -Wall -Wextra)

    if(ANDROID)
        target_link_libraries(arc PRIVATE ${NCURSES_LINK_LIBRARIES})
    endif()

    if(CMAKE_BUILD_TYPE STREQUAL "Debug")
        target_compile_options(arc PRIVATE -g1 -O0 -fno-omit-frame-pointer)
    else()
        target_compile_options(arc PRIVATE -O2)
    endif()
endif()

# ----------------------------------------------------
# 7. Build Summary
# ----------------------------------------------------

message(STATUS "")
message(STATUS "========================================")
message(STATUS "Arc Editor Build Configuration")
message(STATUS "========================================")
message(STATUS "Build type: ${CMAKE_BUILD_TYPE}")
message(STATUS "Compiler: ${CMAKE_CXX_COMPILER_ID}")
message(STATUS "Platform: ${CMAKE_SYSTEM_NAME}")
message(STATUS "Curses library: ${CURSES_LIBRARIES}")
message(STATUS "Tree-sitter enabled: ${TREE_SITTER_ENABLED}")
if(TREE_SITTER_ENABLED)
    message(STATUS "  Tree-sitter libraries: ${TS_LIBRARIES}")
    message(STATUS "  Tree-sitter includes: ${TS_INCLUDES}")
    message(STATUS "  Discovered parsers: ${DISCOVERED_PARSERS}")
endif()
message(STATUS "EFSW enabled: ${EFSW_LIBRARIES}")
if(WIN32)
    message(STATUS "VCPKG_APPLOCAL_DEPS: ${VCPKG_APPLOCAL_DEPS}")
endif()
message(STATUS "========================================")
message(STATUS "")

```

--------------------------------------------------------------------------------
/deps/tree-sitter-markdown/bindings/rust/parser.rs:
--------------------------------------------------------------------------------

```rust
use std::collections::HashMap;
use std::num::NonZeroU16;

use tree_sitter::{InputEdit, Language, Node, Parser, Point, Range, Tree, TreeCursor};

use crate::{INLINE_LANGUAGE, LANGUAGE};

/// A parser that produces [`MarkdownTree`]s.
///
/// This is a convenience wrapper around [`LANGUAGE`] and [`INLINE_LANGUAGE`].
pub struct MarkdownParser {
    parser: Parser,
    block_language: Language,
    inline_language: Language,
}

/// A stateful object for walking a [`MarkdownTree`] efficiently.
///
/// This exposes the same methdos as [`TreeCursor`], but abstracts away the
/// double block / inline structure of [`MarkdownTree`].
pub struct MarkdownCursor<'a> {
    markdown_tree: &'a MarkdownTree,
    block_cursor: TreeCursor<'a>,
    inline_cursor: Option<TreeCursor<'a>>,
}

impl<'a> MarkdownCursor<'a> {
    /// Get the cursor's current [`Node`].
    pub fn node(&self) -> Node<'a> {
        match &self.inline_cursor {
            Some(cursor) => cursor.node(),
            None => self.block_cursor.node(),
        }
    }

    /// Returns `true` if the current node is from the (inline language)[INLINE_LANGUAGE]
    ///
    /// This information is needed to handle "tree-sitter internal" data like
    /// [`field_id`](Self::field_id) correctly.
    pub fn is_inline(&self) -> bool {
        self.inline_cursor.is_some()
    }

    /// Get the numerical field id of this tree cursor’s current node.
    ///
    /// You will need to call [`is_inline`](Self::is_inline) to find out if the
    /// current node is an inline or block node.
    ///
    /// See also [`field_name`](Self::field_name).
    pub fn field_id(&self) -> Option<NonZeroU16> {
        match &self.inline_cursor {
            Some(cursor) => cursor.field_id(),
            None => self.block_cursor.field_id(),
        }
    }

    /// Get the field name of this tree cursor’s current node.
    ///
    /// You will need to call [`is_inline`](Self::is_inline) to find out if the
    /// current node is an inline or block node.
    pub fn field_name(&self) -> Option<&'static str> {
        match &self.inline_cursor {
            Some(cursor) => cursor.field_name(),
            None => self.block_cursor.field_name(),
        }
    }

    fn move_to_inline_tree(&mut self) -> bool {
        let node = self.block_cursor.node();
        match node.kind() {
            "inline" | "pipe_table_cell" => {
                if let Some(inline_tree) = self.markdown_tree.inline_tree(&node) {
                    self.inline_cursor = Some(inline_tree.walk());
                    return true;
                }
            }
            _ => (),
        }
        false
    }

    fn move_to_block_tree(&mut self) {
        self.inline_cursor = None;
    }

    /// Move this cursor to the first child of its current node.
    ///
    /// This returns `true` if the cursor successfully moved, and returns `false` if there were no
    /// children.
    /// If the cursor is currently at a node in the block tree and it has an associated inline tree, it
    /// will descend into the inline tree.
    pub fn goto_first_child(&mut self) -> bool {
        match &mut self.inline_cursor {
            Some(cursor) => cursor.goto_first_child(),
            None => {
                if self.move_to_inline_tree() {
                    if !self.inline_cursor.as_mut().unwrap().goto_first_child() {
                        self.move_to_block_tree();
                        false
                    } else {
                        true
                    }
                } else {
                    self.block_cursor.goto_first_child()
                }
            }
        }
    }

    /// Move this cursor to the parent of its current node.
    ///
    /// This returns true if the cursor successfully moved, and returns false if there was no
    /// parent node (the cursor was already on the root node).
    /// If the cursor moves to the root node of an inline tree, the it ascents to the associated
    /// node in the block tree.
    pub fn goto_parent(&mut self) -> bool {
        match &mut self.inline_cursor {
            Some(inline_cursor) => {
                inline_cursor.goto_parent();
                if inline_cursor.node().parent().is_none() {
                    self.move_to_block_tree();
                }
                true
            }
            None => self.block_cursor.goto_parent(),
        }
    }

    /// Move this cursor to the next sibling of its current node.
    ///
    /// This returns true if the cursor successfully moved, and returns false if there was no next
    /// sibling node.
    pub fn goto_next_sibling(&mut self) -> bool {
        match &mut self.inline_cursor {
            Some(inline_cursor) => inline_cursor.goto_next_sibling(),
            None => self.block_cursor.goto_next_sibling(),
        }
    }

    /// Move this cursor to the first child of its current node that extends beyond the given byte offset.
    ///
    /// This returns the index of the child node if one was found, and returns None if no such child was found.
    /// If the cursor is currently at a node in the block tree and it has an associated inline tree, it
    /// will descend into the inline tree.
    pub fn goto_first_child_for_byte(&mut self, index: usize) -> Option<usize> {
        match &mut self.inline_cursor {
            Some(cursor) => cursor.goto_first_child_for_byte(index),
            None => {
                if self.move_to_inline_tree() {
                    self.inline_cursor
                        .as_mut()
                        .unwrap()
                        .goto_first_child_for_byte(index)
                } else {
                    self.block_cursor.goto_first_child_for_byte(index)
                }
            }
        }
    }

    /// Move this cursor to the first child of its current node that extends beyond the given point.
    ///
    /// This returns the index of the child node if one was found, and returns None if no such child was found.
    /// If the cursor is currently at a node in the block tree and it has an associated inline tree, it
    /// will descend into the inline tree.
    pub fn goto_first_child_for_point(&mut self, index: Point) -> Option<usize> {
        match &mut self.inline_cursor {
            Some(cursor) => cursor.goto_first_child_for_point(index),
            None => {
                if self.move_to_inline_tree() {
                    self.inline_cursor
                        .as_mut()
                        .unwrap()
                        .goto_first_child_for_point(index)
                } else {
                    self.block_cursor.goto_first_child_for_point(index)
                }
            }
        }
    }
}

/// An object that holds a combined markdown tree.
#[derive(Debug, Clone)]
pub struct MarkdownTree {
    block_tree: Tree,
    inline_trees: Vec<Tree>,
    inline_indices: HashMap<usize, usize>,
}

impl MarkdownTree {
    /// Edit the block tree and inline trees to keep them in sync with source code that has been
    /// edited.
    ///
    /// You must describe the edit both in terms of byte offsets and in terms of
    /// row/column coordinates.
    pub fn edit(&mut self, edit: &InputEdit) {
        self.block_tree.edit(edit);
        for inline_tree in self.inline_trees.iter_mut() {
            inline_tree.edit(edit);
        }
    }

    /// Returns the block tree for the parsed document
    pub fn block_tree(&self) -> &Tree {
        &self.block_tree
    }

    /// Returns the inline tree for the given inline node.
    ///
    /// Returns `None` if the given node does not have an associated inline tree. Either because
    /// the nodes type is not `inline` or because the inline content is empty.
    pub fn inline_tree(&self, parent: &Node) -> Option<&Tree> {
        let index = *self.inline_indices.get(&parent.id())?;
        Some(&self.inline_trees[index])
    }

    /// Returns the list of all inline trees
    pub fn inline_trees(&self) -> &[Tree] {
        &self.inline_trees
    }

    /// Create a new [`MarkdownCursor`] starting from the root of the tree.
    pub fn walk(&self) -> MarkdownCursor {
        MarkdownCursor {
            markdown_tree: self,
            block_cursor: self.block_tree.walk(),
            inline_cursor: None,
        }
    }
}

impl Default for MarkdownParser {
    fn default() -> Self {
        let block_language = LANGUAGE.into();
        let inline_language = INLINE_LANGUAGE.into();
        let parser = Parser::new();
        MarkdownParser {
            parser,
            block_language,
            inline_language,
        }
    }
}

impl MarkdownParser {
    /// Parse a slice of UTF8 text.
    ///
    /// # Arguments:
    /// * `text` The UTF8-encoded text to parse.
    /// * `old_tree` A previous syntax tree parsed from the same document.
    ///   If the text of the document has changed since `old_tree` was
    ///   created, then you must edit `old_tree` to match the new text using
    ///   [MarkdownTree::edit].
    ///
    /// Returns a [MarkdownTree] if parsing succeeded, or `None` if:
    ///  * The timeout set with [tree_sitter::Parser::set_timeout_micros] expired
    ///  * The cancellation flag set with [tree_sitter::Parser::set_cancellation_flag] was flipped
    pub fn parse_with<T: AsRef<[u8]>, F: FnMut(usize, Point) -> T>(
        &mut self,
        callback: &mut F,
        old_tree: Option<&MarkdownTree>,
    ) -> Option<MarkdownTree> {
        let MarkdownParser {
            parser,
            block_language,
            inline_language,
        } = self;
        parser
            .set_included_ranges(&[])
            .expect("Can not set included ranges to whole document");
        parser
            .set_language(block_language)
            .expect("Could not load block grammar");
        let block_tree = parser.parse_with(callback, old_tree.map(|tree| &tree.block_tree))?;
        let (mut inline_trees, mut inline_indices) = if let Some(old_tree) = old_tree {
            let len = old_tree.inline_trees.len();
            (Vec::with_capacity(len), HashMap::with_capacity(len))
        } else {
            (Vec::new(), HashMap::new())
        };
        parser
            .set_language(inline_language)
            .expect("Could not load inline grammar");
        let mut tree_cursor = block_tree.walk();

        let mut i = 0;
        'outer: loop {
            let node = loop {
                let kind = tree_cursor.node().kind();
                if kind == "inline" || kind == "pipe_table_cell" || !tree_cursor.goto_first_child()
                {
                    while !tree_cursor.goto_next_sibling() {
                        if !tree_cursor.goto_parent() {
                            break 'outer;
                        }
                    }
                }
                let kind = tree_cursor.node().kind();
                if kind == "inline" || kind == "pipe_table_cell" {
                    break tree_cursor.node();
                }
            };
            let mut range = node.range();
            let mut ranges = Vec::new();
            if tree_cursor.goto_first_child() {
                while tree_cursor.goto_next_sibling() {
                    if !tree_cursor.node().is_named() {
                        continue;
                    }
                    let child_range = tree_cursor.node().range();
                    ranges.push(Range {
                        start_byte: range.start_byte,
                        start_point: range.start_point,
                        end_byte: child_range.start_byte,
                        end_point: child_range.start_point,
                    });
                    range.start_byte = child_range.end_byte;
                    range.start_point = child_range.end_point;
                }
                tree_cursor.goto_parent();
            }
            ranges.push(range);
            parser.set_included_ranges(&ranges).ok()?;
            let inline_tree = parser.parse_with(
                callback,
                old_tree.and_then(|old_tree| old_tree.inline_trees.get(i)),
            )?;
            inline_trees.push(inline_tree);
            inline_indices.insert(node.id(), i);
            i += 1;
        }
        drop(tree_cursor);
        inline_trees.shrink_to_fit();
        inline_indices.shrink_to_fit();
        Some(MarkdownTree {
            block_tree,
            inline_trees,
            inline_indices,
        })
    }

    /// Parse a slice of UTF8 text.
    ///
    /// # Arguments:
    /// * `text` The UTF8-encoded text to parse.
    /// * `old_tree` A previous syntax tree parsed from the same document.
    ///   If the text of the document has changed since `old_tree` was
    ///   created, then you must edit `old_tree` to match the new text using
    ///   [MarkdownTree::edit].
    ///
    /// Returns a [MarkdownTree] if parsing succeeded, or `None` if:
    ///  * The timeout set with [tree_sitter::Parser::set_timeout_micros] expired
    ///  * The cancellation flag set with [tree_sitter::Parser::set_cancellation_flag] was flipped
    pub fn parse(&mut self, text: &[u8], old_tree: Option<&MarkdownTree>) -> Option<MarkdownTree> {
        self.parse_with(&mut |byte, _| &text[byte..], old_tree)
    }
}

#[cfg(test)]
mod tests {
    use tree_sitter::{InputEdit, Point};

    use super::*;

    #[test]
    fn inline_ranges() {
        let code = "# title\n\nInline [content].\n";
        let mut parser = MarkdownParser::default();
        let mut tree = parser.parse(code.as_bytes(), None).unwrap();

        let section = tree.block_tree().root_node().child(0).unwrap();
        assert_eq!(section.kind(), "section");
        let heading = section.child(0).unwrap();
        assert_eq!(heading.kind(), "atx_heading");
        let paragraph = section.child(1).unwrap();
        assert_eq!(paragraph.kind(), "paragraph");
        let inline = paragraph.child(0).unwrap();
        assert_eq!(inline.kind(), "inline");
        assert_eq!(
            tree.inline_tree(&inline)
                .unwrap()
                .root_node()
                .child(0)
                .unwrap()
                .kind(),
            "shortcut_link"
        );

        let code = "# Title\n\nInline [content].\n";
        tree.edit(&InputEdit {
            start_byte: 2,
            old_end_byte: 3,
            new_end_byte: 3,
            start_position: Point { row: 0, column: 2 },
            old_end_position: Point { row: 0, column: 3 },
            new_end_position: Point { row: 0, column: 3 },
        });
        let tree = parser.parse(code.as_bytes(), Some(&tree)).unwrap();

        let section = tree.block_tree().root_node().child(0).unwrap();
        assert_eq!(section.kind(), "section");
        let heading = section.child(0).unwrap();
        assert_eq!(heading.kind(), "atx_heading");
        let paragraph = section.child(1).unwrap();
        assert_eq!(paragraph.kind(), "paragraph");
        let inline = paragraph.child(0).unwrap();
        assert_eq!(inline.kind(), "inline");
        assert_eq!(
            tree.inline_tree(&inline)
                .unwrap()
                .root_node()
                .named_child(0)
                .unwrap()
                .kind(),
            "shortcut_link"
        );
    }

    #[test]
    fn markdown_cursor() {
        let code = "# title\n\nInline [content].\n";
        let mut parser = MarkdownParser::default();
        let tree = parser.parse(code.as_bytes(), None).unwrap();
        let mut cursor = tree.walk();
        assert_eq!(cursor.node().kind(), "document");
        assert!(cursor.goto_first_child());
        assert_eq!(cursor.node().kind(), "section");
        assert!(cursor.goto_first_child());
        assert_eq!(cursor.node().kind(), "atx_heading");
        assert!(cursor.goto_next_sibling());
        assert_eq!(cursor.node().kind(), "paragraph");
        assert!(cursor.goto_first_child());
        assert_eq!(cursor.node().kind(), "inline");
        assert!(cursor.goto_first_child());
        assert_eq!(cursor.node().kind(), "shortcut_link");
        assert!(cursor.goto_parent());
        assert!(cursor.goto_parent());
        assert!(cursor.goto_parent());
        assert!(cursor.goto_parent());
        assert_eq!(cursor.node().kind(), "document");
    }

    #[test]
    fn table() {
        let code = "| foo |\n| --- |\n| *bar*|\n";
        let mut parser = MarkdownParser::default();
        let tree = parser.parse(code.as_bytes(), None).unwrap();
        dbg!(&tree.inline_trees());
        let mut cursor = tree.walk();

        assert_eq!(cursor.node().kind(), "document");
        assert!(cursor.goto_first_child());
        assert_eq!(cursor.node().kind(), "section");
        assert!(cursor.goto_first_child());
        assert_eq!(cursor.node().kind(), "pipe_table");
        assert!(cursor.goto_first_child());
        assert!(cursor.goto_next_sibling());
        assert!(cursor.goto_next_sibling());
        assert_eq!(cursor.node().kind(), "pipe_table_row");
        assert!(cursor.goto_first_child());
        assert!(cursor.goto_next_sibling());
        assert_eq!(cursor.node().kind(), "pipe_table_cell");
        assert!(cursor.goto_first_child());
        assert_eq!(cursor.node().kind(), "emphasis");
    }
}

```

--------------------------------------------------------------------------------
/deps/tree-sitter-markdown/tree-sitter-markdown-inline/grammar.js:
--------------------------------------------------------------------------------

```javascript
// This grammar only concerns the inline structure according to the CommonMark Spec
// (https://spec.commonmark.org/0.30/#inlines)
// For more information see README.md

/// <reference types="tree-sitter-cli/dsl" />

const common = require('../common/common');

// Levels used for dynmic precedence. Ideally
// n * PRECEDENCE_LEVEL_EMPHASIS > PRECEDENCE_LEVEL_LINK for any n, so maybe the
// maginuted of these values should be increased in the future
const PRECEDENCE_LEVEL_EMPHASIS = 1;
const PRECEDENCE_LEVEL_LINK = 10;
const PRECEDENCE_LEVEL_HTML = 100;

// Punctuation characters as specified in
// https://github.github.com/gfm/#ascii-punctuation-character
const PUNCTUATION_CHARACTERS_REGEX = '!-/:-@\\[-`\\{-~';


// !!!
// Notice the call to `add_inline_rules` which generates some additional rules related to parsing
// inline contents in different contexts.
// !!!
module.exports = grammar(add_inline_rules({
    name: 'markdown_inline',

    externals: $ => [
        // An `$._error` token is never valid  and gets emmited to kill invalid parse branches. Concretely
        // this is used to decide wether a newline closes a paragraph and together and it gets emitted
        // when trying to parse the `$._trigger_error` token in `$.link_title`.
        $._error,
        $._trigger_error,

        // Opening and closing delimiters for code spans. These are sequences of one or more backticks.
        // An opening token does not mean the text after has to be a code span if there is no closing token
        $._code_span_start,
        $._code_span_close,

        // Opening and closing delimiters for emphasis.
        $._emphasis_open_star,
        $._emphasis_open_underscore,
        $._emphasis_close_star,
        $._emphasis_close_underscore,

        // For emphasis we need to tell the parser if the last character was a whitespace (or the
        // beginning of a line) or a punctuation. These tokens never actually get emitted.
        $._last_token_whitespace,
        $._last_token_punctuation,

        $._strikethrough_open,
        $._strikethrough_close,

        // Opening and closing delimiters for latex. These are sequences of one or more dollar signs.
        // An opening token does not mean the text after has to be latex if there is no closing token
        $._latex_span_start,
        $._latex_span_close,

        // Token emmited when encountering opening delimiters for a leaf span
        // e.g. a code span, that does not have a matching closing span
        $._unclosed_span
    ],
    precedences: $ => [
        // [$._strong_emphasis_star, $._inline_element_no_star],
        [$._strong_emphasis_star_no_link, $._inline_element_no_star_no_link],
        // [$._strong_emphasis_underscore, $._inline_element_no_underscore],
        [$._strong_emphasis_underscore_no_link, $._inline_element_no_underscore_no_link],
        [$.hard_line_break, $._whitespace],
        [$.hard_line_break, $._text_base],
    ],
    // More conflicts are defined in `add_inline_rules`
    conflicts: $ => [

        [$._closing_tag, $._text_base],
        [$._open_tag, $._text_base],
        [$._html_comment, $._text_base],
        [$._processing_instruction, $._text_base],
        [$._declaration, $._text_base],
        [$._cdata_section, $._text_base],

        [$._link_text_non_empty, $._inline_element],
        [$._link_text_non_empty, $._inline_element_no_star],
        [$._link_text_non_empty, $._inline_element_no_underscore],
        [$._link_text_non_empty, $._inline_element_no_tilde],
        [$._link_text, $._inline_element],
        [$._link_text, $._inline_element_no_star],
        [$._link_text, $._inline_element_no_underscore],
        [$._link_text, $._inline_element_no_tilde],

        [$._image_description, $._image_description_non_empty, $._text_base],
        // [$._image_description, $._image_description_non_empty, $._text_inline],
        // [$._image_description, $._image_description_non_empty, $._text_inline_no_star],
        // [$._image_description, $._image_description_non_empty, $._text_inline_no_underscore],

        [$._image_shortcut_link, $._image_description],
        [$.shortcut_link, $._link_text],
        [$.link_destination, $.link_title],
        [$._link_destination_parenthesis, $.link_title],

        [$.wiki_link, $._inline_element],
        [$.wiki_link, $._inline_element_no_star],
        [$.wiki_link, $._inline_element_no_underscore],
        [$.wiki_link, $._inline_element_no_tilde],
    ],
    extras: $ => [],

    rules: {
        inline: $ => seq(optional($._last_token_whitespace), $._inline),

        ...common.rules,


        // A lot of inlines are defined in `add_inline_rules`, including:
        //
        // * collections of inlines
        // * emphasis
        // * textual content
        //
        // This is done to reduce code duplication, as some inlines need to be parsed differently
        // depending on the context. For example inlines in ATX headings may not contain newlines.

        code_span: $ => seq(
            alias($._code_span_start, $.code_span_delimiter),
            repeat(choice($._text_base, '[', ']', $._soft_line_break, $._html_tag)),
            alias($._code_span_close, $.code_span_delimiter)
        ),

        latex_block: $ => seq(
            alias($._latex_span_start, $.latex_span_delimiter),
            repeat(choice($._text_base, '[', ']', $._soft_line_break, $._html_tag, $.backslash_escape)),
            alias($._latex_span_close, $.latex_span_delimiter),
        ),

        // Different kinds of links:
        // * inline links (https://github.github.com/gfm/#inline-link)
        // * full reference links (https://github.github.com/gfm/#full-reference-link)
        // * collapsed reference links (https://github.github.com/gfm/#collapsed-reference-link)
        // * shortcut links (https://github.github.com/gfm/#shortcut-reference-link)
        //
        // Dynamic precedence is distributed as granular as possible to help the parser decide
        // while parsing which branch is the most important.
        //
        // https://github.github.com/gfm/#links
        _link_text: $ => prec.dynamic(PRECEDENCE_LEVEL_LINK, choice(
            $._link_text_non_empty,
            seq('[', ']')
        )),
        _link_text_non_empty: $ => seq('[', alias($._inline_no_link, $.link_text), ']'),
        shortcut_link: $ => prec.dynamic(PRECEDENCE_LEVEL_LINK, $._link_text_non_empty),
        full_reference_link: $ => prec.dynamic(2 * PRECEDENCE_LEVEL_LINK, seq(
            $._link_text,
            $.link_label
        )),
        collapsed_reference_link: $ => prec.dynamic(PRECEDENCE_LEVEL_LINK, seq(
            $._link_text,
            '[',
            ']'
        )),
        inline_link: $ => prec.dynamic(PRECEDENCE_LEVEL_LINK, seq(
            $._link_text,
            '(',
            repeat(choice($._whitespace, $._soft_line_break)),
            optional(seq(
                choice(
                    seq(
                        $.link_destination,
                        optional(seq(
                            repeat1(choice($._whitespace, $._soft_line_break)),
                            $.link_title
                        ))
                    ),
                    $.link_title,
                ),
                repeat(choice($._whitespace, $._soft_line_break)),
            )),
            ')'
        )),

        wiki_link: $ => prec.dynamic(2 * PRECEDENCE_LEVEL_LINK, seq(
            '[', '[',
            alias($._wiki_link_destination, $.link_destination),
            optional(seq(
                '|',
                alias($._wiki_link_text, $.link_text)
            )),
            ']', ']'
            )
        ),

        _wiki_link_destination: $ => repeat1(choice(
            $._word,
            common.punctuation_without($, ['[',']', '|']),
            $._whitespace,
        )),

        _wiki_link_text: $ => repeat1(choice(
            $._word,
            common.punctuation_without($, ['[',']']),
            $._whitespace,
        )),

        // Images work exactly like links with a '!' added in front.
        //
        // https://github.github.com/gfm/#images
        image: $ => choice(
            $._image_inline_link,
            $._image_shortcut_link,
            $._image_full_reference_link,
            $._image_collapsed_reference_link
        ),
        _image_inline_link: $ => prec.dynamic(PRECEDENCE_LEVEL_LINK, seq(
            $._image_description,
            '(',
            repeat(choice($._whitespace, $._soft_line_break)),
            optional(seq(
                choice(
                    seq(
                        $.link_destination,
                        optional(seq(
                            repeat1(choice($._whitespace, $._soft_line_break)),
                            $.link_title
                        ))
                    ),
                    $.link_title,
                ),
                repeat(choice($._whitespace, $._soft_line_break)),
            )),
            ')'
        )),
        _image_shortcut_link: $ => prec.dynamic(3 * PRECEDENCE_LEVEL_LINK, $._image_description_non_empty),
        _image_full_reference_link: $ => prec.dynamic(PRECEDENCE_LEVEL_LINK, seq($._image_description, $.link_label)),
        _image_collapsed_reference_link: $ => prec.dynamic(PRECEDENCE_LEVEL_LINK, seq($._image_description, '[', ']')),
        _image_description: $ => prec.dynamic(3 * PRECEDENCE_LEVEL_LINK, choice($._image_description_non_empty, seq('!', '[', prec(1, ']')))),
        _image_description_non_empty: $ => seq('!', '[', alias($._inline, $.image_description), prec(1, ']')),

        // Autolinks. Uri autolinks actually accept protocolls of arbitrary length which does not
        // align with the spec. This is because the binary for the grammar gets to large if done
        // otherwise as tree-sitters code generation is not very concise for this type of regex.
        //
        // Email autolinks do not match every valid email (emails normally should not be parsed
        // using regexes), but this is how they are defined in the spec.
        //
        // https://github.github.com/gfm/#autolinks
        uri_autolink: $ => /<[a-zA-Z][a-zA-Z0-9+\.\-][a-zA-Z0-9+\.\-]*:[^ \t\r\n<>]*>/,
        email_autolink: $ =>
            /<[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*>/,

        // Raw html. As with html blocks we do not emit additional information as this is best done
        // by a proper html tree-sitter grammar.
        //
        // https://github.github.com/gfm/#raw-html
        _html_tag: $ => choice($._open_tag, $._closing_tag, $._html_comment, $._processing_instruction, $._declaration, $._cdata_section),
        _open_tag: $ => prec.dynamic(PRECEDENCE_LEVEL_HTML, seq('<', $._tag_name, repeat($._attribute), repeat(choice($._whitespace, $._soft_line_break)), optional('/'), '>')),
        _closing_tag: $ => prec.dynamic(PRECEDENCE_LEVEL_HTML, seq('<', '/', $._tag_name, repeat(choice($._whitespace, $._soft_line_break)), '>')),
        _tag_name: $ => seq($._word_no_digit, repeat(choice($._word_no_digit, $._digits, '-'))),
        _attribute: $ => seq(repeat1(choice($._whitespace, $._soft_line_break)), $._attribute_name, repeat(choice($._whitespace, $._soft_line_break)), '=', repeat(choice($._whitespace, $._soft_line_break)), $._attribute_value),
        _attribute_name: $ => /[a-zA-Z_:][a-zA-Z0-9_\.:\-]*/,
        _attribute_value: $ => choice(
            /[^ \t\r\n"'=<>`]+/,
            seq("'", repeat(choice($._word, $._whitespace, $._soft_line_break, common.punctuation_without($, ["'"]))), "'"),
            seq('"', repeat(choice($._word, $._whitespace, $._soft_line_break, common.punctuation_without($, ['"']))), '"'),
        ),
        _html_comment: $ => prec.dynamic(PRECEDENCE_LEVEL_HTML, seq(
            '<!--',
            optional(seq(
                choice(
                    $._word,
                    $._whitespace,
                    $._soft_line_break,
                    common.punctuation_without($, ['-', '>']),
                    seq(
                        '-',
                        common.punctuation_without($, ['>']),
                    )
                ),
                repeat(prec.right(choice(
                    $._word,
                    $._whitespace,
                    $._soft_line_break,
                    common.punctuation_without($, ['-']),
                    seq(
                        '-',
                        choice(
                            $._word,
                            $._whitespace,
                            $._soft_line_break,
                            common.punctuation_without($, ['-']),
                        )
                    )
                ))),
            )),
            '-->'
        )),
        _processing_instruction: $ => prec.dynamic(PRECEDENCE_LEVEL_HTML, seq(
            '<?',
            repeat(prec.right(choice(
                $._word,
                $._whitespace,
                $._soft_line_break,
                common.punctuation_without($, []),
            ))),
            '?>'
        )),
        _declaration: $ => prec.dynamic(PRECEDENCE_LEVEL_HTML, seq(
            /<![A-Z]+/,
            choice(
                $._whitespace,
                $._soft_line_break,
            ),
            repeat(prec.right(choice(
                $._word,
                $._whitespace,
                $._soft_line_break,
                common.punctuation_without($, ['>']),
            ))),
            '>'
        )),
        _cdata_section: $ => prec.dynamic(PRECEDENCE_LEVEL_HTML, seq(
            '<![CDATA[',
            repeat(prec.right(choice(
                $._word,
                $._whitespace,
                $._soft_line_break,
                common.punctuation_without($, []),
            ))),
            ']]>'
        )),

        // A hard line break.
        //
        // https://github.github.com/gfm/#hard-line-breaks
        hard_line_break: $ => seq(choice('\\', $._whitespace_ge_2), $._soft_line_break),
        _text: $ => choice($._word, common.punctuation_without($, []), $._whitespace),

        // Whitespace is divided into single whitespaces and multiple whitespaces as wee need this
        // information for hard line breaks.
        _whitespace_ge_2: $ => /\t| [ \t]+/,
        _whitespace: $ => seq(choice($._whitespace_ge_2, / /), optional($._last_token_whitespace)),

        // Other than whitespace we tokenize into strings of digits, punctuation characters
        // (handled by `common.punctuation_without`) and strings of any other characters. This way the
        // lexer does not have to many different states, which makes it a lot easier to make
        // conflicts work.
        _word: $ => choice($._word_no_digit, $._digits),
        _word_no_digit: $ => new RegExp('[^' + PUNCTUATION_CHARACTERS_REGEX + ' \\t\\n\\r0-9]+(_+[^' + PUNCTUATION_CHARACTERS_REGEX + ' \\t\\n\\r0-9]+)*'),
        _digits: $ => /[0-9][0-9_]*/,
        _soft_line_break: $ => seq($._newline_token, optional($._last_token_whitespace)),

        _inline_base: $ => prec.right(repeat1(choice(
            $.image,
            $._soft_line_break,
            $.backslash_escape,
            $.hard_line_break,
            $.uri_autolink,
            $.email_autolink,
            $.entity_reference,
            $.numeric_character_reference,
            (common.EXTENSION_LATEX ? $.latex_block : choice()),
            $.code_span,
            alias($._html_tag, $.html_tag),
            $._text_base,
            common.EXTENSION_TAGS ? $.tag : choice(),
            $._unclosed_span,
        ))),
        _text_base: $ => choice(
            $._word,
            common.punctuation_without($, ['[', ']']),
            $._whitespace,
            '<!--',
            /<![A-Z]+/,
            '<?',
            '<![CDATA[',
        ),
        _text_inline_no_link: $ => choice(
            $._text_base,
            $._emphasis_open_star,
            $._emphasis_open_underscore,
            $._unclosed_span,
        ),

        ...(common.EXTENSION_TAGS ? {
            tag: $ => /#[0-9]*[a-zA-Z_\-\/][a-zA-Z_\-\/0-9]*/,
        } : {}),

    },
}));

// This function adds some extra inline rules. This is done to reduce code duplication, as some
// rules may not contain newlines, characters like '*' and '_', ... depending on the context.
//
// This is by far the most ugly part of this code and should be cleaned up.
function add_inline_rules(grammar) {
    let conflicts = [];
    for (let link of [true, false]) {
        let suffix_link = link ? "" : "_no_link";
        for (let delimiter of [false, "star", "underscore", "tilde"]) {
            let suffix_delimiter = delimiter ? "_no_" + delimiter : "";
            let suffix = suffix_delimiter + suffix_link;
            grammar.rules["_inline_element" + suffix] = $ => {
                let elements = [
                    $._inline_base,
                    alias($['_emphasis_star' + suffix_link], $.emphasis),
                    alias($['_strong_emphasis_star' + suffix_link], $.strong_emphasis),
                    alias($['_emphasis_underscore' + suffix_link], $.emphasis),
                    alias($['_strong_emphasis_underscore' + suffix_link], $.strong_emphasis),
                ];
                if (common.EXTENSION_STRIKETHROUGH) {
                    elements.push(alias($['_strikethrough' + suffix_link], $.strikethrough));
                }
                if (delimiter !== "star") {
                    elements.push($._emphasis_open_star);
                }
                if (delimiter !== "underscore") {
                    elements.push($._emphasis_open_underscore);
                }
                if (delimiter !== "tilde") {
                    elements.push($._strikethrough_open);
                }
                if (link) {
                    elements = elements.concat([
                        $.shortcut_link,
                        $.full_reference_link,
                        $.collapsed_reference_link,
                        $.inline_link,
                        // (common.EXTENSION_WIKI_LINK && $.wiki_link),
                        seq(choice('[', ']'), optional($._last_token_punctuation)),
                    ]);
                    if (common.EXTENSION_WIKI_LINK) {
                        elements.push($.wiki_link);
                    }
                }
                return choice(...elements);
            };
            grammar.rules["_inline" + suffix] = $ => repeat1($["_inline_element" + suffix]);
            if (delimiter !== "star") {
                conflicts.push(['_emphasis_star' + suffix_link, '_inline_element' + suffix_delimiter + suffix_link]);
                conflicts.push(['_emphasis_star' + suffix_link, '_strong_emphasis_star' + suffix_link, '_inline_element' + suffix_delimiter + suffix_link]);
            }
            if (delimiter == 'star' || delimiter == 'underscore') {
                conflicts.push(['_strong_emphasis_' + delimiter + suffix_link, '_inline_element_no_' + delimiter]);
            }
            if (delimiter !== "underscore") {
                conflicts.push(['_emphasis_underscore' + suffix_link, '_inline_element' + suffix_delimiter + suffix_link]);
                conflicts.push(['_emphasis_underscore' + suffix_link, '_strong_emphasis_underscore' + suffix_link, '_inline_element' + suffix_delimiter + suffix_link]);
            }
            if (delimiter !== "tilde") {
                conflicts.push(['_strikethrough' + suffix_link, '_inline_element' + suffix_delimiter + suffix_link]);
            }
        }

        if (common.EXTENSION_STRIKETHROUGH) {
            grammar.rules['_strikethrough' + suffix_link] = $ => prec.dynamic(PRECEDENCE_LEVEL_EMPHASIS, seq(alias($._strikethrough_open, $.emphasis_delimiter), optional($._last_token_punctuation), $['_inline' + '_no_tilde' + suffix_link], alias($._strikethrough_close, $.emphasis_delimiter)));
        }
        grammar.rules['_emphasis_star' + suffix_link] = $ => prec.dynamic(PRECEDENCE_LEVEL_EMPHASIS, seq(alias($._emphasis_open_star, $.emphasis_delimiter), optional($._last_token_punctuation), $['_inline' + '_no_star' + suffix_link], alias($._emphasis_close_star, $.emphasis_delimiter)));
        grammar.rules['_strong_emphasis_star' + suffix_link] = $ => prec.dynamic(2 * PRECEDENCE_LEVEL_EMPHASIS, seq(alias($._emphasis_open_star, $.emphasis_delimiter), $['_emphasis_star' + suffix_link], alias($._emphasis_close_star, $.emphasis_delimiter)));
        grammar.rules['_emphasis_underscore' + suffix_link] = $ => prec.dynamic(PRECEDENCE_LEVEL_EMPHASIS, seq(alias($._emphasis_open_underscore, $.emphasis_delimiter), optional($._last_token_punctuation), $['_inline' + '_no_underscore' + suffix_link], alias($._emphasis_close_underscore, $.emphasis_delimiter)));
        grammar.rules['_strong_emphasis_underscore' + suffix_link] = $ => prec.dynamic(2 * PRECEDENCE_LEVEL_EMPHASIS, seq(alias($._emphasis_open_underscore, $.emphasis_delimiter), $['_emphasis_underscore' + suffix_link], alias($._emphasis_close_underscore, $.emphasis_delimiter)));
    }

    let old = grammar.conflicts
    grammar.conflicts = $ => {
        let cs = old($);
        for (let conflict of conflicts) {
            let c = [];
            for (let rule of conflict) {
                c.push($[rule]);
            }
            cs.push(c);
        }
        return cs;
    }

    return grammar;
}

```

--------------------------------------------------------------------------------
/src/ui/style_manager.cpp:
--------------------------------------------------------------------------------

```cpp
#include "style_manager.h"
#include <algorithm>
#include <cctype>
#include <cstring>
#include <fstream>
#include <iostream>
#ifdef _WIN32
#include <curses.h>
inline int setenv(const char *name, const char *value, int overwrite)
{
  if (!overwrite)
  {
    size_t envsize = 0;
    getenv_s(&envsize, nullptr, 0, name);
    if (envsize != 0)
      return 0; // Variable exists, don't overwrite
  }
  return _putenv_s(name, value);
}
#else
#include <ncurses.h>
#endif
#include <sstream>

StyleManager::StyleManager()
    : initialized(false), supports_256_colors_cache(false),
      next_custom_color_id(16)
{
}

void StyleManager::initialize()
{
  if (initialized)
  {
    std::cerr << "StyleManager already initialized" << std::endl;
    return;
  }

  // Critical: Check if ncurses has been initialized first
  if (!stdscr)
  {
    std::cerr << "ERROR: ncurses not initialized. Call initscr() first!"
              << std::endl;
    return;
  }

  if (!has_colors())
  {
    std::cerr << "Terminal does not support colors" << std::endl;
    return;
  }

  // Initialize color support
  if (start_color() == ERR)
  {
    std::cerr << "Failed to start color support" << std::endl;
    return;
  }

  // Use default terminal colors - CRITICAL for transparent support
  if (use_default_colors() == ERR)
  {
    std::cerr << "Warning: use_default_colors() failed, using fallback"
              << std::endl;
    // Fallback: assume black background, white foreground
    assume_default_colors(COLOR_WHITE, COLOR_BLACK);
  }

  // Initialize color capability cache and custom color tracking
  supports_256_colors_cache = supports_256_colors();
  next_custom_color_id = 16; // Start custom colors at ID 16
  color_cache.clear();

  // std::cerr << "=== UNIFIED THEME SYSTEM WITH HEX SUPPORT ===" << std::endl;
  // std::cerr << "COLORS: " << COLORS << std::endl;
  // std::cerr << "COLOR_PAIRS: " << COLOR_PAIRS << std::endl;
  // std::cerr << "256-color support: "
  //           << (supports_256_colors_cache ? "YES" : "NO") << std::endl;
  // std::cerr << "TERM: " << (getenv("TERM") ? getenv("TERM") : "not set")
  //           << std::endl;

  // Check for WSL-specific issues
  const char *wsl_distro = getenv("WSL_DISTRO_NAME");
  if (wsl_distro)
  {
    // std::cerr << "WSL detected: " << wsl_distro << std::endl;
    // WSL sometimes has color issues, force TERM if needed
    if (!getenv("TERM") || strcmp(getenv("TERM"), "dumb") == 0)
    {
      // std::cerr << "Setting TERM=xterm-256color for WSL" << std::endl;
      setenv("TERM", "xterm-256color", 1);
    }
  }

  load_default_theme();
  apply_theme();

  initialized = true;
  // std::cerr << "Unified theme system initialized successfully" << std::endl;
}

short StyleManager::resolve_theme_color(const std::string &config_value)
{
  // Handle transparent/default colors
  if (config_value.empty() || config_value == "transparent" ||
      config_value == "default")
  {
    return -1; // Use terminal default
  }

  // Check if it's a hex color
  if (config_value.length() == 7 && config_value[0] == '#')
  {
    // Check cache first
    auto cache_it = color_cache.find(config_value);
    if (cache_it != color_cache.end())
    {
      return cache_it->second;
    }

    // Parse the hex color
    RGB rgb = parse_hex_color(config_value);

    if (supports_256_colors_cache && next_custom_color_id < COLORS)
    {
      // 256-color mode: use init_color for true color mapping
      // Scale R, G, B from 0-255 to ncurses' 0-1000 range
      short r_1000 = (rgb.r * 1000) / 255;
      short g_1000 = (rgb.g * 1000) / 255;
      short b_1000 = (rgb.b * 1000) / 255;

      short color_id = next_custom_color_id;
      if (init_color(color_id, r_1000, g_1000, b_1000) == OK)
      {
        // Cache the mapping and increment for next time
        auto cache_it = color_cache.find(config_value);
        if (cache_it != color_cache.end())
        {
          return cache_it->second; // OK: using iterator
        }

        color_cache[config_value] = color_id;
        const_cast<StyleManager *>(this)->next_custom_color_id++;

        // std::cerr << "Created custom color " << color_id << " for "
        //           << config_value << " (RGB: " << rgb.r << "," << rgb.g <<
        //           ","
        //           << rgb.b << ")" << std::endl;
        return color_id;
      }
      else
      {
        std::cerr << "Failed to create custom color for " << config_value
                  << ", falling back to closest 8-color" << std::endl;
      }
    }

    // Fallback to legacy 8-color mode
    return find_closest_8color(rgb);
  }

  // Legacy named color support (for backward compatibility)
  // This handles old theme files that might still use color names
  ThemeColor legacy_color = string_to_theme_color(config_value);
  return theme_color_to_ncurses_color(legacy_color);
}

// NEW: Hex color parsing utility
RGB StyleManager::parse_hex_color(const std::string &hex_str) const
{
  RGB rgb;

  if (hex_str.length() != 7 || hex_str[0] != '#')
  {
    std::cerr << "Invalid hex color format: " << hex_str << ", using black"
              << std::endl;
    return RGB(0, 0, 0);
  }

  try
  {
    // Parse each component (skip the '#')
    std::string r_str = hex_str.substr(1, 2);
    std::string g_str = hex_str.substr(3, 2);
    std::string b_str = hex_str.substr(5, 2);

    rgb.r = std::stoi(r_str, nullptr, 16);
    rgb.g = std::stoi(g_str, nullptr, 16);
    rgb.b = std::stoi(b_str, nullptr, 16);

    // Clamp to valid range
    rgb.r = std::max(0, std::min(255, rgb.r));
    rgb.g = std::max(0, std::min(255, rgb.g));
    rgb.b = std::max(0, std::min(255, rgb.b));
  }
  catch (const std::exception &e)
  {
    std::cerr << "Error parsing hex color " << hex_str << ": " << e.what()
              << std::endl;
    return RGB(0, 0, 0);
  }

  return rgb;
}

// NEW: Find closest 8-color match for fallback
short StyleManager::find_closest_8color(const RGB &rgb) const
{
  // Define the standard 8 colors in RGB
  struct ColorMapping
  {
    short ncurses_color;
    RGB rgb;
    const char *name;
  };

  static const ColorMapping basic_colors[] = {
      {COLOR_BLACK, RGB(0, 0, 0), "black"},
      {COLOR_RED, RGB(128, 0, 0), "red"},
      {COLOR_GREEN, RGB(0, 128, 0), "green"},
      {COLOR_YELLOW, RGB(128, 128, 0), "yellow"},
      {COLOR_BLUE, RGB(0, 0, 128), "blue"},
      {COLOR_MAGENTA, RGB(128, 0, 128), "magenta"},
      {COLOR_CYAN, RGB(0, 128, 128), "cyan"},
      {COLOR_WHITE, RGB(192, 192, 192), "white"}};

  // Find the closest color using simple Euclidean distance
  double min_distance = 1000000;
  short closest_color = COLOR_WHITE;

  for (const auto &color : basic_colors)
  {
    double dr = rgb.r - color.rgb.r;
    double dg = rgb.g - color.rgb.g;
    double db = rgb.b - color.rgb.b;
    double distance = dr * dr + dg * dg + db * db;

    if (distance < min_distance)
    {
      min_distance = distance;
      closest_color = color.ncurses_color;
    }
  }

  return closest_color;
}

// Legacy function - kept only for load_default_theme compatibility
ThemeColor
StyleManager::string_to_theme_color(const std::string &color_name) const
{
  std::string lower_name = color_name;
  std::transform(lower_name.begin(), lower_name.end(), lower_name.begin(),
                 ::tolower);

  if (lower_name == "black")
    return ThemeColor::BLACK;
  if (lower_name == "dark_gray" || lower_name == "dark_grey")
    return ThemeColor::DARK_GRAY;
  if (lower_name == "gray" || lower_name == "grey")
    return ThemeColor::GRAY;
  if (lower_name == "light_gray" || lower_name == "light_grey")
    return ThemeColor::LIGHT_GRAY;
  if (lower_name == "white")
    return ThemeColor::WHITE;
  if (lower_name == "red")
    return ThemeColor::RED;
  if (lower_name == "green")
    return ThemeColor::GREEN;
  if (lower_name == "blue")
    return ThemeColor::BLUE;
  if (lower_name == "yellow")
    return ThemeColor::YELLOW;
  if (lower_name == "magenta")
    return ThemeColor::MAGENTA;
  if (lower_name == "cyan")
    return ThemeColor::CYAN;
  if (lower_name == "bright_red")
    return ThemeColor::BRIGHT_RED;
  if (lower_name == "bright_green")
    return ThemeColor::BRIGHT_GREEN;
  if (lower_name == "bright_blue")
    return ThemeColor::BRIGHT_BLUE;
  if (lower_name == "bright_yellow")
    return ThemeColor::BRIGHT_YELLOW;
  if (lower_name == "bright_magenta")
    return ThemeColor::BRIGHT_MAGENTA;
  if (lower_name == "bright_cyan")
    return ThemeColor::BRIGHT_CYAN;

  std::cerr << "Unknown color name: " << color_name << ", using white"
            << std::endl;
  return ThemeColor::WHITE;
}

// Legacy function - kept for backward compatibility
int StyleManager::theme_color_to_ncurses_color(ThemeColor color) const
{
  switch (color)
  {
  case ThemeColor::BLACK:
    return COLOR_BLACK;
  case ThemeColor::DARK_GRAY:
    return COLOR_BLACK; // Will use A_BOLD attribute for brighter black
  case ThemeColor::GRAY:
    return COLOR_WHITE; // Will use A_DIM attribute for dimmed white
  case ThemeColor::LIGHT_GRAY:
    return COLOR_WHITE; // Will use A_DIM + A_BOLD for medium brightness
  case ThemeColor::WHITE:
    return COLOR_WHITE;
  case ThemeColor::RED:
    return COLOR_RED;
  case ThemeColor::GREEN:
    return COLOR_GREEN;
  case ThemeColor::BLUE:
    return COLOR_BLUE;
  case ThemeColor::YELLOW:
    return COLOR_YELLOW;
  case ThemeColor::MAGENTA:
    return COLOR_MAGENTA;
  case ThemeColor::CYAN:
    return COLOR_CYAN;
  case ThemeColor::BRIGHT_RED:
    return COLOR_RED; // Will use A_BOLD attribute
  case ThemeColor::BRIGHT_GREEN:
    return COLOR_GREEN; // Will use A_BOLD attribute
  case ThemeColor::BRIGHT_BLUE:
    return COLOR_BLUE; // Will use A_BOLD attribute
  case ThemeColor::BRIGHT_YELLOW:
    return COLOR_YELLOW; // Will use A_BOLD attribute
  case ThemeColor::BRIGHT_MAGENTA:
    return COLOR_MAGENTA; // Will use A_BOLD attribute
  case ThemeColor::BRIGHT_CYAN:
    return COLOR_CYAN; // Will use A_BOLD attribute
  case ThemeColor::TERMINAL:
    return COLOR_RED;
  default:
    return COLOR_WHITE;
  }
}

// Legacy function - simplified since 256-color provides enough fidelity
int StyleManager::theme_color_to_ncurses_attr(ThemeColor color) const
{
  // With 256-color support, we can rely more on actual colors than attributes
  // Keep only essential attributes
  switch (color)
  {
  case ThemeColor::BRIGHT_RED:
  case ThemeColor::BRIGHT_GREEN:
  case ThemeColor::BRIGHT_BLUE:
  case ThemeColor::BRIGHT_YELLOW:
  case ThemeColor::BRIGHT_MAGENTA:
  case ThemeColor::BRIGHT_CYAN:
    return A_BOLD; // Keep bold for legacy compatibility
  default:
    return A_NORMAL;
  }
}

bool StyleManager::is_light_theme() const
{
  // Check if background is a light hex color or legacy light theme
  if (current_theme.background[0] == '#')
  {
    RGB bg_rgb = parse_hex_color(current_theme.background);
    // Consider it light if the average RGB value is > 128
    return (bg_rgb.r + bg_rgb.g + bg_rgb.b) / 3 > 128;
  }

  // Legacy check for named colors
  ThemeColor legacy_bg = string_to_theme_color(current_theme.background);
  return (legacy_bg == ThemeColor::WHITE ||
          legacy_bg == ThemeColor::LIGHT_GRAY);
}

void StyleManager::load_default_theme()
{
  current_theme = {
      "Default Dark (Hex)",
      "#000000", // background
      "#FFFFFF", // foreground
      "#FFFFFF", // cursor
      "#0000FF", // selection
      "#333333", // line_highlight
      "#FFFF00", // line_numbers
      "#FFFF99", // line_numbers_active
      "#000080", // status_bar_bg
      "#FFFFFF", // status_bar_fg
      "#00FFFF", // status_bar_active

      // Semantic categories
      "#569CD6", // keyword
      "#CE9178", // string_literal
      "#B5CEA8", // number
      "#6A9955", // comment
      "#DCDCAA", // function_name
      "#9CDCFE", // variable
      "#4EC9B0", // type
      "#D4D4D4", // operator
      "#D4D4D4", // punctuation
      "#4FC1FF", // constant
      "#4EC9B0", // namespace
      "#9CDCFE", // property
      "#DCDCAA", // decorator
      "#C586C0", // macro
      "#569CD6", // label

      // Markup
      "#569CD6", // markup_heading
      "#D4D4D4", // markup_bold
      "#CE9178", // markup_italic
      "#CE9178", // markup_code
      "#CE9178", // markup_code_block
      "#3794FF", // markup_link
      "#3794FF", // markup_url
      "#6A9955", // markup_list
      "#6A9955", // markup_blockquote
      "#FF6B6B", // markup_strikethrough
      "#6A9955"  // markup_quote
  };
}

void StyleManager::apply_theme()
{
  if (!initialized)
  {
    // std::cerr << "StyleManager not initialized, cannot apply theme"
    //           << std::endl;
    return;
  }

  // std::cerr << "Applying theme: " << current_theme.name << std::endl;

  short terminal_bg = resolve_theme_color(current_theme.background);
  short terminal_fg = resolve_theme_color(current_theme.foreground);

  init_pair(0, terminal_fg, terminal_bg);
  const int BACKGROUND_PAIR_ID = 100;
  init_pair(BACKGROUND_PAIR_ID, terminal_fg, terminal_bg);

  auto init_pair_enhanced = [&](int pair_id, const std::string &fg_color_str,
                                const std::string &bg_color_str) -> bool
  {
    if (pair_id >= COLOR_PAIRS)
      return false;
    short fg = resolve_theme_color(fg_color_str);
    short bg = resolve_theme_color(bg_color_str);
    int result = init_pair(pair_id, fg, bg);
    return (result == OK);
  };

  // UI Elements
  init_pair_enhanced(2, current_theme.line_numbers, current_theme.background);
  init_pair_enhanced(3, current_theme.line_numbers_active,
                     current_theme.background);
  init_pair_enhanced(4, "#808080", current_theme.background);

  // Status bar
  init_pair_enhanced(5, current_theme.status_bar_fg,
                     current_theme.status_bar_bg);
  init_pair_enhanced(6, current_theme.status_bar_fg,
                     current_theme.status_bar_bg);
  init_pair_enhanced(7, current_theme.status_bar_active,
                     current_theme.status_bar_bg);
  init_pair_enhanced(8, "#00FFFF", current_theme.status_bar_bg);
  init_pair_enhanced(9, "#FFFF00", current_theme.status_bar_bg);
  init_pair_enhanced(10, "#00FF00", current_theme.status_bar_bg);
  init_pair_enhanced(11, "#FF00FF", current_theme.status_bar_bg);
  init_pair_enhanced(12, "#808080", current_theme.status_bar_bg);

  // Selection and cursor
  init_pair_enhanced(13, current_theme.cursor, current_theme.background);
  init_pair_enhanced(14, current_theme.foreground, current_theme.selection);
  init_pair_enhanced(15, current_theme.foreground,
                     current_theme.line_highlight);

  // Semantic categories (20-39)
  init_pair_enhanced(20, current_theme.keyword, current_theme.background);
  init_pair_enhanced(21, current_theme.string_literal,
                     current_theme.background);
  init_pair_enhanced(22, current_theme.number, current_theme.background);
  init_pair_enhanced(23, current_theme.comment, current_theme.background);
  init_pair_enhanced(24, current_theme.function_name, current_theme.background);
  init_pair_enhanced(25, current_theme.variable, current_theme.background);
  init_pair_enhanced(26, current_theme.type, current_theme.background);
  init_pair_enhanced(27, current_theme.operator_color,
                     current_theme.background);
  init_pair_enhanced(28, current_theme.punctuation, current_theme.background);
  init_pair_enhanced(29, current_theme.constant, current_theme.background);
  init_pair_enhanced(30, current_theme.namespace_color,
                     current_theme.background);
  init_pair_enhanced(31, current_theme.property, current_theme.background);
  init_pair_enhanced(32, current_theme.decorator, current_theme.background);
  init_pair_enhanced(33, current_theme.macro, current_theme.background);
  init_pair_enhanced(34, current_theme.label, current_theme.background);

  // Markup (50-61)
  init_pair_enhanced(50, current_theme.markup_heading,
                     current_theme.background);
  init_pair_enhanced(51, current_theme.markup_bold, current_theme.background);
  init_pair_enhanced(52, current_theme.markup_italic, current_theme.background);
  init_pair_enhanced(53, current_theme.markup_code, current_theme.background);
  init_pair_enhanced(54, current_theme.markup_code_block,
                     current_theme.background);
  init_pair_enhanced(55, current_theme.markup_link, current_theme.background);
  init_pair_enhanced(56, current_theme.markup_url, current_theme.background);
  init_pair_enhanced(57, current_theme.markup_blockquote,
                     current_theme.background);
  init_pair_enhanced(58, current_theme.markup_list, current_theme.background);
  init_pair_enhanced(59, current_theme.operator_color,
                     current_theme.background);
  init_pair_enhanced(60, current_theme.markup_strikethrough,
                     current_theme.background);
  init_pair_enhanced(61, current_theme.markup_quote, current_theme.background);

  // Special pairs
  init_pair_enhanced(70, current_theme.foreground,
                     current_theme.line_highlight);

  bkgdset(' ' | COLOR_PAIR(BACKGROUND_PAIR_ID));
  clear();
  // refresh();
}

// Also add this helper function to detect and optimize for WSL
bool StyleManager::is_wsl_environment() const
{
  return getenv("WSL_DISTRO_NAME") != nullptr;
}

// Enhanced color initialization for WSL
void StyleManager::optimize_for_wsl()
{
  if (!is_wsl_environment())
    return;

  std::cerr << "WSL environment detected - applying optimizations" << std::endl;

  // Check Windows Terminal version and capabilities
  const char *wt_session = getenv("WT_SESSION");
  if (wt_session)
  {
    std::cerr << "Windows Terminal detected" << std::endl;

    // Windows Terminal supports true color
    if (supports_true_color())
    {
      std::cerr << "True color support detected" << std::endl;
    }

    // Force TERM to get better colors
    if (!getenv("TERM") || strcmp(getenv("TERM"), "xterm-256color") != 0)
    {
      std::cerr << "Setting TERM=xterm-256color for better WSL colors"
                << std::endl;
      setenv("TERM", "xterm-256color", 1);
    }
  }
}

// Helper function to apply color with attributes (simplified for 256-color)
void StyleManager::apply_color_pair(int pair_id, ThemeColor theme_color) const
{
  int attrs = COLOR_PAIR(pair_id) | theme_color_to_ncurses_attr(theme_color);
  attrset(attrs);
}

// Legacy compatibility functions

// Terminal capability detection
bool StyleManager::supports_256_colors() const { return COLORS >= 256; }

bool StyleManager::supports_true_color() const
{
  const char *colorterm = getenv("COLORTERM");
  return (colorterm && (std::strcmp(colorterm, "truecolor") == 0 ||
                        std::strcmp(colorterm, "24bit") == 0));
}

void StyleManager::apply_legacy_theme(const Theme &theme)
{
  if (initialized)
  {
    apply_theme();
  }
}

// YAML parsing utilities remain the same
std::string StyleManager::trim(const std::string &str)
{
  size_t start = str.find_first_not_of(" \t\n\r");
  if (start == std::string::npos)
    return "";
  size_t end = str.find_last_not_of(" \t\n\r");
  return str.substr(start, end - start + 1);
}

std::string StyleManager::remove_quotes(const std::string &str)
{
  std::string trimmed = trim(str);
  if (trimmed.length() >= 2 &&
      ((trimmed.front() == '"' && trimmed.back() == '"') ||
       (trimmed.front() == '\'' && trimmed.back() == '\'')))
  {
    return trimmed.substr(1, trimmed.length() - 2);
  }
  return trimmed;
}

std::map<std::string, std::string>
StyleManager::parse_yaml(const std::string &yaml_content)
{
  std::map<std::string, std::string> result;
  std::istringstream stream(yaml_content);
  std::string line;

  while (std::getline(stream, line))
  {
    std::string trimmed_line = trim(line);
    if (trimmed_line.empty() || trimmed_line[0] == '#')
      continue;

    size_t colon_pos = line.find(':');
    if (colon_pos == std::string::npos)
      continue;

    std::string key = trim(line.substr(0, colon_pos));
    std::string value = trim(line.substr(colon_pos + 1));
    value = remove_quotes(value);

    if (!key.empty() && !value.empty())
    {
      result[key] = value;
    }
  }
  return result;
}

bool StyleManager::load_theme_from_yaml(const std::string &yaml_content)
{
  try
  {
    auto config = parse_yaml(yaml_content);
    NamedTheme theme;

    auto get_color = [&](const std::string &key,
                         const std::string &default_color) -> std::string
    {
      auto it = config.find(key);
      return (it != config.end()) ? it->second : default_color;
    };

    theme.name = config.count("name") ? config["name"] : "Custom Theme";
    theme.background = get_color("background", "#000000");
    theme.foreground = get_color("foreground", "#FFFFFF");
    theme.cursor = get_color("cursor", "#FFFFFF");
    theme.selection = get_color("selection", "#0000FF");
    theme.line_highlight = get_color("line_highlight", "#333333");
    theme.line_numbers = get_color("line_numbers", "#808080");
    theme.line_numbers_active = get_color("line_numbers_active", "#FFFFFF");
    theme.status_bar_bg = get_color("status_bar_bg", "#000080");
    theme.status_bar_fg = get_color("status_bar_fg", "#FFFFFF");
    theme.status_bar_active = get_color("status_bar_active", "#00FFFF");

    // Semantic categories
    theme.keyword = get_color("keyword", "#569CD6");
    theme.string_literal = get_color("string_literal", "#CE9178");
    theme.number = get_color("number", "#B5CEA8");
    theme.comment = get_color("comment", "#6A9955");
    theme.function_name = get_color("function_name", "#DCDCAA");
    theme.variable = get_color("variable", "#9CDCFE");
    theme.type = get_color("type", "#4EC9B0");
    theme.operator_color = get_color("operator", "#D4D4D4");
    theme.punctuation = get_color("punctuation", "#D4D4D4");
    theme.constant = get_color("constant", "#4FC1FF");
    theme.namespace_color = get_color("namespace", "#4EC9B0");
    theme.property = get_color("property", "#9CDCFE");
    theme.decorator = get_color("decorator", "#DCDCAA");
    theme.macro = get_color("macro", "#C586C0");
    theme.label = get_color("label", "#569CD6");

    // Markup
    theme.markup_heading = get_color("markup_heading", "#569CD6");
    theme.markup_bold = get_color("markup_bold", "#D4D4D4");
    theme.markup_italic = get_color("markup_italic", "#CE9178");
    theme.markup_code = get_color("markup_code", "#CE9178");
    theme.markup_code_block = get_color("markup_code_block", "#CE9178");
    theme.markup_link = get_color("markup_link", "#3794FF");
    theme.markup_url = get_color("markup_url", "#3794FF");
    theme.markup_list = get_color("markup_list", "#6A9955");
    theme.markup_blockquote = get_color("markup_blockquote", "#6A9955");
    theme.markup_strikethrough = get_color("markup_strikethrough", "#FF6B6B");
    theme.markup_quote = get_color("markup_quote", "#6A9955");

    current_theme = theme;
    if (initialized)
    {
      apply_theme();
    }
    return true;
  }
  catch (const std::exception &e)
  {
    std::cerr << "Error parsing theme: " << e.what() << std::endl;
    load_default_theme();
    return false;
  }
}

bool StyleManager::load_theme_from_file(const std::string &file_path)
{
  try
  {
    std::ifstream file(file_path);
    if (!file.is_open())
    {
      std::cerr << "Failed to open theme file: " << file_path << std::endl;
      load_default_theme();
      return false;
    }

    std::stringstream buffer;
    buffer << file.rdbuf();
    file.close();

    return load_theme_from_yaml(buffer.str());
  }
  catch (const std::exception &e)
  {
    std::cerr << "Error reading theme file " << file_path << ": " << e.what()
              << std::endl;
    load_default_theme();
    return false;
  }
}

// Global instance
StyleManager g_style_manager;

// Legacy API functions for backward compatibility
void init_colors() { g_style_manager.initialize(); }

void load_default_theme()
{
  if (!g_style_manager.is_initialized())
  {
    g_style_manager.initialize();
  }
}

void apply_theme(const Theme &theme)
{
  g_style_manager.apply_legacy_theme(theme);
}

```
Page 2/8FirstPrevNextLast