// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)

#undef MARKUP_LAYER_COLOR
#define MARKUP_LAYER_COLOR 0.10f, 0.20f, 0.25f

////////////////////////////////
//~ rjf: Generated Code

#include "generated/raddbg.meta.c"

////////////////////////////////
//~ rjf: Handles

internal RD_Handle
rd_handle_zero(void)
{
  RD_Handle result = {0};
  return result;
}

internal B32
rd_handle_match(RD_Handle a, RD_Handle b)
{
  return (a.u64[0] == b.u64[0] && a.u64[1] == b.u64[1]);
}

internal void
rd_handle_list_push_node(RD_HandleList *list, RD_HandleNode *node)
{
  DLLPushBack(list->first, list->last, node);
  list->count += 1;
}

internal void
rd_handle_list_push(Arena *arena, RD_HandleList *list, RD_Handle handle)
{
  RD_HandleNode *n = push_array(arena, RD_HandleNode, 1);
  n->handle = handle;
  rd_handle_list_push_node(list, n);
}

internal RD_HandleList
rd_handle_list_copy(Arena *arena, RD_HandleList list)
{
  RD_HandleList result = {0};
  for(RD_HandleNode *n = list.first; n != 0; n = n->next)
  {
    rd_handle_list_push(arena, &result, n->handle);
  }
  return result;
}

////////////////////////////////
//~ rjf: Config Type Functions

internal void
rd_cfg_table_push_unparsed_string(Arena *arena, RD_CfgTable *table, String8 string, RD_CfgSrc source)
{
  if(table->slot_count == 0)
  {
    table->slot_count = 64;
    table->slots = push_array(arena, RD_CfgSlot, table->slot_count);
  }
  MD_TokenizeResult tokenize = md_tokenize_from_text(arena, string);
  MD_ParseResult parse = md_parse_from_text_tokens(arena, str8_lit(""), string, tokenize.tokens);
  for MD_EachNode(tln, parse.root->first) if(tln->string.size != 0)
  {
    // rjf: map string -> hash*slot
    String8 string = str8(tln->string.str, tln->string.size);
    U64 hash = d_hash_from_string__case_insensitive(string);
    U64 slot_idx = hash % table->slot_count;
    RD_CfgSlot *slot = &table->slots[slot_idx];
    
    // rjf: find existing value for this string
    RD_CfgVal *val = 0;
    for(RD_CfgVal *v = slot->first; v != 0; v = v->hash_next)
    {
      if(str8_match(v->string, string, StringMatchFlag_CaseInsensitive))
      {
        val = v;
        break;
      }
    }
    
    // rjf: create new value if needed
    if(val == 0)
    {
      val = push_array(arena, RD_CfgVal, 1);
      val->string = push_str8_copy(arena, string);
      val->insertion_stamp = table->insertion_stamp_counter;
      SLLStackPush_N(slot->first, val, hash_next);
      SLLQueuePush_N(table->first_val, table->last_val, val, linear_next);
      table->insertion_stamp_counter += 1;
    }
    
    // rjf: create new node within this value
    RD_CfgTree *tree = push_array(arena, RD_CfgTree, 1);
    SLLQueuePush_NZ(&d_nil_cfg_tree, val->first, val->last, tree, next);
    tree->source = source;
    tree->root   = md_tree_copy(arena, tln);
  }
}

internal RD_CfgVal *
rd_cfg_val_from_string(RD_CfgTable *table, String8 string)
{
  RD_CfgVal *result = &d_nil_cfg_val;
  if(table->slot_count != 0)
  {
    U64 hash = d_hash_from_string__case_insensitive(string);
    U64 slot_idx = hash % table->slot_count;
    RD_CfgSlot *slot = &table->slots[slot_idx];
    for(RD_CfgVal *val = slot->first; val != 0; val = val->hash_next)
    {
      if(str8_match(val->string, string, StringMatchFlag_CaseInsensitive))
      {
        result = val;
        break;
      }
    }
  }
  return result;
}

////////////////////////////////
//~ rjf: Registers Type Functions

internal void
rd_regs_copy_contents(Arena *arena, RD_Regs *dst, RD_Regs *src)
{
  MemoryCopyStruct(dst, src);
  dst->entity_list = rd_handle_list_copy(arena, src->entity_list);
  dst->cfg_list    = rd_handle_list_copy(arena, src->cfg_list);
  dst->file_path   = push_str8_copy(arena, src->file_path);
  dst->lines       = d_line_list_copy(arena, &src->lines);
  dst->dbgi_key    = di_key_copy(arena, &src->dbgi_key);
  dst->string      = push_str8_copy(arena, src->string);
  dst->cmd_name    = push_str8_copy(arena, src->cmd_name);
  dst->params_tree = md_tree_copy(arena, src->params_tree);
  if(dst->entity_list.count == 0 && !rd_handle_match(rd_handle_zero(), dst->entity))
  {
    rd_handle_list_push(arena, &dst->entity_list, dst->entity);
  }
}

internal RD_Regs *
rd_regs_copy(Arena *arena, RD_Regs *src)
{
  RD_Regs *dst = push_array(arena, RD_Regs, 1);
  rd_regs_copy_contents(arena, dst, src);
  return dst;
}

////////////////////////////////
//~ rjf: Commands Type Functions

internal void
rd_cmd_list_push_new(Arena *arena, RD_CmdList *cmds, String8 name, RD_Regs *regs)
{
  RD_CmdNode *n = push_array(arena, RD_CmdNode, 1);
  n->cmd.name = push_str8_copy(arena, name);
  n->cmd.regs = rd_regs_copy(arena, regs);
  DLLPushBack(cmds->first, cmds->last, n);
  cmds->count += 1;
}

////////////////////////////////
//~ rjf: Entity Functions

//- rjf: nil

internal B32
rd_entity_is_nil(RD_Entity *entity)
{
  return (entity == 0 || entity == &rd_nil_entity);
}

//- rjf: handle <-> entity conversions

internal U64
rd_index_from_entity(RD_Entity *entity)
{
  return (U64)(entity - rd_state->entities_base);
}

internal RD_Handle
rd_handle_from_entity(RD_Entity *entity)
{
  RD_Handle handle = rd_handle_zero();
  if(!rd_entity_is_nil(entity))
  {
    handle.u64[0] = rd_index_from_entity(entity);
    handle.u64[1] = entity->gen;
  }
  return handle;
}

internal RD_Entity *
rd_entity_from_handle(RD_Handle handle)
{
  RD_Entity *result = rd_state->entities_base + handle.u64[0];
  if(handle.u64[0] >= rd_state->entities_count || result->gen != handle.u64[1])
  {
    result = &rd_nil_entity;
  }
  return result;
}

//- rjf: entity recursion iterators

internal RD_EntityRec
rd_entity_rec_depth_first(RD_Entity *entity, RD_Entity *subtree_root, U64 sib_off, U64 child_off)
{
  RD_EntityRec result = {0};
  if(!rd_entity_is_nil(*MemberFromOffset(RD_Entity **, entity, child_off)))
  {
    result.next = *MemberFromOffset(RD_Entity **, entity, child_off);
    result.push_count = 1;
  }
  else for(RD_Entity *parent = entity; parent != subtree_root && !rd_entity_is_nil(parent); parent = parent->parent)
  {
    if(parent != subtree_root && !rd_entity_is_nil(*MemberFromOffset(RD_Entity **, parent, sib_off)))
    {
      result.next = *MemberFromOffset(RD_Entity **, parent, sib_off);
      break;
    }
    result.pop_count += 1;
  }
  return result;
}

//- rjf: ancestor/child introspection

internal RD_Entity *
rd_entity_child_from_kind(RD_Entity *entity, RD_EntityKind kind)
{
  RD_Entity *result = &rd_nil_entity;
  for(RD_Entity *child = entity->first; !rd_entity_is_nil(child); child = child->next)
  {
    if(!(child->flags & RD_EntityFlag_MarkedForDeletion) && child->kind == kind)
    {
      result = child;
      break;
    }
  }
  return result;
}

//- rjf: entity list building

internal void
rd_entity_list_push(Arena *arena, RD_EntityList *list, RD_Entity *entity)
{
  RD_EntityNode *n = push_array(arena, RD_EntityNode, 1);
  n->entity = entity;
  SLLQueuePush(list->first, list->last, n);
  list->count += 1;
}

internal RD_EntityArray
rd_entity_array_from_list(Arena *arena, RD_EntityList *list)
{
  RD_EntityArray result = {0};
  result.count = list->count;
  result.v = push_array(arena, RD_Entity *, result.count);
  U64 idx = 0;
  for(RD_EntityNode *n = list->first; n != 0; n = n->next, idx += 1)
  {
    result.v[idx] = n->entity;
  }
  return result;
}

//- rjf: entity -> color operations

internal Vec4F32
rd_hsva_from_entity(RD_Entity *entity)
{
  Vec4F32 result = {0};
  if(entity->flags & RD_EntityFlag_HasColor)
  {
    result = entity->color_hsva;
  }
  return result;
}

internal Vec4F32
rd_rgba_from_entity(RD_Entity *entity)
{
  Vec4F32 result = {0};
  if(entity->flags & RD_EntityFlag_HasColor)
  {
    Vec3F32 hsv = v3f32(entity->color_hsva.x, entity->color_hsva.y, entity->color_hsva.z);
    Vec3F32 rgb = rgb_from_hsv(hsv);
    result = v4f32(rgb.x, rgb.y, rgb.z, entity->color_hsva.w);
  }
  else switch(entity->kind)
  {
    default:{}break;
    case RD_EntityKind_Breakpoint:
    {
      result = rd_rgba_from_theme_color(RD_ThemeColor_Breakpoint);
    }break;
  }
  return result;
}

//- rjf: entity -> expansion tree keys

internal EV_Key
rd_ev_key_from_entity(RD_Entity *entity)
{
  EV_Key parent_key = rd_parent_ev_key_from_entity(entity);
  EV_Key key = ev_key_make(ev_hash_from_key(parent_key), (U64)entity);
  return key;
}

internal EV_Key
rd_parent_ev_key_from_entity(RD_Entity *entity)
{
  EV_Key parent_key = ev_key_make(5381, (U64)entity);
  return parent_key;
}

////////////////////////////////
//~ rjf: View Spec Type Functions

internal RD_ViewRuleKind
rd_view_rule_kind_from_string(String8 string)
{
  RD_ViewRuleKind kind = RD_ViewRuleKind_Null;
  for EachEnumVal(RD_ViewRuleKind, k)
  {
    if(str8_match(string, rd_view_rule_kind_info_table[k].string, 0))
    {
      kind = k;
      break;
    }
  }
  return kind;
}

internal RD_ViewRuleInfo *
rd_view_rule_info_from_kind(RD_ViewRuleKind kind)
{
  return &rd_view_rule_kind_info_table[kind];
}

internal RD_ViewRuleInfo *
rd_view_rule_info_from_string(String8 string)
{
  RD_ViewRuleInfo *result = &rd_nil_view_rule_info;
  {
    RD_ViewRuleKind kind = rd_view_rule_kind_from_string(string);
    if(kind != RD_ViewRuleKind_Null)
    {
      result = &rd_view_rule_kind_info_table[kind];
    }
  }
  return result;
}

////////////////////////////////
//~ rjf: Global Cross-Window UI Interaction State Functions

internal B32
rd_drag_is_active(void)
{
  return ((rd_state->drag_drop_state == RD_DragDropState_Dragging) ||
          (rd_state->drag_drop_state == RD_DragDropState_Dropping));
}

internal void
rd_drag_begin(RD_RegSlot slot)
{
  if(!rd_drag_is_active())
  {
    arena_clear(rd_state->drag_drop_arena);
    rd_state->drag_drop_regs = rd_regs_copy(rd_state->drag_drop_arena, rd_regs());
    rd_state->drag_drop_regs_slot = slot;
    rd_state->drag_drop_state = RD_DragDropState_Dragging;
  }
}

internal B32
rd_drag_drop(void)
{
  B32 result = 0;
  if(rd_state->drag_drop_state == RD_DragDropState_Dropping)
  {
    result = 1;
    rd_state->drag_drop_state = RD_DragDropState_Null;
  }
  return result;
}

internal void
rd_drag_kill(void)
{
  rd_state->drag_drop_state = RD_DragDropState_Null;
}

internal void
rd_set_hover_regs(RD_RegSlot slot)
{
  rd_state->next_hover_regs = rd_regs_copy(rd_frame_arena(), rd_regs());
  rd_state->next_hover_regs_slot = slot;
}

internal RD_Regs *
rd_get_hover_regs(void)
{
  return rd_state->hover_regs;
}

internal void
rd_open_ctx_menu(UI_Key anchor_box_key, Vec2F32 anchor_box_off, RD_RegSlot slot)
{
  RD_Cfg *window_cfg = rd_cfg_from_handle(rd_regs()->window);
  RD_WindowState *ws = rd_window_state_from_cfg(window_cfg);
  if(ws != 0)
  {
    ui_ctx_menu_open(rd_state->ctx_menu_key, anchor_box_key, anchor_box_off);
    arena_clear(ws->ctx_menu_arena);
    ws->ctx_menu_regs = rd_regs_copy(ws->ctx_menu_arena, rd_regs());
    ws->ctx_menu_regs_slot = slot;
  }
}

////////////////////////////////
//~ rjf: Name Allocation

internal U64
rd_name_bucket_idx_from_string_size(U64 size)
{
  U64 size_rounded = u64_up_to_pow2(size+1);
  size_rounded = ClampBot((1<<4), size_rounded);
  U64 bucket_idx = 0;
  switch(size_rounded)
  {
    case 1<<4: {bucket_idx = 0;}break;
    case 1<<5: {bucket_idx = 1;}break;
    case 1<<6: {bucket_idx = 2;}break;
    case 1<<7: {bucket_idx = 3;}break;
    case 1<<8: {bucket_idx = 4;}break;
    case 1<<9: {bucket_idx = 5;}break;
    case 1<<10:{bucket_idx = 6;}break;
    default:{bucket_idx = ArrayCount(rd_state->free_name_chunks)-1;}break;
  }
  return bucket_idx;
}

internal String8
rd_name_alloc(String8 string)
{
  if(string.size == 0) {return str8_zero();}
  U64 bucket_idx = rd_name_bucket_idx_from_string_size(string.size);
  
  // rjf: loop -> find node, allocate if not there
  //
  // (we do a loop here so that all allocation logic goes through
  // the same path, such that we *always* pull off a free list,
  // rather than just using what was pushed onto an arena directly,
  // which is not undoable; the free lists we control, and are thus
  // trivially undoable)
  //
  RD_NameChunkNode *node = 0;
  for(;node == 0;)
  {
    node = rd_state->free_name_chunks[bucket_idx];
    
    // rjf: pull from bucket free list
    if(node != 0)
    {
      if(bucket_idx == ArrayCount(rd_state->free_name_chunks)-1)
      {
        node = 0;
        RD_NameChunkNode *prev = 0;
        for(RD_NameChunkNode *n = rd_state->free_name_chunks[bucket_idx];
            n != 0;
            prev = n, n = n->next)
        {
          if(n->size >= string.size)
          {
            if(prev == 0)
            {
              rd_state->free_name_chunks[bucket_idx] = n->next;
            }
            else
            {
              prev->next = n->next;
            }
            node = n;
            break;
          }
        }
      }
      else
      {
        SLLStackPop(rd_state->free_name_chunks[bucket_idx]);
      }
    }
    
    // rjf: no found node -> allocate new, push onto associated free list
    if(node == 0)
    {
      U64 chunk_size = 0;
      if(bucket_idx < ArrayCount(rd_state->free_name_chunks)-1)
      {
        chunk_size = 1<<(bucket_idx+4);
      }
      else
      {
        chunk_size = u64_up_to_pow2(string.size);
      }
      U8 *chunk_memory = push_array(rd_state->arena, U8, chunk_size);
      RD_NameChunkNode *chunk = (RD_NameChunkNode *)chunk_memory;
      chunk->size = chunk_size;
      SLLStackPush(rd_state->free_name_chunks[bucket_idx], chunk);
    }
  }
  
  // rjf: fill string & return
  String8 allocated_string = str8((U8 *)node, string.size);
  MemoryCopy((U8 *)node, string.str, string.size);
  return allocated_string;
}

internal void
rd_name_release(String8 string)
{
  if(string.size == 0) {return;}
  U64 bucket_idx = rd_name_bucket_idx_from_string_size(string.size);
  RD_NameChunkNode *node = (RD_NameChunkNode *)string.str;
  node->size = u64_up_to_pow2(string.size);
  SLLStackPush(rd_state->free_name_chunks[bucket_idx], node);
}

////////////////////////////////
//~ rjf: New Config/Entity Data Structure Functions

internal RD_Cfg *
rd_cfg_alloc(void)
{
  RD_Cfg *result = rd_state->free_cfg;
  if(result)
  {
    SLLStackPop(rd_state->free_cfg);
  }
  else
  {
    result = push_array_no_zero(rd_state->arena, RD_Cfg, 1);
  }
  U64 old_gen = result->gen;
  MemoryZeroStruct(result);
  result->first = result->last = result->next = result->prev = result->parent = &rd_nil_cfg;
  result->gen = old_gen + 1;
  return result;
}

internal void
rd_cfg_release(RD_Cfg *cfg)
{
  Temp scratch = scratch_begin(0, 0);
  rd_cfg_unhook(cfg->parent, cfg);
  RD_CfgList nodes = {0};
  for(RD_Cfg *c = cfg; c != &rd_nil_cfg; c = rd_cfg_rec__depth_first(cfg, c).next)
  {
    rd_cfg_list_push(scratch.arena, &nodes, c);
  }
  for(RD_CfgNode *n = nodes.first; n != 0; n = n->next)
  {
    RD_Cfg *c = n->v;
    c->gen += 1;
    rd_name_release(c->string);
    SLLStackPush(rd_state->free_cfg, c);
  }
  scratch_end(scratch);
}

internal void
rd_cfg_release_all_children(RD_Cfg *cfg)
{
  for(RD_Cfg *child = cfg->first, *next = &rd_nil_cfg; child != &rd_nil_cfg; child = next)
  {
    next = child->next;
    rd_cfg_release(child);
  }
}

internal RD_Handle
rd_handle_from_cfg(RD_Cfg *cfg)
{
  RD_Handle handle = {0};
  if(cfg != &rd_nil_cfg)
  {
    handle.u64[0] = (U64)cfg;
    handle.u64[1] = cfg->gen;
  }
  return handle;
}

internal RD_Cfg *
rd_cfg_from_handle(RD_Handle handle)
{
  RD_Cfg *cfg = (RD_Cfg *)handle.u64[0];
  if(cfg == 0 || handle.u64[1] != cfg->gen)
  {
    cfg = &rd_nil_cfg;
  }
  return cfg;
}

internal RD_Cfg *
rd_cfg_new(RD_Cfg *parent, String8 string)
{
  RD_Cfg *cfg = rd_cfg_alloc();
  rd_cfg_insert_child(parent, parent->last, cfg);
  rd_cfg_equip_string(cfg, string);
  return cfg;
}

internal RD_Cfg *
rd_cfg_newf(RD_Cfg *parent, char *fmt, ...)
{
  Temp scratch = scratch_begin(0, 0);
  va_list args;
  va_start(args, fmt);
  String8 string = push_str8fv(scratch.arena, fmt, args);
  RD_Cfg *result = rd_cfg_new(parent, string);
  va_end(args);
  scratch_end(scratch);
  return result;
}

internal RD_Cfg *
rd_cfg_deep_copy(RD_Cfg *src_root)
{
  RD_CfgRec rec = {0};
  RD_Cfg *dst_root = &rd_nil_cfg;
  RD_Cfg *dst_parent = &rd_nil_cfg;
  for(RD_Cfg *src = src_root; src != &rd_nil_cfg; src = rec.next)
  {
    RD_Cfg *dst = rd_cfg_new(dst_parent, src->string);
    if(dst_root == &rd_nil_cfg)
    {
      dst_root = dst;
    }
    rec = rd_cfg_rec__depth_first(src_root, src);
    if(rec.push_count > 0)
    {
      dst_parent = dst;
    }
    else for(S32 pop_idx = 0; pop_idx < rec.pop_count; pop_idx += 1)
    {
      dst_parent = dst_parent->parent;
    }
  }
  return dst_root;
}

internal void
rd_cfg_equip_string(RD_Cfg *cfg, String8 string)
{
  if(cfg->string.size != 0)
  {
    rd_name_release(cfg->string);
  }
  cfg->string = rd_name_alloc(string);
}

internal void
rd_cfg_equip_stringf(RD_Cfg *cfg, char *fmt, ...)
{
  Temp scratch = scratch_begin(0, 0);
  va_list args;
  va_start(args, fmt);
  String8 string = push_str8fv(scratch.arena, fmt, args);
  rd_cfg_equip_string(cfg, string);
  va_end(args);
  scratch_end(scratch);
}

internal void
rd_cfg_insert_child(RD_Cfg *parent, RD_Cfg *prev_child, RD_Cfg *new_child)
{
  if(parent != &rd_nil_cfg)
  {
    DLLInsert_NPZ(&rd_nil_cfg, parent->first, parent->last, prev_child, new_child, next, prev);
    new_child->parent = parent;
  }
}

internal void
rd_cfg_unhook(RD_Cfg *parent, RD_Cfg *child)
{
  if(child != &rd_nil_cfg && parent == child->parent && parent != &rd_nil_cfg)
  {
    DLLRemove_NPZ(&rd_nil_cfg, parent->first, parent->last, child, next, prev);
    child->parent = &rd_nil_cfg;
  }
}

internal RD_Cfg *
rd_cfg_child_from_string(RD_Cfg *parent, String8 string)
{
  RD_Cfg *child = &rd_nil_cfg;
  for(RD_Cfg *c = parent->first; c != &rd_nil_cfg; c = c->next)
  {
    if(str8_match(c->string, string, 0))
    {
      child = c;
      break;
    }
  }
  return child;
}

internal RD_Cfg *
rd_cfg_child_from_string_or_alloc(RD_Cfg *parent, String8 string)
{
  RD_Cfg *child = rd_cfg_child_from_string(parent, string);
  if(child == &rd_nil_cfg)
  {
    child = rd_cfg_new(parent, string);
  }
  return child;
}

internal RD_CfgList
rd_cfg_child_list_from_string(Arena *arena, RD_Cfg *parent, String8 string)
{
  RD_CfgList result = {0};
  for(RD_Cfg *child = parent->first; child != &rd_nil_cfg; child = child->next)
  {
    if(str8_match(child->string, string, 0))
    {
      rd_cfg_list_push(arena, &result, child);
    }
  }
  return result;
}

internal RD_CfgList
rd_cfg_top_level_list_from_string(Arena *arena, String8 string)
{
  RD_CfgList result = {0};
  for(RD_Cfg *bucket = rd_state->root_cfg->first; bucket != &rd_nil_cfg; bucket = bucket->next)
  {
    for(RD_Cfg *tln = bucket->first; tln != &rd_nil_cfg; tln = tln->next)
    {
      if(str8_match(tln->string, string, 0))
      {
        rd_cfg_list_push(arena, &result, tln);
      }
    }
  }
  return result;
}

internal RD_CfgArray
rd_cfg_array_from_list(Arena *arena, RD_CfgList *list)
{
  RD_CfgArray array = {0};
  array.count = list->count;
  array.v = push_array_no_zero(arena, RD_Cfg *, array.count);
  U64 idx = 0;
  for(RD_CfgNode *n = list->first; n != 0; n = n->next, idx += 1)
  {
    array.v[idx] = n->v;
  }
  return array;
}

internal RD_CfgList
rd_cfg_tree_list_from_string(Arena *arena, String8 string)
{
  RD_CfgList result = {0};
  Temp scratch = scratch_begin(&arena, 1);
  MD_Node *root = md_tree_from_string(scratch.arena, string);
  for MD_EachNode(tln, root->first)
  {
    RD_Cfg *dst_root_n = &rd_nil_cfg;
    RD_Cfg *dst_active_parent_n = &rd_nil_cfg;
    MD_NodeRec rec = {0};
    for(MD_Node *src_n = tln; !md_node_is_nil(src_n); src_n = rec.next)
    {
      RD_Cfg *dst_n = rd_cfg_alloc();
      String8 src_n_string = src_n->string;
      String8 src_n_string__raw = raw_from_escaped_str8(scratch.arena, src_n_string);
      rd_cfg_equip_string(dst_n, src_n_string__raw);
      if(dst_active_parent_n != &rd_nil_cfg)
      {
        rd_cfg_insert_child(dst_active_parent_n, dst_active_parent_n->last, dst_n);
      }
      rec = md_node_rec_depth_first_pre(src_n, tln);
      if(rec.push_count > 0)
      {
        if(dst_active_parent_n == &rd_nil_cfg)
        {
          dst_root_n = dst_n;
        }
        dst_active_parent_n = dst_n;
      }
      else for(S32 pop_idx = 0; pop_idx < rec.pop_count; pop_idx += 1)
      {
        dst_active_parent_n = dst_active_parent_n->parent;
      }
    }
    rd_cfg_list_push(arena, &result, dst_root_n);
  }
  scratch_end(scratch);
  return result;
}

internal String8
rd_string_from_cfg_tree(Arena *arena, RD_Cfg *cfg)
{
  Temp scratch = scratch_begin(&arena, 1);
  String8List strings = {0};
  {
    typedef struct NestTask NestTask;
    struct NestTask
    {
      NestTask *next;
      RD_Cfg *cfg;
      B32 is_simple;
    };
    NestTask *top_nest_task = 0;
    RD_CfgRec rec = {0};
    for(RD_Cfg *c = cfg; c != &rd_nil_cfg; c = rec.next)
    {
      // rjf: push name of this node
      if(c->string.size != 0 || c->first == &rd_nil_cfg)
      {
        String8List c_name_strings = {0};
        B32 name_can_be_pushed_standalone = 0;
        {
          Temp temp = temp_begin(scratch.arena);
          MD_TokenizeResult c_name_tokenize = md_tokenize_from_text(temp.arena, c->string);
          name_can_be_pushed_standalone = (c_name_tokenize.tokens.count == 1 && c_name_tokenize.tokens.v[0].flags & (MD_TokenFlag_Identifier|
                                                                                                                     MD_TokenFlag_Numeric|
                                                                                                                     MD_TokenFlag_StringLiteral|
                                                                                                                     MD_TokenFlag_Symbol));
          temp_end(temp);
        }
        if(name_can_be_pushed_standalone)
        {
          str8_list_push(scratch.arena, &c_name_strings, c->string);
        }
        else
        {
          String8 c_name_escaped = escaped_from_raw_str8(scratch.arena, c->string);
          str8_list_push(scratch.arena, &c_name_strings, str8_lit("\""));
          str8_list_push(scratch.arena, &c_name_strings, c_name_escaped);
          str8_list_push(scratch.arena, &c_name_strings, str8_lit("\""));
        }
        if(top_nest_task != 0 && top_nest_task->is_simple)
        {
          str8_list_push(scratch.arena, &strings, str8_lit(" "));
        }
        str8_list_concat_in_place(&strings, &c_name_strings);
      }
      
      // rjf: grab next recursion
      rec = rd_cfg_rec__depth_first(cfg, c);
      
      // rjf: determine if this node is simple, and can be encoded on a single line -
      // if so, push a new nesting task onto the stack
      if(c->first != &rd_nil_cfg)
      {
        B32 is_simple_children_list = 1;
        for(RD_Cfg *child = c->first; child != &rd_nil_cfg; child = child->next)
        {
          if(child->first != &rd_nil_cfg && child != c->last)
          {
            is_simple_children_list = 0;
            break;
          }
        }
        NestTask *task = push_array(scratch.arena, NestTask, 1);
        task->cfg = c;
        task->is_simple = is_simple_children_list;
        SLLStackPush(top_nest_task, task);
      }
      
      // rjf: tree navigations -> encode hierarchy
      if(rec.push_count > 0)
      {
        if(top_nest_task->is_simple && c->string.size != 0)
        {
          str8_list_push(scratch.arena, &strings, str8_lit(":"));
        }
        else
        {
          if(c->string.size != 0)
          {
            str8_list_push(scratch.arena, &strings, str8_lit(":\n"));
          }
          str8_list_push(scratch.arena, &strings, str8_lit("{"));
        }
      }
      else
      {
        for(S32 pop_idx = 0; pop_idx < rec.pop_count; pop_idx += 1, SLLStackPop(top_nest_task))
        {
          if(top_nest_task->is_simple)
          {
            if(top_nest_task->cfg->string.size == 0)
            {
              str8_list_push(scratch.arena, &strings, str8_lit(" }"));
            }
          }
          else
          {
            str8_list_push(scratch.arena, &strings, str8_lit("\n}"));
          }
        }
      }
      if(!top_nest_task || top_nest_task->is_simple == 0)
      {
        str8_list_push(scratch.arena, &strings, str8_lit("\n"));
      }
    }
  }
  String8 result_unindented = str8_list_join(scratch.arena, &strings, 0);
  String8 result = indented_from_string(arena, result_unindented);
  scratch_end(scratch);
  return result;
}

internal RD_CfgRec
rd_cfg_rec__depth_first(RD_Cfg *root, RD_Cfg *cfg)
{
  RD_CfgRec rec = {&rd_nil_cfg};
  if(cfg->first != &rd_nil_cfg)
  {
    rec.next = cfg->first;
    rec.push_count = 1;
  }
  else for(RD_Cfg *p = cfg; p != root; p = p->parent, rec.pop_count += 1)
  {
    if(p->next != &rd_nil_cfg)
    {
      rec.next = p->next;
      break;
    }
  }
  return rec;
}

internal void
rd_cfg_list_push(Arena *arena, RD_CfgList *list, RD_Cfg *cfg)
{
  RD_CfgNode *n = push_array(arena, RD_CfgNode, 1);
  n->v = cfg;
  DLLPushBack(list->first, list->last, n);
  list->count += 1;
}

internal RD_PanelTree
rd_panel_tree_from_cfg(Arena *arena, RD_Cfg *cfg)
{
  Temp scratch = scratch_begin(&arena, 1);
  RD_Cfg *wcfg = rd_window_from_cfg(cfg);
  RD_Cfg *src_root = rd_cfg_child_from_string(wcfg, str8_lit("panels"));
  RD_PanelNode *dst_root = &rd_nil_panel_node;
  RD_PanelNode *dst_focused = &rd_nil_panel_node;
  {
    Axis2 active_split_axis = rd_cfg_child_from_string(wcfg, str8_lit("split_x")) != &rd_nil_cfg ? Axis2_X : Axis2_Y;
    RD_CfgRec rec = {0};
    RD_PanelNode *dst_active_parent = &rd_nil_panel_node;
    for(RD_Cfg *src = src_root; src != &rd_nil_cfg; src = rec.next)
    {
      // rjf: build a panel node
      RD_PanelNode *dst = push_array(arena, RD_PanelNode, 1);
      MemoryCopyStruct(dst, &rd_nil_panel_node);
      dst->parent = dst_active_parent;
      if(dst_active_parent != &rd_nil_panel_node)
      {
        DLLPushBack_NPZ(&rd_nil_panel_node, dst_active_parent->first, dst_active_parent->last, dst, next, prev);
        dst_active_parent->child_count += 1;
      }
      
      // rjf: extract cfg info
      B32 panel_has_tabs = 0;
      dst->cfg = src;
      dst->pct_of_parent = (F32)f64_from_str8(src->string);
      dst->tab_side = (rd_cfg_child_from_string(src, str8_lit("tabs_on_bottom")) != &rd_nil_cfg ? Side_Max : Side_Min);
      dst->split_axis = active_split_axis;
      for(RD_Cfg *src_child = src->first; src_child != &rd_nil_cfg; src_child = src_child->next)
      {
        MD_TokenizeResult tokenize = md_tokenize_from_text(scratch.arena, src_child->string);
        if(tokenize.tokens.count == 1 && tokenize.tokens.v[0].flags & MD_TokenFlag_Numeric)
        {
          // NOTE(rjf): skip - this is a panel.
        }
        else if(str8_match(src_child->string, str8_lit("tabs_on_bottom"), 0))
        {
          // NOTE(rjf): skip - this is a panel option.
        }
        else if(str8_match(src_child->string, str8_lit("selected"), 0))
        {
          dst_focused = dst;
        }
        else if(tokenize.tokens.count == 1 && tokenize.tokens.v[0].flags & MD_TokenFlag_Identifier)
        {
          panel_has_tabs = 1;
          rd_cfg_list_push(arena, &dst->tabs, src_child);
          if(rd_cfg_child_from_string(src_child, str8_lit("selected")) != &rd_nil_cfg)
          {
            dst->selected_tab = src_child;
          }
        }
      }
      
      // rjf: recurse
      rec = rd_cfg_rec__depth_first(src_root, src);
      if(panel_has_tabs)
      {
        MemoryZeroStruct(&rec);
        rec.next = &rd_nil_cfg;
        for(RD_Cfg *p = src; p != src_root && p != &rd_nil_cfg; p = p->parent, rec.pop_count += 1)
        {
          if(p->next != &rd_nil_cfg)
          {
            rec.next = p->next;
            break;
          }
        }
      }
      if(rec.push_count > 0)
      {
        dst_active_parent = dst;
        if(dst_root == &rd_nil_panel_node)
        {
          dst_root = dst;
        }
        active_split_axis = axis2_flip(active_split_axis);
      }
      else for(S32 pop_idx = 0; pop_idx < rec.pop_count; pop_idx += 1)
      {
        dst_active_parent = dst_active_parent->parent;
        active_split_axis = axis2_flip(active_split_axis);
      }
    }
  }
  scratch_end(scratch);
  RD_PanelTree tree = {dst_root, dst_focused};
  return tree;
}

internal RD_PanelNodeRec
rd_panel_node_rec__depth_first(RD_PanelNode *root, RD_PanelNode *panel, U64 sib_off, U64 child_off)
{
  RD_PanelNodeRec rec = {&rd_nil_panel_node};
  if(*MemberFromOffset(RD_PanelNode **, panel, child_off) != &rd_nil_panel_node)
  {
    rec.next = *MemberFromOffset(RD_PanelNode **, panel, child_off);
    rec.push_count += 1;
  }
  else for(RD_PanelNode *p = panel; p != &rd_nil_panel_node && p != root; p = p->parent, rec.pop_count += 1)
  {
    if(*MemberFromOffset(RD_PanelNode **, p, sib_off) != &rd_nil_panel_node)
    {
      rec.next = *MemberFromOffset(RD_PanelNode **, p, sib_off);
      break;
    }
  }
  return rec;
}

internal RD_PanelNode *
rd_panel_node_from_tree_cfg(RD_PanelNode *root, RD_Cfg *cfg)
{
  RD_PanelNode *result = &rd_nil_panel_node;
  for(RD_PanelNode *p = root;
      p != &rd_nil_panel_node;
      p = rd_panel_node_rec__depth_first_pre(root, p).next)
  {
    if(p->cfg == cfg)
    {
      result = p;
      break;
    }
  }
  return result;
}

internal Rng2F32
rd_target_rect_from_panel_node_child(Rng2F32 parent_rect, RD_PanelNode *parent, RD_PanelNode *panel)
{
  Rng2F32 rect = parent_rect;
  if(parent != &rd_nil_panel_node)
  {
    Vec2F32 parent_rect_size = dim_2f32(parent_rect);
    Axis2 axis = parent->split_axis;
    rect.p1.v[axis] = rect.p0.v[axis];
    for(RD_PanelNode *child = parent->first; child != &rd_nil_panel_node; child = child->next)
    {
      rect.p1.v[axis] += parent_rect_size.v[axis] * child->pct_of_parent;
      if(child == panel)
      {
        break;
      }
      rect.p0.v[axis] = rect.p1.v[axis];
    }
    //rect.p0.v[axis] += parent_rect_size.v[axis] * panel->off_pct_of_parent.v[axis];
    //rect.p0.v[axis2_flip(axis)] += parent_rect_size.v[axis2_flip(axis)] * panel->off_pct_of_parent.v[axis2_flip(axis)];
  }
  rect.x0 = round_f32(rect.x0);
  rect.x1 = round_f32(rect.x1);
  rect.y0 = round_f32(rect.y0);
  rect.y1 = round_f32(rect.y1);
  return rect;
}

internal Rng2F32
rd_target_rect_from_panel_node(Rng2F32 root_rect, RD_PanelNode *root, RD_PanelNode *panel)
{
  Temp scratch = scratch_begin(0, 0);
  
  // rjf: count ancestors
  U64 ancestor_count = 0;
  for(RD_PanelNode *p = panel->parent; p != &rd_nil_panel_node; p = p->parent)
  {
    ancestor_count += 1;
  }
  
  // rjf: gather ancestors
  RD_PanelNode **ancestors = push_array(scratch.arena, RD_PanelNode *, ancestor_count);
  {
    U64 ancestor_idx = 0;
    for(RD_PanelNode *p = panel->parent; p != &rd_nil_panel_node; p = p->parent)
    {
      ancestors[ancestor_idx] = p;
      ancestor_idx += 1;
    }
  }
  
  // rjf: go from highest ancestor => panel and calculate rect
  Rng2F32 parent_rect = root_rect;
  for(S64 ancestor_idx = (S64)ancestor_count-1;
      0 <= ancestor_idx && ancestor_idx < ancestor_count;
      ancestor_idx -= 1)
  {
    RD_PanelNode *ancestor = ancestors[ancestor_idx];
    RD_PanelNode *parent = ancestor->parent;
    if(parent != &rd_nil_panel_node)
    {
      parent_rect = rd_target_rect_from_panel_node_child(parent_rect, parent, ancestor);
    }
  }
  
  // rjf: calculate final rect
  Rng2F32 rect = rd_target_rect_from_panel_node_child(parent_rect, panel->parent, panel);
  
  scratch_end(scratch);
  return rect;
}

internal B32
rd_cfg_is_project_filtered(RD_Cfg *cfg)
{
  RD_Cfg *project = rd_cfg_child_from_string(cfg, str8_lit("project"));
  B32 result = path_match_normalized(rd_state->project_path, project->first->string);
  return result;
}

internal RD_KeyMapNodePtrList
rd_key_map_node_ptr_list_from_name(Arena *arena, String8 string)
{
  RD_KeyMapNodePtrList list = {0};
  {
    U64 hash = d_hash_from_string(string);
    U64 slot_idx = hash%rd_state->key_map->name_slots_count;
    for(RD_KeyMapNode *n = rd_state->key_map->name_slots[slot_idx].first; n != 0; n = n->name_hash_next)
    {
      if(str8_match(n->name, string, 0))
      {
        RD_KeyMapNodePtr *ptr = push_array(arena, RD_KeyMapNodePtr, 1);
        ptr->v = n;
        SLLQueuePush(list.first, list.last, ptr);
        list.count += 1;
      }
    }
  }
  return list;
}

internal RD_KeyMapNodePtrList
rd_key_map_node_ptr_list_from_binding(Arena *arena, RD_Binding binding)
{
  RD_KeyMapNodePtrList list = {0};
  {
    U64 hash = d_hash_from_string(str8_struct(&binding));
    U64 slot_idx = hash%rd_state->key_map->binding_slots_count;
    for(RD_KeyMapNode *n = rd_state->key_map->binding_slots[slot_idx].first; n != 0; n = n->binding_hash_next)
    {
      if(MemoryMatchStruct(&binding, &n->binding))
      {
        RD_KeyMapNodePtr *ptr = push_array(arena, RD_KeyMapNodePtr, 1);
        ptr->v = n;
        SLLQueuePush(list.first, list.last, ptr);
        list.count += 1;
      }
    }
  }
  return list;
}

internal Vec4F32
rd_hsva_from_cfg(RD_Cfg *cfg)
{
  Vec4F32 hsva = {0};
  RD_Cfg *hsva_root = rd_cfg_child_from_string(cfg, str8_lit("hsva"));
  RD_Cfg *h = hsva_root->first;
  RD_Cfg *s = h->next;
  RD_Cfg *v = s->next;
  RD_Cfg *a = v->next;
  hsva.x = (F32)f64_from_str8(h->string);
  hsva.y = (F32)f64_from_str8(s->string);
  hsva.z = (F32)f64_from_str8(v->string);
  hsva.w = (F32)f64_from_str8(a->string);
  return hsva;
}

internal Vec4F32
rd_rgba_from_cfg(RD_Cfg *cfg)
{
  Vec4F32 hsva = rd_hsva_from_cfg(cfg);
  Vec4F32 rgba = rgba_from_hsva(hsva);
  return rgba;
}

internal B32
rd_disabled_from_cfg(RD_Cfg *cfg)
{
  B32 is_disabled = (rd_cfg_child_from_string(cfg, str8_lit("disabled")) != &rd_nil_cfg);
  return is_disabled;
}

internal RD_Location
rd_location_from_cfg(RD_Cfg *cfg)
{
  RD_Location dst_loc = {0};
  {
    RD_Cfg *src_loc = rd_cfg_child_from_string(cfg, str8_lit("location"));
    if(src_loc->first != &rd_nil_cfg && src_loc->first->first != &rd_nil_cfg)
    {
      dst_loc.file_path = src_loc->first->string;
      try_s64_from_str8_c_rules(src_loc->first->first->string, &dst_loc.pt.line);
      if(!try_s64_from_str8_c_rules(src_loc->first->first->first->string, &dst_loc.pt.column))
      {
        dst_loc.pt.column = 1;
      }
    }
    else
    {
      Temp scratch = scratch_begin(0, 0);
      MD_TokenizeResult tokenize = md_tokenize_from_text(scratch.arena, src_loc->first->string);
      if(tokenize.tokens.count == 1 && tokenize.tokens.v[0].flags & (MD_TokenFlag_Identifier|MD_TokenFlag_StringLiteral))
      {
        dst_loc.name = src_loc->first->string;
      }
      else if(tokenize.tokens.count == 1 && tokenize.tokens.v[0].flags & MD_TokenFlag_Numeric)
      {
        try_u64_from_str8_c_rules(src_loc->first->string, &dst_loc.vaddr);
      }
      scratch_end(scratch);
    }
  }
  return dst_loc;
}

internal String8
rd_expr_from_cfg(RD_Cfg *cfg)
{
  RD_Cfg *expr_root = rd_cfg_child_from_string(cfg, str8_lit("expression"));
  String8 result = expr_root->first->string;
  return result;
}

internal D_Target
rd_target_from_cfg(Arena *arena, RD_Cfg *cfg)
{
  D_Target target = {0};
  target.exe                        = rd_cfg_child_from_string(cfg, str8_lit("executable"))->first->string;
  target.args                       = rd_cfg_child_from_string(cfg, str8_lit("arguments"))->first->string;
  target.working_directory          = rd_cfg_child_from_string(cfg, str8_lit("working_directory"))->first->string;
  target.custom_entry_point_name    = rd_cfg_child_from_string(cfg, str8_lit("entry_point"))->first->string;
  target.stdout_path                = rd_cfg_child_from_string(cfg, str8_lit("stdout_path"))->first->string;
  target.stderr_path                = rd_cfg_child_from_string(cfg, str8_lit("stderr_path"))->first->string;
  target.stdin_path                 = rd_cfg_child_from_string(cfg, str8_lit("stdin_path"))->first->string;
  target.debug_subprocesses         = (rd_cfg_child_from_string(cfg, str8_lit("debug_subprocesses")) != &rd_nil_cfg);
  RD_Cfg *env_root = rd_cfg_child_from_string(cfg, str8_lit("environment"));
  for(RD_Cfg *env_child = env_root->first; env_child != &rd_nil_cfg; env_child = env_child->next)
  {
    str8_list_push(arena, &target.env, env_child->string);
  }
  return target;
}

internal DR_FancyStringList
rd_title_fstrs_from_cfg(Arena *arena, RD_Cfg *cfg, Vec4F32 secondary_color, F32 size)
{
  DR_FancyStringList result = {0};
  {
    Temp scratch = scratch_begin(&arena, 1);
    
    //- rjf: unpack config
    B32 is_disabled = rd_disabled_from_cfg(cfg);
    RD_Location loc = rd_location_from_cfg(cfg);
    D_Target target = rd_target_from_cfg(scratch.arena, cfg);
    Vec4F32 rgba = rd_rgba_from_cfg(cfg);
    if(rgba.w == 0)
    {
      rgba = ui_top_palette()->text;
    }
    RD_IconKind icon_kind = rd_icon_kind_from_code_name(cfg->string);
    B32 is_from_command_line = 0;
    {
      RD_Cfg *cmd_line_root = rd_cfg_child_from_string(rd_state->root_cfg, str8_lit("command_line"));
      for(RD_Cfg *p = cfg->parent; p != &rd_nil_cfg; p = p->parent)
      {
        if(p == cmd_line_root)
        {
          is_from_command_line = 1;
          break;
        }
      }
    }
    
    //- rjf: set up color/size for all parts of the title
    //
    // the "running" part implies that it changes as things are added - 
    // so if a primary title is pushed, we can make the rest of the title
    // more faded/smaller, but only after a primary title is pushed,
    // which could be caused by many different potential parts of a cfg.
    //
    Vec4F32 secondary_rgba = secondary_color;
    F32 secondary_size = size*0.8f;
    B32 running_is_secondary = 0;
    Vec4F32 running_rgba = rgba;
    F32 running_size = size;
#define start_secondary() if(!running_is_secondary){running_is_secondary = 1; running_rgba = secondary_rgba; running_size = secondary_size;}
    
    //- rjf: push icon
    if(icon_kind != RD_IconKind_Null)
    {
      dr_fancy_string_list_push_new(arena, &result, rd_font_from_slot(RD_FontSlot_Icons), size, secondary_color, rd_icon_kind_text_table[icon_kind]);
      dr_fancy_string_list_push_new(arena, &result, rd_font_from_slot(RD_FontSlot_Code), size, secondary_color, str8_lit(" "));
    }
    
    //- rjf: push warning icon for command-line entities
    if(is_from_command_line)
    {
      dr_fancy_string_list_push_new(arena, &result, rd_font_from_slot(RD_FontSlot_Icons), size, rd_rgba_from_theme_color(RD_ThemeColor_TextNegative), rd_icon_kind_text_table[RD_IconKind_Info]);
      dr_fancy_string_list_push_new(arena, &result, rd_font_from_slot(RD_FontSlot_Code), size, secondary_color, str8_lit(" "));
    }
    
    //- rjf: push label
    {
      String8 label = rd_cfg_child_from_string(cfg, str8_lit("label"))->first->string;
      if(label.size != 0)
      {
        dr_fancy_string_list_push_new(arena, &result, rd_font_from_slot(RD_FontSlot_Code), running_size, running_rgba, label);
        start_secondary();
      }
    }
    
    //- rjf: push expression
    {
      String8 expr = rd_cfg_child_from_string(cfg, str8_lit("expression"))->first->string;
      if(expr.size != 0)
      {
        dr_fancy_string_list_push_new(arena, &result, rd_font_from_slot(RD_FontSlot_Code), running_size, running_rgba, expr);
        start_secondary();
      }
    }
    
    //- rjf: push text location
    if(loc.file_path.size != 0)
    {
      String8 location_string = push_str8f(arena, "%S:%I64d:%I64d", loc.file_path, loc.pt.line, loc.pt.column);
      dr_fancy_string_list_push_new(arena, &result, rd_font_from_slot(RD_FontSlot_Main), running_size, running_rgba, location_string);
      start_secondary();
    }
    
    //- rjf: push target executable name
    if(target.exe.size != 0)
    {
      dr_fancy_string_list_push_new(arena, &result, rd_font_from_slot(RD_FontSlot_Main), running_size, running_rgba, target.exe);
      start_secondary();
    }
    
    //- rjf: push target arguments
    if(target.args.size != 0)
    {
      dr_fancy_string_list_push_new(arena, &result, rd_font_from_slot(RD_FontSlot_Main), secondary_size, secondary_rgba, target.args);
    }
    
    //- rjf: push conditions
    {
      String8 condition = rd_cfg_child_from_string(cfg, str8_lit("condition"))->first->string;
      if(condition.size != 0)
      {
        dr_fancy_string_list_push_new(arena, &result, rd_font_from_slot(RD_FontSlot_Main), secondary_size, secondary_rgba, condition);
      }
    }
    
    //- rjf: push disabled marker
    if(is_disabled)
    {
      dr_fancy_string_list_push_new(arena, &result, rd_font_from_slot(RD_FontSlot_Main), secondary_size, secondary_rgba, str8_lit("(Disabled)"));
    }
    
    //- rjf: push hit count
    {
      String8 hit_count_value_string = rd_cfg_child_from_string(cfg, str8_lit("hit_count"))->first->string;
      U64 hit_count = 0;
      if(try_u64_from_str8_c_rules(hit_count_value_string, &hit_count))
      {
        String8 hit_count_text = push_str8f(arena, "(%I64u hit%s)", hit_count, hit_count == 1 ? "" : "s");
        dr_fancy_string_list_push_new(arena, &result, rd_font_from_slot(RD_FontSlot_Main), secondary_size, secondary_rgba, hit_count_text);
      }
    }
    
    //- rjf: special case: auto view rule
    if(str8_match(cfg->string, str8_lit("auto_view_rule"), 0))
    {
      String8 src_string = rd_cfg_child_from_string(cfg, str8_lit("source"))->first->string;
      String8 dst_string = rd_cfg_child_from_string(cfg, str8_lit("dest"))->first->string;
      Vec4F32 src_color = rgba;
      Vec4F32 dst_color = rgba;
      DR_FancyStringList src_fstrs = {0};
      DR_FancyStringList dst_fstrs = {0};
      if(src_string.size == 0)
      {
        src_string = str8_lit("(type)");
        src_color = secondary_color;
        dr_fancy_string_list_push_new(arena, &src_fstrs, rd_font_from_slot(RD_FontSlot_Main), size, src_color, src_string);
      }
      else RD_Font(RD_FontSlot_Code)
      {
        src_fstrs = rd_fancy_string_list_from_code_string(arena, 1.f, 0, src_color, src_string);
      }
      if(dst_string.size == 0)
      {
        dst_string = str8_lit("(view rule)");
        dst_color = secondary_color;
        dr_fancy_string_list_push_new(arena, &dst_fstrs, rd_font_from_slot(RD_FontSlot_Main), size, dst_color, dst_string);
      }
      else RD_Font(RD_FontSlot_Code)
      {
        dst_fstrs = rd_fancy_string_list_from_code_string(arena, 1.f, 0, dst_color, dst_string);
      }
      dr_fancy_string_list_concat_in_place(&result, &src_fstrs);
      dr_fancy_string_list_push_new(arena, &result, rd_font_from_slot(RD_FontSlot_Code), size, v4f32(0, 0, 0, 0), str8_lit(" "));
      dr_fancy_string_list_push_new(arena, &result, rd_font_from_slot(RD_FontSlot_Icons), size, secondary_color, rd_icon_kind_text_table[RD_IconKind_RightArrow]);
      dr_fancy_string_list_push_new(arena, &result, rd_font_from_slot(RD_FontSlot_Code), size, v4f32(0, 0, 0, 0), str8_lit(" "));
      dr_fancy_string_list_concat_in_place(&result, &dst_fstrs);
    }
    
    //- rjf: special case: file path maps
    if(str8_match(cfg->string, str8_lit("file_path_map"), 0))
    {
      String8 src_string = rd_cfg_child_from_string(cfg, str8_lit("source"))->first->string;
      String8 dst_string = rd_cfg_child_from_string(cfg, str8_lit("dest"))->first->string;
      Vec4F32 src_color = rgba;
      Vec4F32 dst_color = rgba;
      if(src_string.size == 0)
      {
        src_string = str8_lit("(source path)");
        src_color = secondary_color;
      }
      if(dst_string.size == 0)
      {
        dst_string = str8_lit("(destination path)");
        dst_color = secondary_color;
      }
      dr_fancy_string_list_push_new(arena, &result, rd_font_from_slot(RD_FontSlot_Main), size, src_color, src_string);
      dr_fancy_string_list_push_new(arena, &result, rd_font_from_slot(RD_FontSlot_Code), size, v4f32(0, 0, 0, 0), str8_lit(" "));
      dr_fancy_string_list_push_new(arena, &result, rd_font_from_slot(RD_FontSlot_Icons), size, secondary_color, rd_icon_kind_text_table[RD_IconKind_RightArrow]);
      dr_fancy_string_list_push_new(arena, &result, rd_font_from_slot(RD_FontSlot_Code), size, v4f32(0, 0, 0, 0), str8_lit(" "));
      dr_fancy_string_list_push_new(arena, &result, rd_font_from_slot(RD_FontSlot_Main), size, dst_color, dst_string);
    }
    
#undef start_secondary
    scratch_end(scratch);
  }
  return result;
}

internal MD_Node *
rd_schema_from_name(Arena *arena, String8 name)
{
  MD_Node *schema = &md_nil_node;
  for EachElement(idx, rd_name_schema_info_table)
  {
    if(str8_match(name, rd_name_schema_info_table[idx].name, 0))
    {
      schema = rd_state->schemas[idx];
      break;
    }
  }
  return schema;
}

internal String8
rd_setting_from_name(String8 name)
{
  String8 result = {0};
  {
    // rjf: find most-granular config scope to begin looking for the setting
    RD_Cfg *start_cfg = rd_cfg_from_handle(rd_regs()->view);
    if(start_cfg == &rd_nil_cfg) { start_cfg = rd_cfg_from_handle(rd_regs()->panel); }
    if(start_cfg == &rd_nil_cfg) { start_cfg = rd_cfg_from_handle(rd_regs()->window); }
    
    // rjf: scan upwards the config tree until we find the setting
    RD_Cfg *setting = &rd_nil_cfg;
    for(RD_Cfg *cfg = start_cfg; cfg != &rd_nil_cfg && setting == &rd_nil_cfg; cfg = cfg->parent)
    {
      setting = rd_cfg_child_from_string(cfg, name);
    }
    
    // rjf: return resultant child string stored under this key
    result = setting->first->string;
    
    // rjf: no result -> look for default in settings schema
    if(result.size == 0) ProfScope("default setting schema lookup")
    {
      Temp scratch = scratch_begin(0, 0);
      MD_Node *schema = rd_schema_from_name(scratch.arena, str8_lit("settings"));
      MD_Node *setting = md_child_from_string(schema, name, 0);
      MD_Node *default_tag = md_tag_from_string(setting, str8_lit("default"), 0);
      result = default_tag->first->string;
      scratch_end(scratch);
    }
  }
  return result;
}

internal RD_Cfg *
rd_immediate_cfg_from_key(String8 string)
{
  RD_Cfg *transient = rd_cfg_child_from_string(rd_state->root_cfg, str8_lit("transient"));
  RD_Cfg *immediate = &rd_nil_cfg;
  RD_Cfg *cfg = &rd_nil_cfg;
  for(RD_Cfg *child = transient->first; child != &rd_nil_cfg; child = child->next)
  {
    if(str8_match(child->string, str8_lit("immediate"), 0))
    {
      cfg = rd_cfg_child_from_string(child, string);
      if(cfg != &rd_nil_cfg)
      {
        immediate = child;
        break;
      }
    }
  }
  if(cfg == &rd_nil_cfg)
  {
    immediate = rd_cfg_new(transient, str8_lit("immediate"));
    cfg = rd_cfg_new(immediate, string);
  }
  rd_cfg_new(immediate, str8_lit("hot"));
  return cfg;
}

internal RD_Cfg *
rd_immediate_cfg_from_keyf(char *fmt, ...)
{
  Temp scratch = scratch_begin(0, 0);
  va_list args;
  va_start(args, fmt);
  String8 key = push_str8fv(scratch.arena, fmt, args);
  RD_Cfg *result = rd_immediate_cfg_from_key(key);
  va_end(args);
  scratch_end(scratch);
  return result;
}

////////////////////////////////
//~ rjf: Entity State Functions

//- rjf: entity allocation + tree forming

internal RD_Entity *
rd_entity_alloc(RD_Entity *parent, RD_EntityKind kind)
{
  B32 user_defined_lifetime = !!(rd_entity_kind_flags_table[kind] & RD_EntityKindFlag_UserDefinedLifetime);
  U64 free_list_idx = !!user_defined_lifetime;
  if(rd_entity_is_nil(parent)) { parent = rd_state->entities_root; }
  
  // rjf: empty free list -> push new
  if(!rd_state->entities_free[free_list_idx])
  {
    RD_Entity *entity = push_array(rd_state->entities_arena, RD_Entity, 1);
    rd_state->entities_count += 1;
    rd_state->entities_free_count += 1;
    SLLStackPush(rd_state->entities_free[free_list_idx], entity);
  }
  
  // rjf: pop new entity off free-list
  RD_Entity *entity = rd_state->entities_free[free_list_idx];
  SLLStackPop(rd_state->entities_free[free_list_idx]);
  rd_state->entities_free_count -= 1;
  rd_state->entities_active_count += 1;
  
  // rjf: zero entity
  {
    U64 gen = entity->gen;
    MemoryZeroStruct(entity);
    entity->gen = gen;
  }
  
  // rjf: set up alloc'd entity links
  entity->first = entity->last = entity->next = entity->prev = entity->parent = &rd_nil_entity;
  entity->parent = parent;
  
  // rjf: stitch up parent links
  if(rd_entity_is_nil(parent))
  {
    rd_state->entities_root = entity;
  }
  else
  {
    DLLPushBack_NPZ(&rd_nil_entity, parent->first, parent->last, entity, next, prev);
  }
  
  // rjf: fill out metadata
  entity->kind = kind;
  rd_state->entities_id_gen += 1;
  entity->id = rd_state->entities_id_gen;
  entity->gen += 1;
  entity->alloc_time_us = os_now_microseconds();
  
  // rjf: initialize to deleted, record history, then "undelete" if this allocation can be undone
  if(user_defined_lifetime)
  {
    // TODO(rjf)
  }
  
  // rjf: dirtify caches
  rd_state->kind_alloc_gens[kind] += 1;
  
  // rjf: log
  LogInfoNamedBlockF("new_entity")
  {
    log_infof("kind: \"%S\"\n", d_entity_kind_display_string_table[kind]);
    log_infof("id: $0x%I64x\n", entity->id);
  }
  
  return entity;
}

internal void
rd_entity_mark_for_deletion(RD_Entity *entity)
{
  if(!rd_entity_is_nil(entity))
  {
    entity->flags |= RD_EntityFlag_MarkedForDeletion;
    rd_state->kind_alloc_gens[entity->kind] += 1;
  }
}

internal void
rd_entity_release(RD_Entity *entity)
{
  Temp scratch = scratch_begin(0, 0);
  
  // rjf: unpack
  U64 free_list_idx = !!(rd_entity_kind_flags_table[entity->kind] & RD_EntityKindFlag_UserDefinedLifetime);
  
  // rjf: release whole tree
  typedef struct Task Task;
  struct Task
  {
    Task *next;
    RD_Entity *e;
  };
  Task start_task = {0, entity};
  Task *first_task = &start_task;
  Task *last_task = &start_task;
  for(Task *task = first_task; task != 0; task = task->next)
  {
    for(RD_Entity *child = task->e->first; !rd_entity_is_nil(child); child = child->next)
    {
      Task *t = push_array(scratch.arena, Task, 1);
      t->e = child;
      SLLQueuePush(first_task, last_task, t);
    }
    LogInfoNamedBlockF("end_entity")
    {
      log_infof("kind: \"%S\"\n", d_entity_kind_display_string_table[task->e->kind]);
      log_infof("id: $0x%I64x\n", task->e->id);
    }
    SLLStackPush(rd_state->entities_free[free_list_idx], task->e);
    rd_state->entities_free_count += 1;
    rd_state->entities_active_count -= 1;
    task->e->gen += 1;
    if(task->e->string.size != 0)
    {
      rd_name_release(task->e->string);
    }
    rd_state->kind_alloc_gens[task->e->kind] += 1;
  }
  
  scratch_end(scratch);
}

internal void
rd_entity_change_parent(RD_Entity *entity, RD_Entity *old_parent, RD_Entity *new_parent, RD_Entity *prev_child)
{
  Assert(entity->parent == old_parent);
  Assert(prev_child->parent == old_parent || rd_entity_is_nil(prev_child));
  if(prev_child != entity)
  {
    // rjf: fix up links
    if(!rd_entity_is_nil(old_parent))
    {
      DLLRemove_NPZ(&rd_nil_entity, old_parent->first, old_parent->last, entity, next, prev);
    }
    if(!rd_entity_is_nil(new_parent))
    {
      DLLInsert_NPZ(&rd_nil_entity, new_parent->first, new_parent->last, prev_child, entity, next, prev);
    }
    entity->parent = new_parent;
    
    // rjf: notify
    rd_state->kind_alloc_gens[entity->kind] += 1;
  }
}

internal RD_Entity *
rd_entity_child_from_kind_or_alloc(RD_Entity *entity, RD_EntityKind kind)
{
  RD_Entity *child = rd_entity_child_from_kind(entity, kind);
  if(rd_entity_is_nil(child))
  {
    child = rd_entity_alloc(entity, kind);
  }
  return child;
}

//- rjf: entity simple equipment

internal void
rd_entity_equip_txt_pt(RD_Entity *entity, TxtPt point)
{
  rd_require_entity_nonnil(entity, return);
  entity->text_point = point;
  entity->flags |= RD_EntityFlag_HasTextPoint;
}

internal void
rd_entity_equip_disabled(RD_Entity *entity, B32 value)
{
  rd_require_entity_nonnil(entity, return);
  entity->disabled = value;
}

internal void
rd_entity_equip_u64(RD_Entity *entity, U64 u64)
{
  rd_require_entity_nonnil(entity, return);
  entity->u64 = u64;
  entity->flags |= RD_EntityFlag_HasU64;
}

internal void
rd_entity_equip_color_rgba(RD_Entity *entity, Vec4F32 rgba)
{
  rd_require_entity_nonnil(entity, return);
  Vec3F32 rgb = v3f32(rgba.x, rgba.y, rgba.z);
  Vec3F32 hsv = hsv_from_rgb(rgb);
  Vec4F32 hsva = v4f32(hsv.x, hsv.y, hsv.z, rgba.w);
  rd_entity_equip_color_hsva(entity, hsva);
}

internal void
rd_entity_equip_color_hsva(RD_Entity *entity, Vec4F32 hsva)
{
  rd_require_entity_nonnil(entity, return);
  entity->color_hsva = hsva;
  entity->flags |= RD_EntityFlag_HasColor;
}

internal void
rd_entity_equip_cfg_src(RD_Entity *entity, RD_CfgSrc cfg_src)
{
  rd_require_entity_nonnil(entity, return);
  entity->cfg_src = cfg_src;
}

internal void
rd_entity_equip_timestamp(RD_Entity *entity, U64 timestamp)
{
  rd_require_entity_nonnil(entity, return);
  entity->timestamp = timestamp;
}

//- rjf: control layer correllation equipment

internal void
rd_entity_equip_vaddr(RD_Entity *entity, U64 vaddr)
{
  rd_require_entity_nonnil(entity, return);
  entity->vaddr = vaddr;
  entity->flags |= RD_EntityFlag_HasVAddr;
}

//- rjf: name equipment

internal void
rd_entity_equip_name(RD_Entity *entity, String8 name)
{
  rd_require_entity_nonnil(entity, return);
  if(entity->string.size != 0)
  {
    rd_name_release(entity->string);
  }
  if(name.size != 0)
  {
    entity->string = rd_name_alloc(name);
  }
  else
  {
    entity->string = str8_zero();
  }
}

//- rjf: file path map override lookups

internal String8
rd_mapped_from_file_path(Arena *arena, String8 file_path)
{
  Temp scratch = scratch_begin(&arena, 1);
  String8 result = file_path;
  if(file_path.size != 0)
  {
    String8 file_path__normalized = path_normalized_from_string(scratch.arena, file_path);
    String8List file_path_parts = str8_split_path(scratch.arena, file_path__normalized);
    RD_CfgList maps = rd_cfg_top_level_list_from_string(scratch.arena, str8_lit("file_path_map"));
    String8 best_map_dst = {0};
    U64 best_map_match_length = max_U64;
    String8Node *best_map_remaining_suffix_first = 0;
    for(RD_CfgNode *n = maps.first; n != 0; n = n->next)
    {
      String8 map_src = rd_cfg_child_from_string(n->v, str8_lit("source"))->first->string;
      String8 map_src__normalized = path_normalized_from_string(scratch.arena, map_src);
      String8List map_src_parts = str8_split_path(scratch.arena, map_src__normalized);
      B32 matches = 1;
      U64 match_length = 0;
      String8Node *file_path_part_n = file_path_parts.first;
      for(String8Node *map_src_n = map_src_parts.first;
          map_src_n != 0 && file_path_part_n != 0;
          map_src_n = map_src_n->next, file_path_part_n = file_path_part_n->next)
      {
        if(!str8_match(map_src_n->string, file_path_part_n->string, 0))
        {
          matches = 0;
          break;
        }
        match_length += 1;
      }
      if(matches && match_length < best_map_match_length)
      {
        best_map_match_length = match_length;
        best_map_dst = rd_cfg_child_from_string(n->v, str8_lit("dest"))->first->string;
        best_map_remaining_suffix_first = file_path_part_n;
      }
    }
    if(best_map_dst.size != 0)
    {
      String8 best_map_dst__normalized = path_normalized_from_string(scratch.arena, best_map_dst);
      String8List best_map_dst_parts = str8_split_path(scratch.arena, best_map_dst__normalized);
      for(String8Node *n = best_map_remaining_suffix_first; n != 0; n = n->next)
      {
        str8_list_push(scratch.arena, &best_map_dst_parts, n->string);
      }
      StringJoin join = {.sep = str8_lit("/")};
      result = str8_list_join(arena, &best_map_dst_parts, &join);
    }
    else
    {
      result = path_normalized_from_string(arena, result);
    }
  }
  scratch_end(scratch);
  return result;
}

internal String8List
rd_possible_overrides_from_file_path(Arena *arena, String8 file_path)
{
  // NOTE(rjf): This path, given some target file path, scans all file path map
  // overrides, and collects the set of file paths which could've redirected
  // to the target file path given the set of file path maps.
  //
  // For example, if I have a rule saying D:/devel/ maps to C:/devel/, and I
  // feed in C:/devel/foo/bar.txt, then this path will construct
  // D:/devel/foo/bar.txt, as a possible option.
  //
  // It will also preserve C:/devel/foo/bar.txt in the resultant list, so that
  // overrideless files still work through this path, and both redirected
  // files and non-redirected files can go through the same path.
  //
  String8List result = {0};
  str8_list_push(arena, &result, file_path);
  Temp scratch = scratch_begin(&arena, 1);
  PathStyle pth_style = PathStyle_Relative;
  String8List pth_parts = path_normalized_list_from_string(scratch.arena, file_path, &pth_style);
  {
    RD_CfgList links = rd_cfg_top_level_list_from_string(scratch.arena, str8_lit("file_path_map"));
    for(RD_CfgNode *n = links.first; n != 0; n = n->next)
    {
      //- rjf: unpack link
      RD_Cfg *link = n->v;
      RD_Cfg *src = rd_cfg_child_from_string(link, str8_lit("source"));
      RD_Cfg *dst = rd_cfg_child_from_string(link, str8_lit("dest"));
      PathStyle src_style = PathStyle_Relative;
      PathStyle dst_style = PathStyle_Relative;
      String8List src_parts = path_normalized_list_from_string(scratch.arena, src->first->string, &src_style);
      String8List dst_parts = path_normalized_list_from_string(scratch.arena, dst->first->string, &dst_style);
      
      //- rjf: determine if this link can possibly redirect to the target file path
      B32 dst_redirects_to_pth = 0;
      String8Node *non_redirected_pth_first = 0;
      if(dst_style == pth_style && dst_parts.first != 0 && pth_parts.first != 0)
      {
        dst_redirects_to_pth = 1;
        String8Node *dst_n = dst_parts.first;
        String8Node *pth_n = pth_parts.first;
        for(;dst_n != 0 && pth_n != 0; dst_n = dst_n->next, pth_n = pth_n->next)
        {
          if(!str8_match(dst_n->string, pth_n->string, StringMatchFlag_CaseInsensitive))
          {
            dst_redirects_to_pth = 0;
            break;
          }
          non_redirected_pth_first = pth_n->next;
        }
      }
      
      //- rjf: if this link can redirect to this path via `src` -> `dst`, compute
      // possible full source path, by taking `src` and appending non-redirected
      // suffix (which did not show up in `dst`)
      if(dst_redirects_to_pth)
      {
        String8List candidate_parts = src_parts;
        for(String8Node *p = non_redirected_pth_first; p != 0; p = p->next)
        {
          str8_list_push(scratch.arena, &candidate_parts, p->string);
        }
        StringJoin join = {0};
        join.sep = str8_lit("/");
        String8 candidate_path = str8_list_join(arena, &candidate_parts, &join);
        str8_list_push(arena, &result, candidate_path);
      }
    }
  }
  
  scratch_end(scratch);
  return result;
}

//- rjf: top-level state queries

internal RD_Entity *
rd_entity_root(void)
{
  return rd_state->entities_root;
}

internal RD_EntityList
rd_push_entity_list_with_kind(Arena *arena, RD_EntityKind kind)
{
  ProfBeginFunction();
  RD_EntityList result = {0};
  for(RD_Entity *entity = rd_state->entities_root;
      !rd_entity_is_nil(entity);
      entity = rd_entity_rec_depth_first_pre(entity, &rd_nil_entity).next)
  {
    if(entity->kind == kind && !(entity->flags & RD_EntityFlag_MarkedForDeletion))
    {
      rd_entity_list_push(arena, &result, entity);
    }
  }
  ProfEnd();
  return result;
}

internal RD_Entity *
rd_entity_from_id(RD_EntityID id)
{
  RD_Entity *result = &rd_nil_entity;
  for(RD_Entity *e = rd_entity_root();
      !rd_entity_is_nil(e);
      e = rd_entity_rec_depth_first_pre(e, &rd_nil_entity).next)
  {
    if(e->id == id)
    {
      result = e;
      break;
    }
  }
  return result;
}

internal RD_Entity *
rd_entity_from_name_and_kind(String8 string, RD_EntityKind kind)
{
  RD_Entity *result = &rd_nil_entity;
  RD_EntityList all_of_this_kind = rd_query_cached_entity_list_with_kind(kind);
  for(RD_EntityNode *n = all_of_this_kind.first; n != 0; n = n->next)
  {
    if(str8_match(n->entity->string, string, 0))
    {
      result = n->entity;
      break;
    }
  }
  return result;
}

////////////////////////////////
//~ rjf: Frontend Entity Info Extraction

internal DR_FancyStringList
rd_title_fstrs_from_entity(Arena *arena, RD_Entity *entity, Vec4F32 secondary_color, F32 size)
{
  DR_FancyStringList result = {0};
  RD_Entity *exe  = rd_entity_child_from_kind(entity, RD_EntityKind_Executable);
  RD_Entity *args = rd_entity_child_from_kind(entity, RD_EntityKind_Arguments);
  RD_Entity *loc  = rd_entity_child_from_kind(entity, RD_EntityKind_Location);
  RD_Entity *cnd  = rd_entity_child_from_kind(entity, RD_EntityKind_Condition);
  RD_Entity *src  = rd_entity_child_from_kind(entity, RD_EntityKind_Source);
  RD_Entity *dst  = rd_entity_child_from_kind(entity, RD_EntityKind_Dest);
  RD_IconKind icon_kind = rd_entity_kind_icon_kind_table[entity->kind];
  Vec4F32 color = rd_rgba_from_theme_color(RD_ThemeColor_Text);
  if(icon_kind != RD_IconKind_Null)
  {
    dr_fancy_string_list_push_new(arena, &result, rd_font_from_slot(RD_FontSlot_Icons), size, secondary_color, rd_icon_kind_text_table[icon_kind]);
  }
  dr_fancy_string_list_push_new(arena, &result, rd_font_from_slot(RD_FontSlot_Code), size, secondary_color, str8_lit(" "));
  if(entity->kind == RD_EntityKind_Target && entity->cfg_src == RD_CfgSrc_CommandLine)
  {
    dr_fancy_string_list_push_new(arena, &result, rd_font_from_slot(RD_FontSlot_Icons), size, rd_rgba_from_theme_color(RD_ThemeColor_TextNegative), rd_icon_kind_text_table[RD_IconKind_Info]);
    dr_fancy_string_list_push_new(arena, &result, rd_font_from_slot(RD_FontSlot_Code), size, secondary_color, str8_lit(" "));
  }
  String8 name = entity->string;
  B32 name_is_code = 1;
  if(rd_entity_kind_flags_table[entity->kind] & RD_EntityKindFlag_NameIsPath)
  {
    name_is_code = 0;
  }
  String8 location = {0};
  B32 location_is_code = 0;
  String8 exe_name = str8_skip_last_slash(exe->string);
  String8 args_string = args->string;
  String8 cnd_string = cnd->string;
  if(!rd_entity_is_nil(loc))
  {
    if(loc->string.size != 0 && loc->flags & RD_EntityFlag_HasTextPoint)
    {
      location = push_str8f(arena, "%S:%I64d:%I64d", loc->string, loc->text_point.line, loc->text_point.column);
      location_is_code = 0;
    }
    else if(loc->string.size != 0)
    {
      location = loc->string;
      location_is_code = 1;
    }
    else if(loc->flags & RD_EntityFlag_HasVAddr)
    {
      location = push_str8f(arena, "0x%I64x", loc->vaddr);
      location_is_code = 1;
    }
  }
  B32 extra = 0;
  F32 size_extrafied = size;
  Vec4F32 color_extrafied = color;
  if(name.size != 0)
  {
    dr_fancy_string_list_push_new(arena, &result, rd_font_from_slot(name_is_code ? RD_FontSlot_Code : RD_FontSlot_Main), size, color, name);
    extra = 1;
    size_extrafied = size*0.95f;
    color_extrafied = secondary_color;
  }
  if(location.size != 0)
  {
    if(extra)
    {
      dr_fancy_string_list_push_new(arena, &result, rd_font_from_slot(RD_FontSlot_Code), size, v4f32(0, 0, 0, 0), str8_lit(" "));
    }
    if(location_is_code)
    {
      DR_FancyStringList loc_fstrs = {0};
      RD_Font(RD_FontSlot_Code)
        loc_fstrs = rd_fancy_string_list_from_code_string(arena, 1.f, 0, color_extrafied, location);
      dr_fancy_string_list_concat_in_place(&result, &loc_fstrs);
    }
    else
    {
      dr_fancy_string_list_push_new(arena, &result, rd_font_from_slot(RD_FontSlot_Main), size_extrafied, color_extrafied, location);
    }
    extra = 1;
    size_extrafied = size*0.95f;
    color_extrafied = secondary_color;
  }
  if(exe_name.size != 0)
  {
    if(extra)
    {
      dr_fancy_string_list_push_new(arena, &result, rd_font_from_slot(RD_FontSlot_Code), size, v4f32(0, 0, 0, 0), str8_lit(" "));
    }
    dr_fancy_string_list_push_new(arena, &result, rd_font_from_slot(RD_FontSlot_Main), size_extrafied, color_extrafied, exe_name);
    extra = 1;
    size_extrafied = size*0.95f;
    color_extrafied = secondary_color;
  }
  if(args_string.size != 0)
  {
    if(extra)
    {
      dr_fancy_string_list_push_new(arena, &result, rd_font_from_slot(RD_FontSlot_Code), size, v4f32(0, 0, 0, 0), str8_lit(" "));
    }
    dr_fancy_string_list_push_new(arena, &result, rd_font_from_slot(RD_FontSlot_Main), size_extrafied, color_extrafied, args_string);
    extra = 1;
    size_extrafied = size*0.95f;
    color_extrafied = secondary_color;
  }
  if(cnd_string.size != 0)
  {
    dr_fancy_string_list_push_new(arena, &result, rd_font_from_slot(RD_FontSlot_Code), size, color_extrafied, str8_lit(" if "));
    RD_Font(RD_FontSlot_Code) UI_FontSize(size_extrafied)
    {
      DR_FancyStringList cnd_fstrs = rd_fancy_string_list_from_code_string(arena, 1.f, 0.f, color_extrafied, cnd_string);
      dr_fancy_string_list_concat_in_place(&result, &cnd_fstrs);
    }
  }
  if(entity->kind == RD_EntityKind_FilePathMap)
  {
    String8 src_string = src->string;
    Vec4F32 src_color = color;
    String8 dst_string = dst->string;
    Vec4F32 dst_color = color;
    if(src_string.size == 0)
    {
      src_string = str8_lit("(source path)");
      src_color = secondary_color;
    }
    if(dst_string.size == 0)
    {
      dst_string = str8_lit("(destination path)");
      dst_color = secondary_color;
    }
    dr_fancy_string_list_push_new(arena, &result, rd_font_from_slot(RD_FontSlot_Main), size, src_color, src_string);
    dr_fancy_string_list_push_new(arena, &result, rd_font_from_slot(RD_FontSlot_Code), size, v4f32(0, 0, 0, 0), str8_lit(" "));
    dr_fancy_string_list_push_new(arena, &result, rd_font_from_slot(RD_FontSlot_Icons), size, secondary_color, rd_icon_kind_text_table[RD_IconKind_RightArrow]);
    dr_fancy_string_list_push_new(arena, &result, rd_font_from_slot(RD_FontSlot_Code), size, v4f32(0, 0, 0, 0), str8_lit(" "));
    dr_fancy_string_list_push_new(arena, &result, rd_font_from_slot(RD_FontSlot_Main), size, dst_color, dst_string);
  }
  if(entity->kind == RD_EntityKind_AutoViewRule)
  {
    String8 src_string = src->string;
    Vec4F32 src_color = color;
    String8 dst_string = dst->string;
    Vec4F32 dst_color = color;
    DR_FancyStringList src_fstrs = {0};
    DR_FancyStringList dst_fstrs = {0};
    if(src_string.size == 0)
    {
      src_string = str8_lit("(type)");
      src_color = secondary_color;
      dr_fancy_string_list_push_new(arena, &src_fstrs, rd_font_from_slot(RD_FontSlot_Main), size, src_color, src_string);
    }
    else RD_Font(RD_FontSlot_Code)
    {
      src_fstrs = rd_fancy_string_list_from_code_string(arena, 1.f, 0, src_color, src_string);
    }
    if(dst_string.size == 0)
    {
      dst_string = str8_lit("(view rule)");
      dst_color = secondary_color;
      dr_fancy_string_list_push_new(arena, &dst_fstrs, rd_font_from_slot(RD_FontSlot_Main), size, dst_color, dst_string);
    }
    else RD_Font(RD_FontSlot_Code)
    {
      dst_fstrs = rd_fancy_string_list_from_code_string(arena, 1.f, 0, dst_color, dst_string);
    }
    dr_fancy_string_list_concat_in_place(&result, &src_fstrs);
    dr_fancy_string_list_push_new(arena, &result, rd_font_from_slot(RD_FontSlot_Code), size, v4f32(0, 0, 0, 0), str8_lit(" "));
    dr_fancy_string_list_push_new(arena, &result, rd_font_from_slot(RD_FontSlot_Icons), size, secondary_color, rd_icon_kind_text_table[RD_IconKind_RightArrow]);
    dr_fancy_string_list_push_new(arena, &result, rd_font_from_slot(RD_FontSlot_Code), size, v4f32(0, 0, 0, 0), str8_lit(" "));
    dr_fancy_string_list_concat_in_place(&result, &dst_fstrs);
  }
  if((entity->kind == RD_EntityKind_Target || entity->kind == RD_EntityKind_Breakpoint) && entity->disabled)
  {
    dr_fancy_string_list_push_new(arena, &result, rd_font_from_slot(RD_FontSlot_Code), size, v4f32(0, 0, 0, 0), str8_lit(" "));
    dr_fancy_string_list_push_new(arena, &result, rd_font_from_slot(RD_FontSlot_Main), size*0.95f, secondary_color, str8_lit("(Disabled)"));
  }
  if(entity->kind == RD_EntityKind_Breakpoint)
  {
    dr_fancy_string_list_push_new(arena, &result, rd_font_from_slot(RD_FontSlot_Code), size, v4f32(0, 0, 0, 0), str8_lit(" "));
    String8 string = push_str8f(arena, "(%I64u hit%s)", entity->u64, entity->u64 == 1 ? "" : "s");
    dr_fancy_string_list_push_new(arena, &result, rd_font_from_slot(RD_FontSlot_Main), size_extrafied, secondary_color, string);
  }
  return result;
}

////////////////////////////////
//~ rjf: Control Entity Info Extraction

internal Vec4F32
rd_rgba_from_ctrl_entity(CTRL_Entity *entity)
{
  Vec4F32 result = rd_rgba_from_theme_color(RD_ThemeColor_Text);
  if(entity->rgba != 0)
  {
    result = rgba_from_u32(entity->rgba);
  }
  if(entity->rgba == 0) switch(entity->kind)
  {
    default:{}break;
    case CTRL_EntityKind_Thread:
    {
      CTRL_Entity *process = ctrl_entity_ancestor_from_kind(entity, CTRL_EntityKind_Process);
      CTRL_Entity *main_thread = ctrl_entity_child_from_kind(process, CTRL_EntityKind_Thread);
      if(main_thread != entity)
      {
        result = rd_rgba_from_theme_color(RD_ThemeColor_Thread1);
      }
      else
      {
        result = rd_rgba_from_theme_color(RD_ThemeColor_Thread0);
      }
    }break;
  }
  return result;
}

internal String8
rd_name_from_ctrl_entity(Arena *arena, CTRL_Entity *entity)
{
  String8 string = entity->string;
  if(string.size == 0)
  {
    string = str8_lit("unnamed");
  }
  if(entity->kind == CTRL_EntityKind_Module)
  {
    string = str8_skip_last_slash(string);
  }
  return string;
}

internal DR_FancyStringList
rd_title_fstrs_from_ctrl_entity(Arena *arena, CTRL_Entity *entity, Vec4F32 secondary_color, F32 size, B32 include_extras)
{
  DR_FancyStringList result = {0};
  
  //- rjf: unpack entity info
  F32 extras_size = size*0.95f;
  Vec4F32 color = rd_rgba_from_ctrl_entity(entity);
  String8 name = rd_name_from_ctrl_entity(arena, entity);
  RD_IconKind icon_kind = RD_IconKind_Null;
  B32 name_is_code = 0;
  switch(entity->kind)
  {
    default:{}break;
    case CTRL_EntityKind_Machine: {icon_kind = RD_IconKind_Machine;}break;
    case CTRL_EntityKind_Process: {icon_kind = RD_IconKind_Threads;}break;
    case CTRL_EntityKind_Thread:  {icon_kind = RD_IconKind_Thread; name_is_code = 1;}break;
    case CTRL_EntityKind_Module:  {icon_kind = RD_IconKind_Module;}break;
  }
  
  //- rjf: push icon
  if(icon_kind != RD_IconKind_Null)
  {
    dr_fancy_string_list_push_new(arena, &result, rd_font_from_slot(RD_FontSlot_Icons), size, secondary_color, rd_icon_kind_text_table[icon_kind]);
    dr_fancy_string_list_push_new(arena, &result, rd_font_from_slot(RD_FontSlot_Code), size, secondary_color, str8_lit(" "));
  }
  
  //- rjf: push containing process prefix
  if(entity->kind == CTRL_EntityKind_Thread ||
     entity->kind == CTRL_EntityKind_Module)
  {
    CTRL_EntityList processes = ctrl_entity_list_from_kind(d_state->ctrl_entity_store, CTRL_EntityKind_Process);
    if(processes.count > 1)
    {
      CTRL_Entity *process = ctrl_entity_ancestor_from_kind(entity, CTRL_EntityKind_Process);
      String8 process_name = rd_name_from_ctrl_entity(arena, process);
      Vec4F32 process_color = rd_rgba_from_ctrl_entity(process);
      if(process_name.size != 0)
      {
        dr_fancy_string_list_push_new(arena, &result, rd_font_from_slot(RD_FontSlot_Main), size, process_color, process_name);
        dr_fancy_string_list_push_new(arena, &result, rd_font_from_slot(RD_FontSlot_Code), size, secondary_color, str8_lit(" / "));
      }
    }
  }
  
  //- rjf: push name
  dr_fancy_string_list_push_new(arena, &result, rd_font_from_slot(name_is_code ? RD_FontSlot_Code : RD_FontSlot_Main), size, color, name);
  
  //- rjf: threads get callstack extras
  if(entity->kind == CTRL_EntityKind_Thread && include_extras)
  {
    dr_fancy_string_list_push_new(arena, &result, rd_font_from_slot(RD_FontSlot_Code), size, secondary_color, str8_lit(" "));
    DI_Scope *di_scope = di_scope_open();
    CTRL_Entity *process = ctrl_entity_ancestor_from_kind(entity, CTRL_EntityKind_Process);
    Arch arch = entity->arch;
    CTRL_Unwind unwind = d_query_cached_unwind_from_thread(entity);
    for(U64 idx = 0, limit = 6; idx < unwind.frames.count && idx < limit; idx += 1)
    {
      CTRL_UnwindFrame *f = &unwind.frames.v[unwind.frames.count - 1 - idx];
      U64 rip_vaddr = regs_rip_from_arch_block(arch, f->regs);
      CTRL_Entity *module = ctrl_module_from_process_vaddr(process, rip_vaddr);
      U64 rip_voff = ctrl_voff_from_vaddr(module, rip_vaddr);
      DI_Key dbgi_key = ctrl_dbgi_key_from_module(module);
      RDI_Parsed *rdi = di_rdi_from_key(di_scope, &dbgi_key, 0);
      if(rdi != &di_rdi_parsed_nil)
      {
        RDI_Procedure *procedure = rdi_procedure_from_voff(rdi, rip_voff);
        String8 name = {0};
        name.str = rdi_string_from_idx(rdi, procedure->name_string_idx, &name.size);
        name = push_str8_copy(arena, name);
        if(name.size != 0)
        {
          dr_fancy_string_list_push_new(arena, &result, rd_font_from_slot(RD_FontSlot_Code), extras_size, rd_rgba_from_theme_color(RD_ThemeColor_CodeSymbol), name);
          if(idx+1 < unwind.frames.count)
          {
            dr_fancy_string_list_push_new(arena, &result, rd_font_from_slot(RD_FontSlot_Code), extras_size, secondary_color, str8_lit(" > "));
            if(idx+1 == limit)
            {
              dr_fancy_string_list_push_new(arena, &result, rd_font_from_slot(RD_FontSlot_Code), extras_size, secondary_color, str8_lit("..."));
            }
          }
        }
      }
    }
    di_scope_close(di_scope);
  }
  
  //- rjf: modules get debug info status extras
  if(entity->kind == CTRL_EntityKind_Module && include_extras)
  {
    DI_Scope *di_scope = di_scope_open();
    DI_Key dbgi_key = ctrl_dbgi_key_from_module(entity);
    RDI_Parsed *rdi = di_rdi_from_key(di_scope, &dbgi_key, 0);
    if(rdi->raw_data_size == 0)
    {
      dr_fancy_string_list_push_new(arena, &result, rd_font_from_slot(RD_FontSlot_Code), size, secondary_color, str8_lit(" "));
      dr_fancy_string_list_push_new(arena, &result, rd_font_from_slot(RD_FontSlot_Main), extras_size, secondary_color, str8_lit("(Symbols not found)"));
    }
    di_scope_close(di_scope);
  }
  
  return result;
}

////////////////////////////////
//~ rjf: Evaluation Spaces

//- rjf: cfg <-> eval space

internal RD_Cfg *
rd_cfg_from_eval_space(E_Space space)
{
  RD_Cfg *cfg = &rd_nil_cfg;
  if(space.kind == RD_EvalSpaceKind_MetaCfg)
  {
    RD_Handle handle = {space.u64s[0], space.u64s[1]};
    cfg = rd_cfg_from_handle(handle);
  }
  return cfg;
}

internal E_Space
rd_eval_space_from_cfg(RD_Cfg *cfg)
{
  E_Space space = e_space_make(RD_EvalSpaceKind_MetaCfg);
  RD_Handle handle = rd_handle_from_cfg(cfg);
  space.u64s[0] = handle.u64[0];
  space.u64s[1] = handle.u64[1];
  return space;
}

//- rjf: ctrl entity <-> eval space

internal CTRL_Entity *
rd_ctrl_entity_from_eval_space(E_Space space)
{
  CTRL_Entity *entity = &ctrl_entity_nil;
  if(space.kind == RD_EvalSpaceKind_CtrlEntity ||
     space.kind == RD_EvalSpaceKind_MetaCtrlEntity)
  {
    CTRL_Handle handle;
    handle.machine_id = space.u64s[0];
    handle.dmn_handle.u64[0] = space.u64s[1];
    entity = ctrl_entity_from_handle(d_state->ctrl_entity_store, handle);
  }
  return entity;
}

internal E_Space
rd_eval_space_from_ctrl_entity(CTRL_Entity *entity, E_SpaceKind kind)
{
  E_Space space = e_space_make(kind);
  space.u64s[0] = entity->handle.machine_id;
  space.u64s[1] = entity->handle.dmn_handle.u64[0];
  return space;
}

//- rjf: cfg -> eval blob

internal String8
rd_eval_blob_from_cfg(Arena *arena, RD_Cfg *cfg)
{
  String8 result = {0};
  String8 name = cfg->string;
  E_TypeKey type_key = e_string2typekey_map_lookup(rd_state->meta_name2type_map, name);
  if(!e_type_key_match(e_type_key_zero(), type_key))
  {
    Temp scratch = scratch_begin(&arena, 1);
    MD_Node *schema = rd_schema_from_name(scratch.arena, name);
    String8List fixed_width_parts = {0};
    String8List variable_width_parts = {0};
    {
      E_Type *type = e_type_from_key__cached(type_key);
      if(type->members != 0) for EachIndex(member_idx, type->count)
      {
        E_Member *member = &type->members[member_idx];
        String8 child_name = member->name;
        MD_Node *member_schema = md_child_from_string(schema, child_name, 0);
        String8 member_type_name = member_schema->first->string;
        RD_Cfg *child = rd_cfg_child_from_string(cfg, child_name);
        if(str8_match(member_type_name, str8_lit("location"), 0))
        {
          U64 off = type->byte_size + variable_width_parts.total_size;
          str8_list_push(scratch.arena, &fixed_width_parts, push_str8_copy(scratch.arena, str8_struct(&off)));
          for(RD_Cfg *loc_child = child->first; loc_child != &rd_nil_cfg; loc_child = loc_child->first)
          {
            if(loc_child != child->first)
            {
              str8_list_push(scratch.arena, &variable_width_parts, str8_lit(":"));
            }
            str8_list_push(scratch.arena, &variable_width_parts, loc_child->string);
          }
          str8_list_push(scratch.arena, &variable_width_parts, str8_lit("\0"));
        }
        else if(str8_match(member_type_name, str8_lit("code_string"), 0) ||
                str8_match(member_type_name, str8_lit("path"), 0) ||
                str8_match(member_type_name, str8_lit("string"), 0))
        {
          U64 off = type->byte_size + variable_width_parts.total_size;
          str8_list_push(scratch.arena, &fixed_width_parts, push_str8_copy(scratch.arena, str8_struct(&off)));
          str8_list_push(scratch.arena, &variable_width_parts, child->first->string);
          str8_list_push(scratch.arena, &variable_width_parts, str8_lit("\0"));
        }
        else if(str8_match(member_type_name, str8_lit("u64"), 0))
        {
          U64 val = 0;
          try_u64_from_str8_c_rules(child->first->string, &val);
          str8_list_push(scratch.arena, &fixed_width_parts, push_str8_copy(scratch.arena, str8_struct(&val)));
        }
        else if(str8_match(member_type_name, str8_lit("bool"), 0))
        {
          B32 val = str8_match(child->first->string, str8_lit("1"), 0);
          str8_list_push(scratch.arena, &fixed_width_parts, push_str8_copy(scratch.arena, str8((U8 *)&val, e_type_byte_size_from_key(member->type_key))));
        }
      }
    }
    String8List all_parts = {0};
    str8_list_concat_in_place(&all_parts, &fixed_width_parts);
    str8_list_concat_in_place(&all_parts, &variable_width_parts);
    result = str8_list_join(arena, &all_parts, 0);
  }
  return result;
}

internal String8
rd_eval_blob_from_cfg__cached(RD_Cfg *cfg)
{
  String8 result = {0};
  {
    RD_Cfg2EvalBlobMap *map = rd_state->cfg2evalblob_map;
    RD_Handle handle = rd_handle_from_cfg(cfg);
    U64 hash = d_hash_from_string(str8_struct(&handle));
    U64 slot_idx = hash%map->slots_count;
    RD_Cfg2EvalBlobNode *node = 0;
    for(RD_Cfg2EvalBlobNode *n = map->slots[slot_idx].first; n != 0; n = n->next)
    {
      if(rd_handle_match(handle, n->handle))
      {
        node = n;
        break;
      }
    }
    if(node == 0)
    {
      node = push_array(rd_frame_arena(), RD_Cfg2EvalBlobNode, 1);
      SLLQueuePush(map->slots[slot_idx].first, map->slots[slot_idx].last, node);
      node->handle = handle;
      node->blob = rd_eval_blob_from_cfg(rd_frame_arena(), cfg);
    }
    result = node->blob;
  }
  return result;
}

//- rjf: ctrl entity -> eval blob

internal String8
rd_eval_blob_from_entity(Arena *arena, CTRL_Entity *entity)
{
  String8 result = {0};
  String8 name = ctrl_entity_kind_code_name_table[entity->kind];
  E_TypeKey type_key = e_string2typekey_map_lookup(rd_state->meta_name2type_map, name);
  if(!e_type_key_match(e_type_key_zero(), type_key))
  {
    Temp scratch = scratch_begin(&arena, 1);
    MD_Node *schema = rd_schema_from_name(scratch.arena, name);
    String8List fixed_width_parts = {0};
    String8List variable_width_parts = {0};
    {
      E_Type *type = e_type_from_key__cached(type_key);
      if(type->members != 0) for EachIndex(member_idx, type->count)
      {
        E_Member *member = &type->members[member_idx];
        String8 member_name = member->name;
        if(0){}
        else if(str8_match(member_name, str8_lit("frozen"), 0))
        {
          str8_list_push(scratch.arena, &fixed_width_parts, str8((U8 *)&entity->is_frozen, 1));
        }
        else if(str8_match(member_name, str8_lit("vaddr_range"), 0))
        {
          str8_list_push(scratch.arena, &fixed_width_parts, str8_struct(&entity->vaddr_range));
        }
        else if(str8_match(member_name, str8_lit("id"), 0))
        {
          str8_list_push(scratch.arena, &fixed_width_parts, str8_struct(&entity->id));
        }
        else if(str8_match(member_name, str8_lit("label"), 0))
        {
          String8 label = entity->string;
          if(entity->kind == CTRL_EntityKind_Module)
          {
            label = str8_skip_last_slash(label);
          }
          U64 off = type->byte_size + variable_width_parts.total_size;
          str8_list_push(scratch.arena, &fixed_width_parts, push_str8_copy(scratch.arena, str8_struct(&off)));
          str8_list_push(scratch.arena, &variable_width_parts, label);
          str8_list_push(scratch.arena, &variable_width_parts, str8_lit("\0"));
        }
        else if(str8_match(member_name, str8_lit("exe"), 0))
        {
          String8 string = entity->string;
          U64 off = type->byte_size + variable_width_parts.total_size;
          str8_list_push(scratch.arena, &fixed_width_parts, push_str8_copy(scratch.arena, str8_struct(&off)));
          str8_list_push(scratch.arena, &variable_width_parts, string);
          str8_list_push(scratch.arena, &variable_width_parts, str8_lit("\0"));
        }
        else if(str8_match(member_name, str8_lit("dbg"), 0))
        {
          String8 string = ctrl_entity_child_from_kind(entity, CTRL_EntityKind_DebugInfoPath)->string;
          U64 off = type->byte_size + variable_width_parts.total_size;
          str8_list_push(scratch.arena, &fixed_width_parts, push_str8_copy(scratch.arena, str8_struct(&off)));
          str8_list_push(scratch.arena, &variable_width_parts, string);
          str8_list_push(scratch.arena, &variable_width_parts, str8_lit("\0"));
        }
      }
    }
    String8List all_parts = {0};
    str8_list_concat_in_place(&all_parts, &fixed_width_parts);
    str8_list_concat_in_place(&all_parts, &variable_width_parts);
    result = str8_list_join(arena, &all_parts, 0);
  }
  return result;
}

internal String8
rd_eval_blob_from_entity__cached(CTRL_Entity *entity)
{
  String8 result = {0};
  {
    RD_Entity2EvalBlobMap *map = rd_state->entity2evalblob_map;
    CTRL_Handle handle = entity->handle;
    U64 hash = ctrl_hash_from_handle(handle);
    U64 slot_idx = hash%map->slots_count;
    RD_Entity2EvalBlobNode *node = 0;
    for(RD_Entity2EvalBlobNode *n = map->slots[slot_idx].first; n != 0; n = n->next)
    {
      if(ctrl_handle_match(n->handle, handle))
      {
        node = n;
        break;
      }
    }
    if(node == 0)
    {
      node = push_array(rd_frame_arena(), RD_Entity2EvalBlobNode, 1);
      SLLQueuePush(map->slots[slot_idx].first, map->slots[slot_idx].last, node);
      node->handle = handle;
      node->blob = rd_eval_blob_from_entity(rd_frame_arena(), entity);
    }
    result = node->blob;
  }
  return result;
}

//- rjf: entity -> meta eval

internal CTRL_MetaEval *
rd_ctrl_meta_eval_from_entity(Arena *arena, RD_Entity *entity)
{
  ProfBeginFunction();
  CTRL_MetaEval *meval = push_array(arena, CTRL_MetaEval, 1);
  RD_Entity *exe = rd_entity_child_from_kind(entity, RD_EntityKind_Executable);
  RD_Entity *args= rd_entity_child_from_kind(entity, RD_EntityKind_Arguments);
  RD_Entity *wdir= rd_entity_child_from_kind(entity, RD_EntityKind_WorkingDirectory);
  RD_Entity *entr= rd_entity_child_from_kind(entity, RD_EntityKind_EntryPoint);
  RD_Entity *stdo= rd_entity_child_from_kind(entity, RD_EntityKind_StdoutPath);
  RD_Entity *stde= rd_entity_child_from_kind(entity, RD_EntityKind_StderrPath);
  RD_Entity *stdi= rd_entity_child_from_kind(entity, RD_EntityKind_StdinPath);
  RD_Entity *loc = rd_entity_child_from_kind(entity, RD_EntityKind_Location);
  RD_Entity *cnd = rd_entity_child_from_kind(entity, RD_EntityKind_Condition);
  RD_Entity *src = rd_entity_child_from_kind(entity, RD_EntityKind_Source);
  RD_Entity *dst = rd_entity_child_from_kind(entity, RD_EntityKind_Dest);
  String8 label_string = push_str8_copy(arena, entity->string);
  String8 src_loc_string = {0};
  String8 vaddr_loc_string = {0};
  String8 function_loc_string = {0};
  if(loc->flags & RD_EntityFlag_HasTextPoint)
  {
    src_loc_string = push_str8f(arena, "%S:%I64u:%I64u", loc->string, loc->text_point.line, loc->text_point.column);
  }
  else if(loc->flags & RD_EntityFlag_HasVAddr)
  {
    vaddr_loc_string = push_str8f(arena, "0x%I64x", loc->vaddr);
  }
  else if(loc->string.size != 0)
  {
    function_loc_string = push_str8_copy(arena, loc->string);
  }
  String8 cnd_string = push_str8_copy(arena, cnd->string);
  meval->enabled   = !entity->disabled;
  meval->hit_count = entity->u64;
  meval->color     = u32_from_rgba(rd_rgba_from_entity(entity));
  meval->label     = label_string;
  meval->exe       = exe->string;
  meval->args      = args->string;
  meval->working_directory = wdir->string;
  meval->entry_point = entr->string;
  meval->stdout_path = stdo->string;
  meval->stderr_path = stde->string;
  meval->stdin_path  = stdi->string;
  meval->source_location = src_loc_string;
  meval->address_location = vaddr_loc_string;
  meval->function_location = function_loc_string;
  meval->condition = cnd_string;
  meval->debug_subprocesses.b32 = entity->debug_subprocesses;
  switch(entity->kind)
  {
    default:{}break;
    case RD_EntityKind_FilePathMap:
    {
      meval->source_path = src->string;
      meval->destination_path = dst->string;
    }break;
    case RD_EntityKind_AutoViewRule:
    {
      meval->type      = src->string;
      meval->view_rule = dst->string;
    }break;
  }
  ProfEnd();
  return meval;
}

internal CTRL_MetaEval *
rd_ctrl_meta_eval_from_ctrl_entity(Arena *arena, CTRL_Entity *entity)
{
  ProfBeginFunction();
  CTRL_MetaEval *meval = push_array(arena, CTRL_MetaEval, 1);
  meval->frozen      = entity->is_frozen;
  meval->vaddr_range = entity->vaddr_range;
  meval->color       = entity->rgba;
  meval->label       = entity->string;
  meval->id          = entity->id;
  if(entity->kind == CTRL_EntityKind_Thread)
  {
    DI_Scope *di_scope = di_scope_open();
    CTRL_Entity *process = ctrl_entity_ancestor_from_kind(entity, CTRL_EntityKind_Process);
    CTRL_Unwind base_unwind = d_query_cached_unwind_from_thread(entity);
    CTRL_CallStack rich_unwind = ctrl_call_stack_from_unwind(arena, di_scope, process, &base_unwind);
    meval->callstack.count = rich_unwind.total_frame_count;
    meval->callstack.v = push_array(arena, CTRL_MetaEvalFrame, meval->callstack.count);
    U64 idx = 0;
    for(U64 base_idx = 0; base_idx < rich_unwind.concrete_frame_count; base_idx += 1)
    {
      U64 inline_idx = 0;
      for(CTRL_CallStackInlineFrame *f = rich_unwind.frames[base_idx].first_inline_frame; f != 0; f = f->next, inline_idx += 1)
      {
        meval->callstack.v[idx].vaddr = regs_rip_from_arch_block(entity->arch, rich_unwind.frames[base_idx].regs);
        meval->callstack.v[idx].inline_depth = inline_idx + 1;
        idx += 1;
      }
      meval->callstack.v[idx].vaddr = regs_rip_from_arch_block(entity->arch, rich_unwind.frames[base_idx].regs);
      idx += 1;
    }
    di_scope_close(di_scope);
  }
  if(entity->kind == CTRL_EntityKind_Module)
  {
    DI_Key dbgi_key = ctrl_dbgi_key_from_module(entity);
    meval->label = str8_skip_last_slash(entity->string);
    meval->exe = path_normalized_from_string(arena, entity->string);
    meval->dbg = path_normalized_from_string(arena, dbgi_key.path);
  }
  ProfEnd();
  return meval;
}

//- rjf: eval space reads/writes

internal B32
rd_eval_space_read(void *u, E_Space space, void *out, Rng1U64 range)
{
  Temp scratch = scratch_begin(0, 0);
  B32 result = 0;
  CTRL_MetaEval *meval_read = 0;
  Rng1U64 meval_legal_range = {0};
  switch(space.kind)
  {
    //- rjf: filesystem reads
    case E_SpaceKind_HashStoreKey:
    {
      U128 key = space.u128;
      U128 hash = hs_hash_from_key(key, 0);
      HS_Scope *scope = hs_scope_open();
      {
        String8 data = hs_data_from_hash(scope, hash);
        Rng1U64 legal_range = r1u64(0, data.size);
        Rng1U64 read_range = intersect_1u64(range, legal_range);
        if(read_range.min < read_range.max)
        {
          result = 1;
          MemoryCopy(out, data.str + read_range.min, dim_1u64(read_range));
        }
      }
      hs_scope_close(scope);
    }break;
    
    //- rjf: interior control entity reads (inside process address space or thread register block)
    case RD_EvalSpaceKind_CtrlEntity:
    {
      CTRL_Entity *entity = rd_ctrl_entity_from_eval_space(space);
      switch(entity->kind)
      {
        default:{}break;
        case CTRL_EntityKind_Process:
        {
          CTRL_ProcessMemorySlice slice = ctrl_query_cached_data_from_process_vaddr_range(scratch.arena, entity->handle, range, d_state->frame_eval_memread_endt_us);
          String8 data = slice.data;
          if(data.size == dim_1u64(range))
          {
            result = 1;
            MemoryCopy(out, data.str, data.size);
          }
        }break;
        case CTRL_EntityKind_Thread:
        {
          CTRL_Unwind unwind = d_query_cached_unwind_from_thread(entity);
          U64 frame_idx = e_interpret_ctx->reg_unwind_count;
          if(frame_idx < unwind.frames.count)
          {
            CTRL_UnwindFrame *f = &unwind.frames.v[frame_idx];
            U64 regs_size = regs_block_size_from_arch(e_interpret_ctx->reg_arch);
            Rng1U64 legal_range = r1u64(0, regs_size);
            Rng1U64 read_range = intersect_1u64(legal_range, range);
            U64 read_size = dim_1u64(read_range);
            MemoryCopy(out, (U8 *)f->regs + read_range.min, read_size);
            result = (read_size == dim_1u64(range));
          }
        }break;
      }
    }break;
    
    //- rjf: meta-config reads
    case RD_EvalSpaceKind_MetaCfg:
    {
      RD_Cfg *cfg = rd_cfg_from_eval_space(space);
      String8 cfg_eval_blob = rd_eval_blob_from_cfg__cached(cfg);
      Rng1U64 legal_range = r1u64(0, cfg_eval_blob.size);
      Rng1U64 read_range = intersect_1u64(range, legal_range);
      if(read_range.min < read_range.max)
      {
        result = 1;
        MemoryCopy(out, cfg_eval_blob.str + read_range.min, dim_1u64(read_range));
      }
    }break;
    
    //- rjf: meta-entity reads
    case RD_EvalSpaceKind_MetaCtrlEntity:
    {
      CTRL_Entity *entity = rd_ctrl_entity_from_eval_space(space);
      String8 entity_eval_blob = rd_eval_blob_from_entity__cached(entity);
      Rng1U64 legal_range = r1u64(0, entity_eval_blob.size);
      Rng1U64 read_range = intersect_1u64(range, legal_range);
      if(read_range.min < read_range.max)
      {
        result = 1;
        MemoryCopy(out, entity_eval_blob.str + read_range.min, dim_1u64(read_range));
      }
    }break;
  }
  scratch_end(scratch);
  return result;
}

internal B32
rd_eval_space_write(void *u, E_Space space, void *in, Rng1U64 range)
{
  B32 result = 0;
  switch(space.kind)
  {
    default:{}break;
    
    //- rjf: interior control entity writes (inside process address space or
    // thread register block)
    case RD_EvalSpaceKind_CtrlEntity:
    {
      CTRL_Entity *entity = rd_ctrl_entity_from_eval_space(space);
      switch(entity->kind)
      {
        default:{}break;
        case CTRL_EntityKind_Process:
        {
          result = ctrl_process_write(entity->handle, range, in);
        }break;
        case CTRL_EntityKind_Thread:
        {
          Temp scratch = scratch_begin(0, 0);
          U64 regs_size = regs_block_size_from_arch(entity->arch);
          Rng1U64 legal_range = r1u64(0, regs_size);
          Rng1U64 write_range = intersect_1u64(legal_range, range);
          U64 write_size = dim_1u64(write_range);
          void *new_regs = ctrl_query_cached_reg_block_from_thread(scratch.arena, d_state->ctrl_entity_store, entity->handle);
          MemoryCopy((U8 *)new_regs + write_range.min, in, write_size);
          result = ctrl_thread_write_reg_block(entity->handle, new_regs);
          scratch_end(scratch);
        }break;
      }
    }break;
    
    //- rjf: meta-config writes
    case RD_EvalSpaceKind_MetaCfg:
    {
      Temp scratch = scratch_begin(0, 0);
      
      // rjf: unpack
      RD_Cfg *cfg = rd_cfg_from_eval_space(space);
      String8 eval_blob = rd_eval_blob_from_cfg__cached(cfg);
      MD_Node *schema = rd_schema_from_name(scratch.arena, cfg->string);
      String8 name = cfg->string;
      E_TypeKey type_key = e_string2typekey_map_lookup(rd_state->meta_name2type_map, name);
      E_Type *type = e_type_from_key__cached(type_key);
      
      // rjf: find member to which this write applies, reflect back in the cfg tree
      if(type->members != 0) for EachIndex(member_idx, type->count)
      {
        E_Member *member = &type->members[member_idx];
        Rng1U64 member_range = r1u64(member->off, member->off + e_type_byte_size_from_key(member->type_key));
        String8 child_name = member->name;
        MD_Node *member_schema = md_child_from_string(schema, child_name, 0);
        String8 member_type_name = member_schema->first->string;
        RD_Cfg *child = rd_cfg_child_from_string(cfg, child_name);
        if((str8_match(member_type_name, str8_lit("code_string"), 0) ||
            str8_match(member_type_name, str8_lit("path"), 0) ||
            str8_match(member_type_name, str8_lit("string"), 0)) &&
           member->off+sizeof(U64) <= eval_blob.size)
        {
          U64 string_off = *(U64 *)(eval_blob.str + member->off);
          U64 string_opl = string_off + child->first->string.size + 1;
          Rng1U64 string_range = r1u64(string_off, string_opl);
          if(contains_1u64(string_range, range.min))
          {
            String8 new_string = str8_cstring_capped(in, (U8 *)in + dim_1u64(range));
            if(new_string.size == 0)
            {
              rd_cfg_release(child);
            }
            else
            {
              if(child == &rd_nil_cfg)
              {
                child = rd_cfg_new(cfg, child_name);
              }
              rd_cfg_release_all_children(child);
              rd_cfg_new(child, new_string);
            }
            result = 1;
            break;
          }
        }
        else if(str8_match(member_type_name, str8_lit("u64"), 0) && dim_1u64(range) >= 1 && contains_1u64(member_range, range.min))
        {
          U64 value = 0;
          MemoryCopy(&value, in, dim_1u64(range));
          if(value == 0)
          {
            rd_cfg_release(child);
          }
          else
          {
            if(child == &rd_nil_cfg)
            {
              child = rd_cfg_new(cfg, child_name);
            }
            rd_cfg_release_all_children(child);
            rd_cfg_newf(child, "%I64u", value);
          }
          result = 1;
          break;
        }
        else if(str8_match(member_type_name, str8_lit("bool"), 0) && dim_1u64(range) >= 1 && contains_1u64(member_range, range.min))
        {
          U64 value = 0;
          MemoryCopy(&value, in, dim_1u64(range));
          if(value == 0)
          {
            rd_cfg_release(child);
          }
          else
          {
            if(child == &rd_nil_cfg)
            {
              child = rd_cfg_new(cfg, child_name);
            }
            rd_cfg_release_all_children(child);
            rd_cfg_newf(child, "%I64u", value);
          }
          result = 1;
          break;
        }
      }
      
      // rjf: commit to the eval blob cache
      {
        RD_Cfg2EvalBlobMap *map = rd_state->cfg2evalblob_map;
        RD_Handle handle = rd_handle_from_cfg(cfg);
        U64 hash = d_hash_from_string(str8_struct(&handle));
        U64 slot_idx = hash%map->slots_count;
        
        // rjf: cfg -> cached node
        RD_Cfg2EvalBlobNode *node = 0;
        for(RD_Cfg2EvalBlobNode *n = map->slots[slot_idx].first; n != 0; n = n->next)
        {
          if(rd_handle_match(handle, n->handle))
          {
            node = n;
            break;
          }
        }
        
        // rjf: if node -> commit
        if(node)
        {
          node->blob = rd_eval_blob_from_cfg(rd_frame_arena(), cfg);
        }
      }
      
      scratch_end(scratch);
    }break;
    
    case RD_EvalSpaceKind_MetaCtrlEntity:
    {
#if 0  // TODO(rjf): @cfg
      Temp scratch = scratch_begin(0, 0);
      
      // rjf: get entity, produce meta-eval
      CTRL_Entity *entity = rd_ctrl_entity_from_eval_space(space);
      CTRL_MetaEval *meval = rd_ctrl_meta_eval_from_ctrl_entity(scratch.arena, entity);
      
      // rjf: copy meta evaluation to scratch arena, to form range of legal reads
      arena_push(scratch.arena, 0, 64);
      String8 meval_srlzed = serialized_from_struct(scratch.arena, CTRL_MetaEval, meval);
      U64 pos_min = arena_pos(scratch.arena);
      CTRL_MetaEval *meval_read = struct_from_serialized(scratch.arena, CTRL_MetaEval, meval_srlzed);
      U64 pos_opl = arena_pos(scratch.arena);
      
      // rjf: rebase all pointer values in meta evaluation to be relative to base pointer
      struct_rebase_ptrs(CTRL_MetaEval, meval_read, meval_read);
      
      // rjf: perform write to entity
      if(0){}
#define FlatMemberCase(name) else if(range.min == OffsetOf(CTRL_MetaEval, name) && dim_1u64(range) <= sizeof(meval_read->name))
#define StringMemberCase(name) else if(range.min == (U64)meval_read->name.str)
      StringMemberCase(label) {result = 1; ctrl_entity_equip_string(d_state->ctrl_entity_store, entity, str8_cstring_capped(in, (U8 *)in + 4096));}
#undef FlatMemberCase
#undef StringMemberCase
      scratch_end(scratch);
#endif
    }break;
  }
  return result;
}

//- rjf: asynchronous streamed reads -> hashes from spaces

internal U128
rd_key_from_eval_space_range(E_Space space, Rng1U64 range, B32 zero_terminated)
{
  U128 result = {0};
  switch(space.kind)
  {
    case E_SpaceKind_HashStoreKey:
    {
      result = space.u128;
    }break;
    case RD_EvalSpaceKind_CtrlEntity:
    {
      CTRL_Entity *entity = rd_ctrl_entity_from_eval_space(space);
      if(entity->kind == CTRL_EntityKind_Process)
      {
        result = ctrl_hash_store_key_from_process_vaddr_range(entity->handle, range, zero_terminated);
      }
    }break;
  }
  return result;
}

//- rjf: space -> entire range

internal Rng1U64
rd_whole_range_from_eval_space(E_Space space)
{
  Rng1U64 result = {0};
  switch(space.kind)
  {
    case E_SpaceKind_HashStoreKey:
    {
      HS_Scope *scope = hs_scope_open();
      U128 hash = {0};
      for(U64 idx = 0; idx < 2; idx += 1)
      {
        hash = hs_hash_from_key(space.u128, idx);
        if(!u128_match(hash, u128_zero()))
        {
          break;
        }
      }
      String8 data = hs_data_from_hash(scope, hash);
      result = r1u64(0, data.size);
      hs_scope_close(scope);
    }break;
    case RD_EvalSpaceKind_CtrlEntity:
    {
      CTRL_Entity *entity = rd_ctrl_entity_from_eval_space(space);
      if(entity->kind == CTRL_EntityKind_Process)
      {
        result = r1u64(0, 0x7FFFFFFFFFFFull);
      }
    }break;
  }
  return result;
}

////////////////////////////////
//~ rjf: Evaluation View Visualization & Interaction

//- rjf: writing values back to child processes

internal B32
rd_commit_eval_value_string(E_Eval dst_eval, String8 string, B32 string_needs_unescaping)
{
  B32 result = 0;
  if(dst_eval.mode == E_Mode_Offset)
  {
    Temp scratch = scratch_begin(0, 0);
    E_TypeKey type_key = e_type_unwrap(dst_eval.type_key);
    E_TypeKind type_kind = e_type_kind_from_key(type_key);
    E_TypeKey direct_type_key = e_type_unwrap(e_type_direct_from_key(e_type_unwrap(dst_eval.type_key)));
    E_TypeKind direct_type_kind = e_type_kind_from_key(direct_type_key);
    String8 commit_data = {0};
    B32 commit_at_ptr_dest = 0;
    if((E_TypeKind_FirstBasic <= type_kind && type_kind <= E_TypeKind_LastBasic) ||
       type_kind == E_TypeKind_Enum)
    {
      E_Eval src_eval = e_eval_from_string(scratch.arena, string);
      commit_data = push_str8_copy(scratch.arena, str8_struct(&src_eval.value));
      commit_data.size = Min(commit_data.size, e_type_byte_size_from_key(type_key));
    }
    else if(type_kind == E_TypeKind_Ptr || type_kind == E_TypeKind_Array)
    {
      E_Eval src_eval = e_eval_from_string(scratch.arena, string);
      E_Eval src_eval_value = e_value_eval_from_eval(src_eval);
      E_TypeKind src_eval_value_type_kind = e_type_kind_from_key(src_eval_value.type_key);
      if(direct_type_kind == E_TypeKind_Char8 ||
         direct_type_kind == E_TypeKind_UChar8 ||
         e_type_kind_is_integer(direct_type_kind))
      {
        B32 is_quoted = 0;
        if(string_needs_unescaping)
        {
          if(string.size >= 1 && string.str[0] == '"')
          {
            string = str8_skip(string, 1);
            is_quoted = 1;
          }
          if(string.size >= 1 && string.str[string.size-1] == '"')
          {
            string = str8_chop(string, 1);
          }
        }
        if(is_quoted)
        {
          commit_data = raw_from_escaped_str8(scratch.arena, string);
        }
        else
        {
          commit_data = push_str8_copy(scratch.arena, string);
        }
        commit_data.size += 1;
        if(type_kind == E_TypeKind_Ptr)
        {
          commit_at_ptr_dest = 1;
        }
      }
      else if(type_kind == E_TypeKind_Ptr &&
              (e_type_kind_is_pointer_or_ref(src_eval_value_type_kind) ||
               e_type_kind_is_integer(src_eval_value_type_kind)) &&
              src_eval_value.mode == E_Mode_Value)
      {
        commit_data = push_str8_copy(scratch.arena, str8_struct(&src_eval.value));
        commit_data.size = Min(commit_data.size, e_type_byte_size_from_key(src_eval.type_key));
        commit_data.size = Min(commit_data.size, e_type_byte_size_from_key(type_key));
      }
    }
    if(commit_data.size != 0 && e_type_byte_size_from_key(type_key) != 0)
    {
      U64 dst_offset = dst_eval.value.u64;
      if(dst_eval.mode == E_Mode_Offset && commit_at_ptr_dest)
      {
        E_Eval dst_value_eval = e_value_eval_from_eval(dst_eval);
        dst_offset = dst_value_eval.value.u64;
      }
      result = e_space_write(dst_eval.space, commit_data.str, r1u64(dst_offset, dst_offset + commit_data.size));
    }
    scratch_end(scratch);
  }
  return result;
}

//- rjf: view rule config tree info extraction

internal U64
rd_base_offset_from_eval(E_Eval eval)
{
  if(e_type_kind_is_pointer_or_ref(e_type_kind_from_key(eval.type_key)))
  {
    eval = e_value_eval_from_eval(eval);
  }
  return eval.value.u64;
}

internal E_Value
rd_value_from_params_key(MD_Node *params, String8 key)
{
  Temp scratch = scratch_begin(0, 0);
  MD_Node *key_node = md_child_from_string(params, key, 0);
  String8 expr = md_string_from_children(scratch.arena, key_node);
  E_Eval eval = e_eval_from_string(scratch.arena, expr);
  E_Eval value_eval = e_value_eval_from_eval(eval);
  scratch_end(scratch);
  return value_eval.value;
}

internal Rng1U64
rd_range_from_eval_params(E_Eval eval, MD_Node *params)
{
  Temp scratch = scratch_begin(0, 0);
  U64 size = rd_value_from_params_key(params, str8_lit("size")).u64;
  E_TypeKey type_key = e_type_unwrap(eval.type_key);
  E_TypeKind type_kind = e_type_kind_from_key(type_key);
  E_TypeKey direct_type_key = e_type_unwrap(e_type_direct_from_key(eval.type_key));
  E_TypeKind direct_type_kind = e_type_kind_from_key(direct_type_key);
  if(size == 0 && e_type_kind_is_pointer_or_ref(type_kind) && (direct_type_kind == E_TypeKind_Struct ||
                                                               direct_type_kind == E_TypeKind_Union ||
                                                               direct_type_kind == E_TypeKind_Class ||
                                                               direct_type_kind == E_TypeKind_Array))
  {
    size = e_type_byte_size_from_key(e_type_direct_from_key(e_type_unwrap(eval.type_key)));
  }
  if(size == 0 && eval.mode == E_Mode_Offset && (type_kind == E_TypeKind_Struct ||
                                                 type_kind == E_TypeKind_Union ||
                                                 type_kind == E_TypeKind_Class ||
                                                 type_kind == E_TypeKind_Array))
  {
    size = e_type_byte_size_from_key(e_type_unwrap(eval.type_key));
  }
  if(size == 0)
  {
    size = KB(16);
  }
  Rng1U64 result = {0};
  result.min = rd_base_offset_from_eval(eval);
  result.max = result.min + size;
  scratch_end(scratch);
  return result;
}

internal TXT_LangKind
rd_lang_kind_from_eval_params(E_Eval eval, MD_Node *params)
{
  TXT_LangKind lang_kind = TXT_LangKind_Null;
  if(eval.expr->kind == E_ExprKind_LeafFilePath)
  {
    lang_kind = txt_lang_kind_from_extension(str8_skip_last_dot(eval.expr->string));
  }
  else
  {
    MD_Node *lang_node = md_child_from_string(params, str8_lit("lang"), 0);
    String8 lang_kind_string = lang_node->first->string;
    lang_kind = txt_lang_kind_from_extension(lang_kind_string);
  }
  return lang_kind;
}

internal Arch
rd_arch_from_eval_params(E_Eval eval, MD_Node *params)
{
  Arch arch = Arch_Null;
  MD_Node *arch_node = md_child_from_string(params, str8_lit("arch"), 0);
  String8 arch_kind_string = arch_node->first->string;
  if(str8_match(arch_kind_string, str8_lit("x64"), StringMatchFlag_CaseInsensitive))
  {
    arch = Arch_x64;
  }
  return arch;
}

internal Vec2S32
rd_dim2s32_from_eval_params(E_Eval eval, MD_Node *params)
{
  Vec2S32 dim = v2s32(1, 1);
  {
    dim.x = rd_value_from_params_key(params, str8_lit("w")).s32;
    dim.y = rd_value_from_params_key(params, str8_lit("h")).s32;
  }
  return dim;
}

internal R_Tex2DFormat
rd_tex2dformat_from_eval_params(E_Eval eval, MD_Node *params)
{
  R_Tex2DFormat result = R_Tex2DFormat_RGBA8;
  {
    MD_Node *fmt_node = md_child_from_string(params, str8_lit("fmt"), 0);
    for EachNonZeroEnumVal(R_Tex2DFormat, fmt)
    {
      if(str8_match(r_tex2d_format_display_string_table[fmt], fmt_node->first->string, StringMatchFlag_CaseInsensitive))
      {
        result = fmt;
        break;
      }
    }
  }
  return result;
}

//- rjf: eval <-> file path

internal String8
rd_file_path_from_eval_string(Arena *arena, String8 string)
{
  String8 result = {0};
  {
    Temp scratch = scratch_begin(&arena, 1);
    E_Eval eval = e_eval_from_string(scratch.arena, string);
    if(eval.expr->kind == E_ExprKind_LeafFilePath)
    {
      result = raw_from_escaped_str8(arena, eval.expr->string);
    }
    scratch_end(scratch);
  }
  return result;
}

internal String8
rd_eval_string_from_file_path(Arena *arena, String8 string)
{
  Temp scratch = scratch_begin(&arena, 1);
  String8 string_escaped = escaped_from_raw_str8(scratch.arena, string);
  String8 result = push_str8f(arena, "file:\"%S\"", string_escaped);
  scratch_end(scratch);
  return result;
}

////////////////////////////////
//~ rjf: View Functions

internal RD_ViewState *
rd_view_state_from_cfg(RD_Cfg *cfg)
{
  RD_Handle cfg_handle = rd_handle_from_cfg(cfg);
  U64 hash = d_hash_from_string(str8_struct(&cfg_handle));
  U64 slot_idx = hash%rd_state->view_state_slots_count;
  RD_ViewStateSlot *slot = &rd_state->view_state_slots[slot_idx];
  RD_ViewState *view_state = &rd_nil_view_state;
  for(RD_ViewState *v = slot->first; v != 0; v = v->hash_next)
  {
    if(rd_handle_match(v->cfg_handle, cfg_handle))
    {
      view_state = v;
      break;
    }
  }
  if(view_state == &rd_nil_view_state)
  {
    view_state = rd_state->free_view_state;
    if(view_state)
    {
      SLLStackPop_N(rd_state->free_view_state, hash_next);
    }
    else
    {
      view_state = push_array(rd_state->arena, RD_ViewState, 1);
    }
    MemoryCopyStruct(view_state, &rd_nil_view_state);
    DLLPushBack_NP(slot->first, slot->last, view_state, hash_next, hash_prev);
    view_state->cfg_handle = cfg_handle;
    view_state->arena = arena_alloc();
    view_state->ev_view = ev_view_alloc();
    view_state->loading_t = 1.f;
  }
  if(view_state != &rd_nil_view_state)
  {
    view_state->last_frame_index_touched = rd_state->frame_index;
  }
  return view_state;
}

internal DR_FancyStringList
rd_title_fstrs_from_view(Arena *arena, String8 viewer_name_string, String8 query, Vec4F32 primary_color, Vec4F32 secondary_color, F32 size)
{
  DR_FancyStringList result = {0};
  Temp scratch = scratch_begin(&arena, 1);
  String8 file_path = rd_file_path_from_eval_string(scratch.arena, query);
  
  //- rjf: query is file path - do specific file name strings
  if(file_path.size != 0)
  {
    // rjf: compute disambiguated file name
    String8List qualifiers = {0};
    String8 file_name = str8_skip_last_slash(file_path);
    if(rd_state->ambiguous_path_slots_count != 0)
    {
      U64 hash = d_hash_from_string__case_insensitive(file_name);
      U64 slot_idx = hash%rd_state->ambiguous_path_slots_count;
      RD_AmbiguousPathNode *node = 0;
      {
        for(RD_AmbiguousPathNode *n = rd_state->ambiguous_path_slots[slot_idx];
            n != 0;
            n = n->next)
        {
          if(str8_match(n->name, file_name, StringMatchFlag_CaseInsensitive))
          {
            node = n;
            break;
          }
        }
      }
      if(node != 0 && node->paths.node_count > 1)
      {
        // rjf: get all colliding paths
        String8Array collisions = str8_array_from_list(scratch.arena, &node->paths);
        
        // rjf: get all reversed path parts for each collision
        String8List *collision_parts_reversed = push_array(scratch.arena, String8List, collisions.count);
        for EachIndex(idx, collisions.count)
        {
          String8List parts = str8_split_path(scratch.arena, collisions.v[idx]);
          for(String8Node *n = parts.first; n != 0; n = n->next)
          {
            str8_list_push_front(scratch.arena, &collision_parts_reversed[idx], n->string);
          }
        }
        
        // rjf: get the search path & its reversed parts
        String8List parts = str8_split_path(scratch.arena, file_path);
        String8List parts_reversed = {0};
        for(String8Node *n = parts.first; n != 0; n = n->next)
        {
          str8_list_push_front(scratch.arena, &parts_reversed, n->string);
        }
        
        // rjf: iterate all collision part reversed lists, in lock-step with
        // search path; disqualify until we only have one path remaining; gather
        // qualifiers
        {
          U64 num_collisions_left = collisions.count;
          String8Node **collision_nodes = push_array(scratch.arena, String8Node *, collisions.count);
          for EachIndex(idx, collisions.count)
          {
            collision_nodes[idx] = collision_parts_reversed[idx].first;
          }
          for(String8Node *n = parts_reversed.first; num_collisions_left > 1 && n != 0; n = n->next)
          {
            B32 part_is_qualifier = 0;
            for EachIndex(idx, collisions.count)
            {
              if(collision_nodes[idx] != 0 && !str8_match(collision_nodes[idx]->string, n->string, StringMatchFlag_CaseInsensitive))
              {
                collision_nodes[idx] = 0;
                num_collisions_left -= 1;
                part_is_qualifier = 1;
              }
              else if(collision_nodes[idx] != 0)
              {
                collision_nodes[idx] = collision_nodes[idx]->next;
              }
            }
            if(part_is_qualifier)
            {
              str8_list_push_front(scratch.arena, &qualifiers, n->string);
            }
          }
        }
      }
    }
    
    // rjf: push qualifiers
    for(String8Node *n = qualifiers.first; n != 0; n = n->next)
    {
      String8 string = push_str8f(arena, "<%S> ", n->string);
      dr_fancy_string_list_push_new(arena, &result, rd_font_from_slot(RD_FontSlot_Main), size*0.95f, secondary_color, string);
    }
    
    // rjf: push file name
    DR_FancyString fstr =
    {
      rd_font_from_slot(RD_FontSlot_Main),
      push_str8_copy(arena, file_name),
      primary_color,
      size,
    };
    dr_fancy_string_list_push(arena, &result, &fstr);
  }
  
  //- rjf: query is not file path - do general case, for view rule & expression
  else
  {
    DR_FancyString fstr1 =
    {
      rd_font_from_slot(RD_FontSlot_Main),
      viewer_name_string,
      primary_color,
      size,
    };
    dr_fancy_string_list_push(arena, &result, &fstr1);
    if(query.size != 0)
    {
      DR_FancyString fstr2 =
      {
        rd_font_from_slot(RD_FontSlot_Code),
        str8_lit(" "),
        primary_color,
        size,
      };
      dr_fancy_string_list_push(arena, &result, &fstr2);
      DR_FancyString fstr3 =
      {
        rd_font_from_slot(RD_FontSlot_Code),
        push_str8_copy(arena, query),
        secondary_color,
        size*0.8f,
      };
      dr_fancy_string_list_push(arena, &result, &fstr3);
    }
  }
  scratch_end(scratch);
  return result;
}

////////////////////////////////
//~ rjf: View Building API

//- rjf: view info extraction

internal Arena *
rd_view_arena(void)
{
  RD_Cfg *view = rd_cfg_from_handle(rd_regs()->view);
  RD_ViewState *view_state = rd_view_state_from_cfg(view);
  return view_state->arena;
}

internal UI_ScrollPt2
rd_view_scroll_pos(void)
{
  RD_Cfg *view = rd_cfg_from_handle(rd_regs()->view);
  RD_ViewState *view_state = rd_view_state_from_cfg(view);
  return view_state->scroll_pos;
}

internal EV_View *
rd_view_eval_view(void)
{
  RD_Cfg *view = rd_cfg_from_handle(rd_regs()->view);
  RD_ViewState *view_state = rd_view_state_from_cfg(view);
  return view_state->ev_view;
}

internal String8
rd_view_expr_string(void)
{
  RD_Cfg *view = rd_cfg_from_handle(rd_regs()->view);
  RD_Cfg *expr = rd_cfg_child_from_string(view, str8_lit("expression"));
  String8 expr_string = expr->first->string;
  return expr_string;
}

internal String8
rd_view_filter(void)
{
  RD_Cfg *view = rd_cfg_from_handle(rd_regs()->view);
  RD_Cfg *filter = rd_cfg_child_from_string(view, str8_lit("filter"));
  String8 filter_string = filter->first->string;
  return filter_string;
}

//- rjf: pushing/attaching view resources

internal void *
rd_view_state_by_size(U64 size)
{
  RD_Cfg *view = rd_cfg_from_handle(rd_regs()->view);
  RD_ViewState *view_state = rd_view_state_from_cfg(view);
  if(view_state->user_data == 0)
  {
    view_state->user_data = push_array(view_state->arena, U8, size);
  }
  return view_state->user_data;
}

internal Arena *
rd_push_view_arena(void)
{
  RD_Cfg *view = rd_cfg_from_handle(rd_regs()->view);
  RD_ViewState *view_state = rd_view_state_from_cfg(view);
  RD_ArenaExt *ext = push_array(view_state->arena, RD_ArenaExt, 1);
  ext->arena = arena_alloc();
  SLLQueuePush(view_state->first_arena_ext, view_state->last_arena_ext, ext);
  return ext->arena;
}

//- rjf: storing view-attached state

internal void
rd_store_view_expr_string(String8 string)
{
  RD_Cfg *view = rd_cfg_from_handle(rd_regs()->view);
  RD_Cfg *expr = rd_cfg_child_from_string_or_alloc(view, str8_lit("expression"));
  rd_cfg_release_all_children(expr);
  rd_cfg_new(expr, string);
}

internal void
rd_store_view_filter(String8 string)
{
  RD_Cfg *view = rd_cfg_from_handle(rd_regs()->view);
  RD_Cfg *filter = rd_cfg_child_from_string_or_alloc(view, str8_lit("filter"));
  rd_cfg_release_all_children(filter);
  rd_cfg_new(filter, string);
}

internal void
rd_store_view_loading_info(B32 is_loading, U64 progress_u64, U64 progress_u64_target)
{
  RD_Cfg *view = rd_cfg_from_handle(rd_regs()->view);
  RD_ViewState *view_state = rd_view_state_from_cfg(view);
  view_state->loading_t_target = (F32)!!is_loading;
  view_state->loading_progress_v = progress_u64;
  view_state->loading_progress_v_target = progress_u64_target;
}

internal void
rd_store_view_scroll_pos(UI_ScrollPt2 pos)
{
  RD_Cfg *view = rd_cfg_from_handle(rd_regs()->view);
  RD_ViewState *view_state = rd_view_state_from_cfg(view);
  view_state->scroll_pos = pos;
}

internal void
rd_store_view_param(String8 key, String8 value)
{
  RD_Cfg *view = rd_cfg_from_handle(rd_regs()->view);
  RD_Cfg *child = rd_cfg_child_from_string_or_alloc(view, key);
  rd_cfg_release_all_children(child);
  rd_cfg_new(child, value);
}

internal void
rd_store_view_paramf(String8 key, char *fmt, ...)
{
  Temp scratch = scratch_begin(0, 0);
  va_list args;
  va_start(args, fmt);
  String8 string = push_str8fv(scratch.arena, fmt, args);
  rd_store_view_param(key, string);
  va_end(args);
  scratch_end(scratch);
}

////////////////////////////////
//~ rjf: Window Functions

internal RD_Cfg *
rd_window_from_cfg(RD_Cfg *cfg)
{
  RD_Cfg *result = &rd_nil_cfg;
  for(RD_Cfg *c = cfg; c != &rd_nil_cfg; c = c->parent)
  {
    if(c->parent->parent == rd_state->root_cfg && str8_match(c->string, str8_lit("window"), 0))
    {
      result = c;
      break;
    }
  }
  return result;
}

internal RD_WindowState *
rd_window_state_from_cfg(RD_Cfg *cfg)
{
  //- rjf: unpack
  RD_Cfg *window_cfg = rd_window_from_cfg(cfg);
  RD_Handle handle = rd_handle_from_cfg(window_cfg);
  U64 hash = d_hash_from_string(str8_struct(&handle));
  U64 slot_idx = hash%rd_state->window_state_slots_count;
  RD_WindowStateSlot *slot = &rd_state->window_state_slots[slot_idx];
  
  //- rjf: scan for existing window
  RD_WindowState *ws = &rd_nil_window_state;
  for(RD_WindowState *w = slot->first; w != 0; w = w->hash_next)
  {
    if(rd_handle_match(w->cfg_handle, handle))
    {
      ws = w;
      break;
    }
  }
  
  //- rjf: allocate/open new window if one was not found
  if(window_cfg != &rd_nil_cfg && ws == &rd_nil_window_state)
  {
    Temp scratch = scratch_begin(0, 0);
    
    // rjf: unpack configuration options
    Vec2F32 size = {0};
    OS_Handle preferred_monitor = {0};
    {
      RD_Cfg *size_cfg = rd_cfg_child_from_string(window_cfg, str8_lit("size"));
      RD_Cfg *monitor_cfg = rd_cfg_child_from_string(window_cfg, str8_lit("monitor"));
      size.x = (F32)f64_from_str8(size_cfg->first->string);
      size.y = (F32)f64_from_str8(size_cfg->first->next->string);
      OS_HandleArray monitors = os_push_monitors_array(scratch.arena);
      for EachIndex(idx, monitors.count)
      {
        String8 monitor_name = os_name_from_monitor(scratch.arena, monitors.v[idx]);
        if(str8_match(monitor_name, monitor_cfg->first->string, StringMatchFlag_CaseInsensitive))
        {
          preferred_monitor = monitors.v[idx];
          break;
        }
      }
    }
    
    // rjf: allocate window
    ws = rd_state->free_window_state;
    if(ws != 0)
    {
      SLLStackPop_N(rd_state->free_window_state, order_next);
    }
    else
    {
      ws = push_array_no_zero(rd_state->arena, RD_WindowState, 1);
    }
    MemoryZeroStruct(ws);
    
    // rjf: fill out window
    ws->cfg_handle = handle;
    ws->arena = arena_alloc();
    {
      String8 title = str8_lit_comp(BUILD_TITLE_STRING_LITERAL);
      ws->os = os_window_open(size, OS_WindowFlag_CustomBorder, title);
    }
    ws->r = r_window_equip(ws->os);
    ws->ui = ui_state_alloc();
    ws->ctx_menu_arena = arena_alloc();
    ws->ctx_menu_regs = push_array(ws->ctx_menu_arena, RD_Regs, 1);
    ws->ctx_menu_input_buffer_size = KB(4);
    ws->ctx_menu_input_buffer = push_array(ws->arena, U8, ws->ctx_menu_input_buffer_size);
    ws->drop_completion_arena = arena_alloc();
    ws->hover_eval_arena = arena_alloc();
    ws->last_dpi = os_dpi_from_window(ws->os);
    OS_Handle zero_monitor = {0};
    if(!os_handle_match(zero_monitor, preferred_monitor))
    {
      os_window_set_monitor(ws->os, preferred_monitor);
    }
    if(rd_cfg_child_from_string(window_cfg, str8_lit("fullscreen")) != &rd_nil_cfg)
    {
      os_window_set_fullscreen(ws->os, 1);
    }
    if(rd_cfg_child_from_string(window_cfg, str8_lit("maximized")) != &rd_nil_cfg)
    {
      os_window_set_maximized(ws->os, 1);
    }
    
    // rjf: hook up window links
    DLLPushBack_NPZ(&rd_nil_window_state, rd_state->first_window_state, rd_state->last_window_state, ws, order_next, order_prev);
    DLLPushBack_NP(slot->first, slot->last, ws, hash_next, hash_prev);
    
    scratch_end(scratch);
  }
  
  //- rjf: touch window for this frame
  if(ws != &rd_nil_window_state)
  {
    ws->last_frame_index_touched = rd_state->frame_index;
  }
  
  return ws;
}

internal RD_WindowState *
rd_window_state_from_os_handle(OS_Handle os)
{
  RD_WindowState *ws = &rd_nil_window_state;
  {
    for(RD_WindowState *w = rd_state->first_window_state;
        w != &rd_nil_window_state;
        w = w->order_next)
    {
      if(os_handle_match(w->os, os))
      {
        ws = w;
        break;
      }
    }
  }
  return ws;
}

#if COMPILER_MSVC && !BUILD_DEBUG
#pragma optimize("", off)
#endif

internal void
rd_window_frame(void)
{
  Temp scratch = scratch_begin(0, 0);
  ProfBeginFunction();
  
  //////////////////////////////
  //- rjf: unpack context
  //
  RD_Cfg *window          = rd_cfg_from_handle(rd_regs()->window);
  RD_WindowState *ws      = rd_window_state_from_cfg(rd_cfg_from_handle(rd_regs()->window));
  RD_PanelTree panel_tree = rd_panel_tree_from_cfg(scratch.arena, window);
  B32 window_is_focused   = (os_window_is_focused(ws->os) || ws->window_temporarily_focused_ipc);
  B32 popup_is_open       = (rd_state->popup_active);
  B32 query_is_open       = (ws->top_query_lister != 0);
  B32 hover_eval_is_open  = (!popup_is_open &&
                             ws->hover_eval_string.size != 0 &&
                             ws->hover_eval_first_frame_idx+20 < ws->hover_eval_last_frame_idx &&
                             rd_state->frame_index-ws->hover_eval_last_frame_idx < 20);
  if(!window_is_focused || popup_is_open)
  {
    ws->menu_bar_key_held = 0;
  }
  ws->window_temporarily_focused_ipc = 0;
  ui_select_state(ws->ui);
  
  //////////////////////////////
  //- rjf: pre-emptively rasterize common glyphs on the first frame
  //
  if(rd_state->first_window_state == ws && rd_state->last_window_state == ws && ws->frames_alive == 0)
  {
    RD_FontSlot english_font_slots[] = {RD_FontSlot_Main, RD_FontSlot_Code};
    RD_FontSlot icon_font_slot = RD_FontSlot_Icons;
    for(U64 idx = 0; idx < ArrayCount(english_font_slots); idx += 1)
    {
      Temp scratch = scratch_begin(0, 0);
      RD_FontSlot slot = english_font_slots[idx];
      String8 sample_text = str8_lit("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890~!@#$%^&*()-_+=[{]}\\|;:'\",<.>/?");
      fnt_push_run_from_string(scratch.arena,
                               rd_font_from_slot(slot),
                               rd_font_size_from_slot(RD_FontSlot_Code),
                               0, 0, 0,
                               sample_text);
      fnt_push_run_from_string(scratch.arena,
                               rd_font_from_slot(slot),
                               rd_font_size_from_slot(RD_FontSlot_Main),
                               0, 0, 0,
                               sample_text);
      scratch_end(scratch);
    }
    for(RD_IconKind icon_kind = RD_IconKind_Null; icon_kind < RD_IconKind_COUNT; icon_kind = (RD_IconKind)(icon_kind+1))
    {
      Temp scratch = scratch_begin(0, 0);
      fnt_push_run_from_string(scratch.arena,
                               rd_font_from_slot(icon_font_slot),
                               rd_font_size_from_slot(icon_font_slot),
                               0, 0, FNT_RasterFlag_Smooth,
                               rd_icon_kind_text_table[icon_kind]);
      fnt_push_run_from_string(scratch.arena,
                               rd_font_from_slot(icon_font_slot),
                               rd_font_size_from_slot(RD_FontSlot_Main),
                               0, 0, FNT_RasterFlag_Smooth,
                               rd_icon_kind_text_table[icon_kind]);
      fnt_push_run_from_string(scratch.arena,
                               rd_font_from_slot(icon_font_slot),
                               rd_font_size_from_slot(RD_FontSlot_Code),
                               0, 0, FNT_RasterFlag_Smooth,
                               rd_icon_kind_text_table[icon_kind]);
      scratch_end(scratch);
    }
  }
  
  //////////////////////////////
  //- rjf: fill panel/view interaction registers
  //
  rd_regs()->panel = rd_handle_from_cfg(panel_tree.focused->cfg);
  rd_regs()->view  = rd_handle_from_cfg(panel_tree.focused->selected_tab);
  
  //////////////////////////////
  //- rjf: compute ui palettes from theme
  //
  {
    RD_Theme *current = rd_state->theme;
    for EachEnumVal(RD_PaletteCode, code)
    {
      ws->cfg_palettes[code].null       = v4f32(1, 0, 1, 1);
      ws->cfg_palettes[code].cursor     = current->colors[RD_ThemeColor_Cursor];
      ws->cfg_palettes[code].selection  = current->colors[RD_ThemeColor_SelectionOverlay];
    }
    ws->cfg_palettes[RD_PaletteCode_Base].background = current->colors[RD_ThemeColor_BaseBackground];
    ws->cfg_palettes[RD_PaletteCode_Base].text       = current->colors[RD_ThemeColor_Text];
    ws->cfg_palettes[RD_PaletteCode_Base].text_weak  = current->colors[RD_ThemeColor_TextWeak];
    ws->cfg_palettes[RD_PaletteCode_Base].border     = current->colors[RD_ThemeColor_BaseBorder];
    ws->cfg_palettes[RD_PaletteCode_MenuBar].background = current->colors[RD_ThemeColor_MenuBarBackground];
    ws->cfg_palettes[RD_PaletteCode_MenuBar].text       = current->colors[RD_ThemeColor_Text];
    ws->cfg_palettes[RD_PaletteCode_MenuBar].text_weak  = current->colors[RD_ThemeColor_TextWeak];
    ws->cfg_palettes[RD_PaletteCode_MenuBar].border     = current->colors[RD_ThemeColor_MenuBarBorder];
    ws->cfg_palettes[RD_PaletteCode_Floating].background = current->colors[RD_ThemeColor_FloatingBackground];
    ws->cfg_palettes[RD_PaletteCode_Floating].text       = current->colors[RD_ThemeColor_Text];
    ws->cfg_palettes[RD_PaletteCode_Floating].text_weak  = current->colors[RD_ThemeColor_TextWeak];
    ws->cfg_palettes[RD_PaletteCode_Floating].border     = current->colors[RD_ThemeColor_FloatingBorder];
    ws->cfg_palettes[RD_PaletteCode_ImplicitButton].background = current->colors[RD_ThemeColor_ImplicitButtonBackground];
    ws->cfg_palettes[RD_PaletteCode_ImplicitButton].text       = current->colors[RD_ThemeColor_Text];
    ws->cfg_palettes[RD_PaletteCode_ImplicitButton].text_weak  = current->colors[RD_ThemeColor_TextWeak];
    ws->cfg_palettes[RD_PaletteCode_ImplicitButton].border     = current->colors[RD_ThemeColor_ImplicitButtonBorder];
    ws->cfg_palettes[RD_PaletteCode_PlainButton].background = current->colors[RD_ThemeColor_PlainButtonBackground];
    ws->cfg_palettes[RD_PaletteCode_PlainButton].text       = current->colors[RD_ThemeColor_Text];
    ws->cfg_palettes[RD_PaletteCode_PlainButton].text_weak  = current->colors[RD_ThemeColor_TextWeak];
    ws->cfg_palettes[RD_PaletteCode_PlainButton].border     = current->colors[RD_ThemeColor_PlainButtonBorder];
    ws->cfg_palettes[RD_PaletteCode_PositivePopButton].background = current->colors[RD_ThemeColor_PositivePopButtonBackground];
    ws->cfg_palettes[RD_PaletteCode_PositivePopButton].text       = current->colors[RD_ThemeColor_Text];
    ws->cfg_palettes[RD_PaletteCode_PositivePopButton].text_weak  = current->colors[RD_ThemeColor_TextWeak];
    ws->cfg_palettes[RD_PaletteCode_PositivePopButton].border     = current->colors[RD_ThemeColor_PositivePopButtonBorder];
    ws->cfg_palettes[RD_PaletteCode_NegativePopButton].background = current->colors[RD_ThemeColor_NegativePopButtonBackground];
    ws->cfg_palettes[RD_PaletteCode_NegativePopButton].text       = current->colors[RD_ThemeColor_Text];
    ws->cfg_palettes[RD_PaletteCode_NegativePopButton].text_weak  = current->colors[RD_ThemeColor_TextWeak];
    ws->cfg_palettes[RD_PaletteCode_NegativePopButton].border     = current->colors[RD_ThemeColor_NegativePopButtonBorder];
    ws->cfg_palettes[RD_PaletteCode_NeutralPopButton].background = current->colors[RD_ThemeColor_NeutralPopButtonBackground];
    ws->cfg_palettes[RD_PaletteCode_NeutralPopButton].text       = current->colors[RD_ThemeColor_Text];
    ws->cfg_palettes[RD_PaletteCode_NeutralPopButton].text_weak  = current->colors[RD_ThemeColor_TextWeak];
    ws->cfg_palettes[RD_PaletteCode_NeutralPopButton].border     = current->colors[RD_ThemeColor_NeutralPopButtonBorder];
    ws->cfg_palettes[RD_PaletteCode_ScrollBarButton].background = current->colors[RD_ThemeColor_ScrollBarButtonBackground];
    ws->cfg_palettes[RD_PaletteCode_ScrollBarButton].text       = current->colors[RD_ThemeColor_Text];
    ws->cfg_palettes[RD_PaletteCode_ScrollBarButton].text_weak  = current->colors[RD_ThemeColor_TextWeak];
    ws->cfg_palettes[RD_PaletteCode_ScrollBarButton].border     = current->colors[RD_ThemeColor_ScrollBarButtonBorder];
    ws->cfg_palettes[RD_PaletteCode_Tab].background = current->colors[RD_ThemeColor_TabBackground];
    ws->cfg_palettes[RD_PaletteCode_Tab].text       = current->colors[RD_ThemeColor_Text];
    ws->cfg_palettes[RD_PaletteCode_Tab].text_weak  = current->colors[RD_ThemeColor_TextWeak];
    ws->cfg_palettes[RD_PaletteCode_Tab].border     = current->colors[RD_ThemeColor_TabBorder];
    ws->cfg_palettes[RD_PaletteCode_TabInactive].background = current->colors[RD_ThemeColor_TabBackgroundInactive];
    ws->cfg_palettes[RD_PaletteCode_TabInactive].text       = current->colors[RD_ThemeColor_Text];
    ws->cfg_palettes[RD_PaletteCode_TabInactive].text_weak  = current->colors[RD_ThemeColor_TextWeak];
    ws->cfg_palettes[RD_PaletteCode_TabInactive].border     = current->colors[RD_ThemeColor_TabBorderInactive];
    ws->cfg_palettes[RD_PaletteCode_DropSiteOverlay].background = current->colors[RD_ThemeColor_DropSiteOverlay];
    ws->cfg_palettes[RD_PaletteCode_DropSiteOverlay].text       = current->colors[RD_ThemeColor_DropSiteOverlay];
    ws->cfg_palettes[RD_PaletteCode_DropSiteOverlay].text_weak  = current->colors[RD_ThemeColor_DropSiteOverlay];
    ws->cfg_palettes[RD_PaletteCode_DropSiteOverlay].border     = current->colors[RD_ThemeColor_DropSiteOverlay];
    if(rd_setting_b32_from_name(str8_lit("opaque_backgrounds")))
    {
      for EachEnumVal(RD_PaletteCode, code)
      {
        if(ws->cfg_palettes[code].background.x != 0 ||
           ws->cfg_palettes[code].background.y != 0 ||
           ws->cfg_palettes[code].background.z != 0)
        {
          ws->cfg_palettes[code].background.w = 1;
        }
      }
    }
  }
  
  //////////////////////////////
  //- rjf: gather listers
  //
  typedef struct ListerTask ListerTask;
  struct ListerTask
  {
    ListerTask *next;
    RD_Lister *lister;
    UI_Key root_key;
  };
  ListerTask *first_lister_task = 0;
  ListerTask *last_lister_task = 0;
  ProfScope("build all listers")
  {
    if(ws->autocomp_lister != 0 && ws->autocomp_lister_last_frame_idx+1 >= rd_state->frame_index)
    {
      ListerTask *task = push_array(scratch.arena, ListerTask, 1);
      SLLQueuePush(first_lister_task, last_lister_task, task);
      task->lister = ws->autocomp_lister;
      task->root_key = ui_key_from_stringf(ui_key_zero(), "###lister_%p", task->lister);
    }
    if(ws->top_query_lister != 0)
    {
      ListerTask *task = push_array(scratch.arena, ListerTask, 1);
      SLLQueuePush(first_lister_task, last_lister_task, task);
      task->lister = ws->top_query_lister;
      task->root_key = ui_key_from_stringf(ui_key_zero(), "###lister_%p", task->lister);
    }
  }
  
  //////////////////////////////
  //- rjf: build UI
  //
  UI_Box *lister_box = &ui_nil_box;
  UI_Box *hover_eval_box = &ui_nil_box;
  ProfScope("build UI")
  {
    ////////////////////////////
    //- rjf: set up
    //
    {
      // rjf: gather font info
      FNT_Tag main_font = rd_font_from_slot(RD_FontSlot_Main);
      F32 main_font_size = rd_font_size_from_slot(RD_FontSlot_Main);
      FNT_Tag icon_font = rd_font_from_slot(RD_FontSlot_Icons);
      
      // rjf: build icon info
      UI_IconInfo icon_info = {0};
      {
        icon_info.icon_font = icon_font;
        icon_info.icon_kind_text_map[UI_IconKind_RightArrow]     = rd_icon_kind_text_table[RD_IconKind_RightScroll];
        icon_info.icon_kind_text_map[UI_IconKind_DownArrow]      = rd_icon_kind_text_table[RD_IconKind_DownScroll];
        icon_info.icon_kind_text_map[UI_IconKind_LeftArrow]      = rd_icon_kind_text_table[RD_IconKind_LeftScroll];
        icon_info.icon_kind_text_map[UI_IconKind_UpArrow]        = rd_icon_kind_text_table[RD_IconKind_UpScroll];
        icon_info.icon_kind_text_map[UI_IconKind_RightCaret]     = rd_icon_kind_text_table[RD_IconKind_RightCaret];
        icon_info.icon_kind_text_map[UI_IconKind_DownCaret]      = rd_icon_kind_text_table[RD_IconKind_DownCaret];
        icon_info.icon_kind_text_map[UI_IconKind_LeftCaret]      = rd_icon_kind_text_table[RD_IconKind_LeftCaret];
        icon_info.icon_kind_text_map[UI_IconKind_UpCaret]        = rd_icon_kind_text_table[RD_IconKind_UpCaret];
        icon_info.icon_kind_text_map[UI_IconKind_CheckHollow]    = rd_icon_kind_text_table[RD_IconKind_CheckHollow];
        icon_info.icon_kind_text_map[UI_IconKind_CheckFilled]    = rd_icon_kind_text_table[RD_IconKind_CheckFilled];
      }
      
      // rjf: build widget palette info
      UI_WidgetPaletteInfo widget_palette_info = {0};
      {
        widget_palette_info.tooltip_palette   = rd_palette_from_code(RD_PaletteCode_Floating);
        widget_palette_info.ctx_menu_palette  = rd_palette_from_code(RD_PaletteCode_Floating);
        widget_palette_info.scrollbar_palette = rd_palette_from_code(RD_PaletteCode_ScrollBarButton);
      }
      
      // rjf: build animation info
      UI_AnimationInfo animation_info = {0};
      {
        if(rd_setting_b32_from_name(str8_lit("hover_animations")))       {animation_info.flags |= UI_AnimationInfoFlag_HotAnimations;}
        if(rd_setting_b32_from_name(str8_lit("press_animations")))       {animation_info.flags |= UI_AnimationInfoFlag_ActiveAnimations;}
        if(rd_setting_b32_from_name(str8_lit("focus_animations")))       {animation_info.flags |= UI_AnimationInfoFlag_FocusAnimations;}
        if(rd_setting_b32_from_name(str8_lit("tooltip_animations")))     {animation_info.flags |= UI_AnimationInfoFlag_TooltipAnimations;}
        if(rd_setting_b32_from_name(str8_lit("menu_animations")))        {animation_info.flags |= UI_AnimationInfoFlag_ContextMenuAnimations;}
        if(rd_setting_b32_from_name(str8_lit("scrolling_animations")))   {animation_info.flags |= UI_AnimationInfoFlag_ScrollingAnimations;}
      }
      
      // rjf: begin & push initial stack values
      ui_begin_build(ws->os, &ws->ui_events, &icon_info, &widget_palette_info, &animation_info, rd_state->frame_dt, rd_state->frame_dt);
      ui_push_font(main_font);
      ui_push_font_size(main_font_size);
      ui_push_text_padding(main_font_size*0.3f);
      ui_push_pref_width(ui_em(20.f, 1));
      ui_push_pref_height(ui_em(2.75f, 1.f));
      ui_push_palette(rd_palette_from_code(RD_PaletteCode_Base));
      ui_push_blur_size(10.f);
      FNT_RasterFlags text_raster_flags = 0;
      if(rd_setting_b32_from_name(str8_lit("smooth_main_text"))) {text_raster_flags |= FNT_RasterFlag_Smooth;}
      if(rd_setting_b32_from_name(str8_lit("hint_main_text"))) {text_raster_flags |= FNT_RasterFlag_Hinted;}
      ui_push_text_raster_flags(text_raster_flags);
    }
    
    ////////////////////////////
    //- rjf: calculate top-level rectangles
    //
    Rng2F32 window_rect = os_client_rect_from_window(ws->os);
    Vec2F32 window_rect_dim = dim_2f32(window_rect);
    Rng2F32 top_bar_rect = r2f32p(window_rect.x0, window_rect.y0, window_rect.x0+window_rect_dim.x+1, window_rect.y0+ui_top_pref_height().value);
    Rng2F32 bottom_bar_rect = r2f32p(window_rect.x0, window_rect_dim.y - ui_top_pref_height().value, window_rect.x0+window_rect_dim.x, window_rect.y0+window_rect_dim.y);
    Rng2F32 content_rect = r2f32p(window_rect.x0, top_bar_rect.y1, window_rect.x0+window_rect_dim.x, bottom_bar_rect.y0);
    F32 window_edge_px = os_dpi_from_window(ws->os)*0.035f;
    content_rect = pad_2f32(content_rect, -window_edge_px);
    
    ////////////////////////////
    //- rjf: truncated string hover
    //
    if(ui_string_hover_active()) UI_Tooltip
    {
      Temp scratch = scratch_begin(0, 0);
      String8 string = ui_string_hover_string(scratch.arena);
      DR_FancyRunList runs = ui_string_hover_runs(scratch.arena);
      UI_Box *box = ui_build_box_from_key(UI_BoxFlag_DrawText, ui_key_zero());
      ui_box_equip_display_string_fancy_runs(box, string, &runs);
      scratch_end(scratch);
    }
    
    ////////////////////////////
    //- rjf: rich hover, drag/drop tooltips
    //
    if(rd_state->hover_regs_slot != RD_RegSlot_Null ||
       (rd_state->drag_drop_regs_slot != RD_RegSlot_Null && rd_drag_is_active()))
    {
      Temp scratch = scratch_begin(0, 0);
      RD_RegSlot slot = ((rd_state->drag_drop_regs_slot != RD_RegSlot_Null && rd_drag_is_active()) ? rd_state->drag_drop_regs_slot : rd_state->hover_regs_slot);
      RD_Regs *regs = (((rd_state->drag_drop_regs_slot != RD_RegSlot_Null && rd_drag_is_active()) ? rd_state->drag_drop_regs : rd_state->hover_regs));
      CTRL_Entity *ctrl_entity = &ctrl_entity_nil;
      RD_Palette(RD_PaletteCode_Floating) switch(slot)
      {
        default:{}break;
        
        ////////////////////////
        //- rjf: cfg tooltips
        //
        case RD_RegSlot_Cfg:
        UI_Tooltip
        {
          // rjf: unpack
          RD_Cfg *cfg = rd_cfg_from_handle(regs->cfg);
          DR_FancyStringList fstrs = rd_title_fstrs_from_cfg(scratch.arena, cfg, rd_rgba_from_theme_color(RD_ThemeColor_TextWeak), ui_top_font_size());
          
          // rjf: title
          UI_PrefWidth(ui_children_sum(1)) UI_Row UI_PrefWidth(ui_text_dim(5, 1))
          {
            UI_Box *box = ui_build_box_from_key(UI_BoxFlag_DrawText, ui_key_zero());
            ui_box_equip_display_fancy_strings(box, &fstrs);
          }
        }break;
        
        ////////////////////////
        //- rjf: control entity tooltips
        //
        case RD_RegSlot_Machine:   {ctrl_entity = ctrl_entity_from_handle(d_state->ctrl_entity_store, regs->machine);     }goto ctrl_entity_tooltip;
        case RD_RegSlot_Process:   {ctrl_entity = ctrl_entity_from_handle(d_state->ctrl_entity_store, regs->process);     }goto ctrl_entity_tooltip;
        case RD_RegSlot_Module:    {ctrl_entity = ctrl_entity_from_handle(d_state->ctrl_entity_store, regs->module);      }goto ctrl_entity_tooltip;
        case RD_RegSlot_Thread:    {ctrl_entity = ctrl_entity_from_handle(d_state->ctrl_entity_store, regs->thread);      }goto ctrl_entity_tooltip;
        case RD_RegSlot_CtrlEntity:{ctrl_entity = ctrl_entity_from_handle(d_state->ctrl_entity_store, regs->ctrl_entity); }goto ctrl_entity_tooltip;
        ctrl_entity_tooltip:;
        UI_Tooltip
        {
          // rjf: unpack
          DI_Scope *di_scope = di_scope_open();
          Arch arch = ctrl_entity->arch;
          String8 arch_str = string_from_arch(arch);
          DR_FancyStringList fstrs = rd_title_fstrs_from_ctrl_entity(scratch.arena, ctrl_entity,
                                                                     rd_rgba_from_theme_color(RD_ThemeColor_TextWeak),
                                                                     ui_top_font_size(), 0);
          
          // rjf: title
          UI_PrefWidth(ui_children_sum(1)) UI_Row UI_PrefWidth(ui_text_dim(5, 1))
          {
            UI_Box *box = ui_build_box_from_key(UI_BoxFlag_DrawText, ui_key_zero());
            ui_box_equip_display_fancy_strings(box, &fstrs);
            ui_spacer(ui_em(0.5f, 1.f));
            UI_FontSize(ui_top_font_size() - 1.f)
              UI_CornerRadius(ui_top_font_size()*0.5f)
              RD_Palette(RD_PaletteCode_NeutralPopButton)
            {
              UI_FlagsAdd(UI_BoxFlag_DrawTextWeak|UI_BoxFlag_DrawBorder) ui_label(arch_str);
              ui_spacer(ui_em(0.5f, 1.f));
              if(ctrl_entity->kind == CTRL_EntityKind_Thread ||
                 ctrl_entity->kind == CTRL_EntityKind_Process)
              {
                UI_FlagsAdd(UI_BoxFlag_DrawTextWeak|UI_BoxFlag_DrawBorder) ui_labelf("ID: %i", (U32)ctrl_entity->id);
              }
            }
          }
          
          // rjf: debug info status
          if(ctrl_entity->kind == CTRL_EntityKind_Module) UI_FlagsAdd(UI_BoxFlag_DrawTextWeak)
          {
            DI_Scope *di_scope = di_scope_open();
            DI_Key dbgi_key = ctrl_dbgi_key_from_module(ctrl_entity);
            RDI_Parsed *rdi = di_rdi_from_key(di_scope, &dbgi_key, 0);
            if(rdi->raw_data_size != 0)
            {
              ui_labelf("Symbols successfully loaded from %S", dbgi_key.path);
            }
            else if(dbgi_key.path.size != 0)
            {
              ui_labelf("Symbols not found at %S", dbgi_key.path);
            }
            else if(dbgi_key.path.size == 0)
            {
              ui_labelf("Symbol information not found in module file");
            }
            di_scope_close(di_scope);
          }
          
          // rjf: unwind
          if(ctrl_entity->kind == CTRL_EntityKind_Thread)
          {
            CTRL_Entity *process = ctrl_entity_ancestor_from_kind(ctrl_entity, CTRL_EntityKind_Process);
            CTRL_Unwind base_unwind = d_query_cached_unwind_from_thread(ctrl_entity);
            CTRL_CallStack rich_unwind = ctrl_call_stack_from_unwind(scratch.arena, di_scope, process, &base_unwind);
            if(rich_unwind.concrete_frame_count != 0)
            {
              ui_spacer(ui_em(1.5f, 1.f));
            }
            for(U64 idx = 0; idx < rich_unwind.concrete_frame_count; idx += 1)
            {
              CTRL_CallStackFrame *f = &rich_unwind.frames[idx];
              RDI_Parsed *rdi = f->rdi;
              RDI_Procedure *procedure = f->procedure;
              U64 rip_vaddr = regs_rip_from_arch_block(arch, f->regs);
              CTRL_Entity *module = ctrl_module_from_process_vaddr(process, rip_vaddr);
              String8 module_name = module == &ctrl_entity_nil ? str8_lit("???") : str8_skip_last_slash(module->string);
              
              // rjf: inline frames
              for(CTRL_CallStackInlineFrame *fin = f->last_inline_frame; fin != 0; fin = fin->prev)
                UI_PrefWidth(ui_children_sum(1)) UI_Row
              {
                String8 name = {0};
                name.str = rdi_string_from_idx(rdi, fin->inline_site->name_string_idx, &name.size);
                name.size = Min(512, name.size);
                UI_TextAlignment(UI_TextAlign_Left) RD_Font(RD_FontSlot_Code) UI_FlagsAdd(UI_BoxFlag_DrawTextWeak) UI_PrefWidth(ui_em(12.f, 1)) ui_labelf("0x%I64x", rip_vaddr);
                RD_Font(RD_FontSlot_Code) UI_FlagsAdd(UI_BoxFlag_DrawTextWeak) UI_PrefWidth(ui_text_dim(10, 1)) ui_label(str8_lit("[inlined]"));
                if(name.size != 0)
                {
                  RD_Font(RD_FontSlot_Code) UI_PrefWidth(ui_text_dim(10, 1))
                  {
                    rd_code_label(1.f, 0, rd_rgba_from_theme_color(RD_ThemeColor_CodeSymbol), name);
                  }
                }
                else
                {
                  RD_Font(RD_FontSlot_Code) UI_FlagsAdd(UI_BoxFlag_DrawTextWeak) UI_PrefWidth(ui_text_dim(10, 1)) ui_labelf("[??? in %S]", module_name);
                }
              }
              
              // rjf: concrete frame
              UI_PrefWidth(ui_children_sum(1)) UI_Row
              {
                String8 name = {0};
                name.str = rdi_name_from_procedure(rdi, procedure, &name.size);
                name.size = Min(512, name.size);
                UI_TextAlignment(UI_TextAlign_Left) RD_Font(RD_FontSlot_Code) UI_FlagsAdd(UI_BoxFlag_DrawTextWeak) UI_PrefWidth(ui_em(12.f, 1)) ui_labelf("0x%I64x", rip_vaddr);
                if(name.size != 0)
                {
                  RD_Font(RD_FontSlot_Code) UI_PrefWidth(ui_text_dim(10, 1))
                  {
                    rd_code_label(1.f, 0, rd_rgba_from_theme_color(RD_ThemeColor_CodeSymbol), name);
                  }
                }
                else
                {
                  RD_Font(RD_FontSlot_Code) UI_FlagsAdd(UI_BoxFlag_DrawTextWeak) UI_PrefWidth(ui_text_dim(10, 1)) ui_labelf("[??? in %S]", module_name);
                }
              }
            }
          }
          
          di_scope_close(di_scope);
        }break;
      }
      scratch_end(scratch);
    }
    
    ////////////////////////////
    //- rjf: drag/drop visualization tooltips
    //
    if(rd_drag_is_active() && window_is_focused)
      RD_RegsScope(.window = rd_state->drag_drop_regs->window,
                   .panel = rd_state->drag_drop_regs->panel,
                   .view = rd_state->drag_drop_regs->view)
    {
      Temp scratch = scratch_begin(0, 0);
      RD_Cfg *view = rd_cfg_from_handle(rd_state->drag_drop_regs->view);
      RD_Cfg *query = rd_cfg_child_from_string(view, str8_lit("expression"));
      RD_ViewRuleInfo *view_rule_info = rd_view_rule_info_from_string(view->string);
      {
        //- rjf: tab dragging
        if(rd_state->drag_drop_regs_slot == RD_RegSlot_View && view != &rd_nil_cfg)
        {
          UI_Size main_width = ui_top_pref_width();
          UI_Size main_height = ui_top_pref_height();
          UI_TextAlign main_text_align = ui_top_text_alignment();
          RD_Palette(RD_PaletteCode_Tab)
            UI_Tooltip
            UI_PrefWidth(main_width)
            UI_PrefHeight(main_height)
            UI_TextAlignment(main_text_align)
          {
            ui_set_next_pref_width(ui_em(60.f, 1.f));
            ui_set_next_pref_height(ui_em(40.f, 1.f));
            ui_set_next_child_layout_axis(Axis2_Y);
            UI_Box *container = ui_build_box_from_key(0, ui_key_zero());
            UI_Parent(container)
            {
              UI_Row
              {
                RD_IconKind icon_kind = view_rule_info->icon_kind;
                DR_FancyStringList fstrs = rd_title_fstrs_from_view(scratch.arena, view_rule_info->display_name, query->first->string, ui_top_palette()->text, ui_top_palette()->text_weak, ui_top_font_size());
                RD_Font(RD_FontSlot_Icons)
                  UI_FontSize(rd_font_size_from_slot(RD_FontSlot_Icons))
                  UI_PrefWidth(ui_em(2.5f, 1.f))
                  UI_FlagsAdd(UI_BoxFlag_DrawTextWeak)
                  ui_label(rd_icon_kind_text_table[icon_kind]);
                UI_PrefWidth(ui_text_dim(10, 1))
                {
                  UI_Box *name_box = ui_build_box_from_key(UI_BoxFlag_DrawText, ui_key_zero());
                  ui_box_equip_display_fancy_strings(name_box, &fstrs);
                }
              }
              ui_set_next_pref_width(ui_pct(1, 0));
              ui_set_next_pref_height(ui_pct(1, 0));
              ui_set_next_child_layout_axis(Axis2_Y);
              UI_Box *view_preview_container = ui_build_box_from_stringf(UI_BoxFlag_DrawBorder|UI_BoxFlag_DrawBackground|UI_BoxFlag_Clip, "###view_preview_container");
              UI_Parent(view_preview_container) UI_Focus(UI_FocusKind_Off) UI_WidthFill
              {
                RD_ViewRuleUIFunctionType *view_ui = view_rule_info->ui;
                String8 expr = rd_view_expr_string();
                String8 params_string = rd_string_from_cfg_tree(scratch.arena, view);
                MD_Node *params = md_tree_from_string(scratch.arena, params_string)->first;
                view_ui(expr, params, view_preview_container->rect);
              }
            }
          }
        }
      }
      scratch_end(scratch);
    }
    
    ////////////////////////////
    //- rjf: developer menu
    //
    if(ws->dev_menu_is_open) RD_Font(RD_FontSlot_Code)
    {
      ui_set_next_flags(UI_BoxFlag_ViewScrollY|UI_BoxFlag_AllowOverflowY|UI_BoxFlag_ViewClamp);
      UI_PaneF(r2f32p(30, 30, 30+ui_top_font_size()*100, ui_top_font_size()*150), "###dev_ctx_menu")
      {
        //- rjf: capture
        if(!ProfIsCapturing() && ui_clicked(ui_buttonf("Begin Profiler Capture###prof_cap")))
        {
          ProfBeginCapture("raddbg");
        }
        else if(ProfIsCapturing() && ui_clicked(ui_buttonf("End Profiler Capture###prof_cap")))
        {
          ProfEndCapture();
        }
        
        //- rjf: toggles
        for(U64 idx = 0; idx < ArrayCount(DEV_toggle_table); idx += 1)
        {
          if(ui_clicked(rd_icon_button(*DEV_toggle_table[idx].value_ptr ? RD_IconKind_CheckFilled : RD_IconKind_CheckHollow, 0, DEV_toggle_table[idx].name)))
          {
            *DEV_toggle_table[idx].value_ptr ^= 1;
          }
        }
        
        ui_divider(ui_em(1.f, 1.f));
        
        //- rjf: draw match store stats
        ui_labelf("name match nodes: %I64u", rd_state->match_store->active_match_name_nodes_count);
        
        ui_divider(ui_em(1.f, 1.f));
        
        //- rjf: draw registers
        ui_labelf("hover_reg_slot: %i", rd_state->hover_regs_slot);
        struct
        {
          String8 name;
          RD_Regs *regs;
        }
        regs_info[] =
        {
          {str8_lit("regs"),       rd_regs()},
          {str8_lit("hover_regs"), rd_state->hover_regs},
        };
        for EachElement(idx, regs_info)
        {
          ui_divider(ui_em(1.f, 1.f));
          ui_label(regs_info[idx].name);
          RD_Regs *regs = regs_info[idx].regs;
#define Handle(name) ui_labelf("%s: [0x%I64x, 0x%I64x]", #name, (regs->name).u64[0], (regs->name).u64[1])
          Handle(window);
          Handle(panel);
          Handle(view);
#undef Handle
#define Handle(name) ui_labelf("%s: [0x%I64x, 0x%I64x]", #name, (regs->name).machine_id, (regs->name).dmn_handle.u64[0])
          Handle(machine);
          Handle(process);
          Handle(module);
          Handle(thread);
#undef Handle
          ui_labelf("file_path: \"%S\"", regs->file_path);
          ui_labelf("cursor: (L:%I64d, C:%I64d)", regs->cursor.line, regs->cursor.column);
          ui_labelf("mark: (L:%I64d, C:%I64d)", regs->mark.line, regs->mark.column);
          ui_labelf("unwind_count: %I64u", regs->unwind_count);
          ui_labelf("inline_depth: %I64u", regs->inline_depth);
          ui_labelf("text_key: [0x%I64x, 0x%I64x]", regs->text_key.u64[0], regs->text_key.u64[1]);
          ui_labelf("lang_kind: '%S'", txt_extension_from_lang_kind(regs->lang_kind));
          ui_labelf("vaddr_range: [0x%I64x, 0x%I64x)", regs->vaddr_range.min, regs->vaddr_range.max);
          ui_labelf("voff_range: [0x%I64x, 0x%I64x)", regs->voff_range.min, regs->voff_range.max);
        }
        
        ui_divider(ui_em(1.f, 1.f));
        
        //- rjf: draw per-window stats
        for(RD_WindowState *w = rd_state->first_window_state; w != &rd_nil_window_state; w = w->order_next)
        {
          // rjf: calc ui hash chain length
          F64 avg_ui_hash_chain_length = 0;
          {
            F64 chain_count = 0;
            F64 chain_length_sum = 0;
            for(U64 idx = 0; idx < w->ui->box_table_size; idx += 1)
            {
              F64 chain_length = 0;
              for(UI_Box *b = w->ui->box_table[idx].hash_first; !ui_box_is_nil(b); b = b->hash_next)
              {
                chain_length += 1;
              }
              if(chain_length > 0)
              {
                chain_length_sum += chain_length;
                chain_count += 1;
              }
            }
            avg_ui_hash_chain_length = chain_length_sum / chain_count;
          }
          ui_labelf("Target Hz: %.2f", 1.f/rd_state->frame_dt);
          ui_labelf("Ctrl Run Index: %I64u", ctrl_run_gen());
          ui_labelf("Ctrl Mem Gen Index: %I64u", ctrl_mem_gen());
          ui_labelf("Window %p", w);
          ui_set_next_pref_width(ui_children_sum(1));
          ui_set_next_pref_height(ui_children_sum(1));
          UI_Row
          {
            ui_spacer(ui_em(2.f, 1.f));
            ui_labelf("Box Count: %I64u", w->ui->last_build_box_count);
          }
          ui_set_next_pref_width(ui_children_sum(1));
          ui_set_next_pref_height(ui_children_sum(1));
          UI_Row
          {
            ui_spacer(ui_em(2.f, 1.f));
            ui_labelf("Average UI Hash Chain Length: %f", avg_ui_hash_chain_length);
          }
        }
        
        ui_divider(ui_em(1.f, 1.f));
      }
    }
    
    ////////////////////////////
    //- rjf: do query lister stack controls
    //
    if(ws->top_query_lister != 0)
    {
      if(ui_slot_press(UI_EventActionSlot_Cancel))
      {
        rd_cmd(RD_CmdKind_CancelQuery);
      }
      if(ui_slot_press(UI_EventActionSlot_Accept))
      {
        Temp scratch = scratch_begin(0, 0);
        RD_RegsScope()
        {
          //rd_regs_fill_slot_from_string(query->slot, str8(ws->query_input_buffer, ws->query_input_string_size));
          rd_cmd(RD_CmdKind_CompleteQuery);
        }
        scratch_end(scratch);
      }
    }
    
    ////////////////////////////
    //- rjf: build listers
    //
    for(ListerTask *task = first_lister_task; task != 0; task = task->next)
    {
      DI_Scope *di_scope = di_scope_open();
      
      //////////////////////////
      //- rjf: unpack
      //
      RD_Lister *lister = task->lister;
      RD_ListerFlags flags = lister->regs->lister_flags;
      UI_Box *anchor_box = ui_box_from_key(lister->regs->ui_key);
      
      //////////////////////////
      //- rjf: parameters -> lister items
      //
      RD_ListerItemArray item_array = rd_lister_item_array_from_regs_needle_cursor_off(scratch.arena, lister->regs, str8(lister->input_buffer, lister->input_string_size), lister->input_cursor.column-1);
      
      //////////////////////////
      //- rjf: selected item hash -> cursor
      //
      Vec2S64 cursor = {0};
      for EachIndex(idx, item_array.count)
      {
        RD_ListerItem *item = &item_array.v[idx];
        U64 hash = rd_hash_from_lister_item(item);
        if(hash == lister->selected_item_hash)
        {
          cursor.y = (S64)(idx+1);
          break;
        }
      }
      
      //////////////////////////
      //- rjf: push autocompletion hint
      //
      if(1 <= cursor.y && cursor.y <= item_array.count)
      {
        RD_ListerItem *item = &item_array.v[cursor.y-1];
        if(item->flags & RD_ListerItemFlag_Autocompletion)
        {
          UI_Event evt = zero_struct;
          evt.kind   = UI_EventKind_AutocompleteHint;
          evt.string = item->string;
          ui_event_list_push(ui_build_arena(), &ws->ui_events, &evt);
        }
      }
      
      //////////////////////////
      //- rjf: animate values
      //
      F32 fast_rate = 1 - pow_f32(2, (-40.f * rd_state->frame_dt));
      F32 lister_open_t        = ui_anim(ui_key_from_stringf(ui_key_zero(), "lister_open_%p", lister), 1.f);
      F32 lister_num_of_rows_t = ui_anim(ui_key_from_stringf(ui_key_zero(), "lister_num_of_rows_%p", lister), (F32)item_array.count);
      lister->scroll_pt.off -= lister->scroll_pt.off*fast_rate;
      
      //////////////////////////
      //- rjf: unpack build parameters
      //
      F32 squish       = (0.25f - 0.25f*lister_open_t);
      F32 transparency = (1.f - lister_open_t);
      F32 row_height_px = floor_f32(ui_top_font_size()*3.f);
      if(lister->regs->lister_flags & RD_ListerFlag_Descriptions)
      {
        row_height_px += floor_f32(ui_top_font_size()*3.f);
      }
      Vec2F32 content_rect_dim = dim_2f32(content_rect);
      Vec2F32 content_rect_center = center_2f32(content_rect);
      F32 line_edit_height_px = 0;
      if(lister->regs->lister_flags & RD_ListerFlag_LineEdit)
      {
        line_edit_height_px = floor_f32(ui_top_font_size()*3.f);
      }
      line_edit_height_px = Min(line_edit_height_px, row_height_px);
      F32 lister_height_max = content_rect_dim.y*0.9f;
      Vec2F32 lister_dim_px =
      {
        content_rect_dim.x*0.5f,
        line_edit_height_px + lister_num_of_rows_t*row_height_px,
      };
      if(!ui_box_is_nil(anchor_box) && flags & RD_ListerFlag_SizeByAnchor)
      {
        lister_dim_px.x = dim_2f32(anchor_box->rect).x;
      }
      else if(!ui_box_is_nil(anchor_box))
      {
        lister_dim_px.x = ui_top_font_size()*50.f;
      }
      lister_dim_px.x = Max(lister_dim_px.x, ui_top_font_size()*50.f);
      lister_dim_px.y = Min(lister_dim_px.y, lister_height_max);
      Vec2F32 lister_pos_px =
      {
        content_rect_center.x - lister_dim_px.x/2,
        content_rect_center.y - content_rect_dim.y*0.9f/2,
      };
      if(!ui_box_is_nil(anchor_box))
      {
        lister_pos_px = v2f32(anchor_box->rect.x0 + lister->regs->off_px.x,
                              anchor_box->rect.y0 + lister->regs->off_px.y);
      }
      
      //////////////////////////
      //- rjf: build top-level lister box
      //
      ui_set_next_fixed_x(lister_pos_px.x);
      ui_set_next_fixed_y(lister_pos_px.y);
      ui_set_next_pref_width(ui_px(lister_dim_px.x, 1.f));
      ui_set_next_pref_height(ui_px(lister_dim_px.y, 1.f));
      ui_set_next_child_layout_axis(Axis2_Y);
      ui_set_next_corner_radius_01(ui_top_font_size()*0.25f);
      ui_set_next_corner_radius_11(ui_top_font_size()*0.25f);
      UI_Focus(UI_FocusKind_On)
        UI_Squish(squish)
        UI_Transparency(transparency)
        RD_Palette(RD_PaletteCode_Floating)
      {
        lister_box = ui_build_box_from_key(UI_BoxFlag_Clickable|
                                           UI_BoxFlag_Clip|
                                           UI_BoxFlag_RoundChildrenByParent|
                                           UI_BoxFlag_DisableFocusOverlay|
                                           UI_BoxFlag_DrawBorder|
                                           UI_BoxFlag_DrawBackgroundBlur|
                                           UI_BoxFlag_DrawDropShadow|
                                           UI_BoxFlag_DrawBackground,
                                           task->root_key);
      }
      
      //////////////////////////
      //- rjf: build lister line edit
      //
      if(flags & RD_ListerFlag_LineEdit)
        UI_WidthFill
        UI_Parent(lister_box)
        UI_Focus(UI_FocusKind_On)
        RD_Font(RD_FontSlot_Code)
      {
        UI_PrefHeight(ui_px(line_edit_height_px, 1.f))
        {
          UI_Signal sig = rd_line_edit(RD_LineEditFlag_Border|RD_LineEditFlag_CodeContents,
                                       0,
                                       0,
                                       &lister->input_cursor,
                                       &lister->input_mark,
                                       lister->input_buffer,
                                       sizeof(lister->input_buffer),
                                       &lister->input_string_size,
                                       0,
                                       str8(lister->input_buffer, lister->input_string_size),
                                       str8_lit("###lister_text_input"));
        }
      }
      
      //////////////////////////
      //- rjf: build lister contents
      //
      UI_ScrollListParams params =
      {
        .flags         = UI_ScrollListFlag_All,
        .dim_px        = v2f32(lister_dim_px.x, lister_dim_px.y - line_edit_height_px),
        .row_height_px = row_height_px,
        .cursor_range  = r2s64(v2s64(0, 0), v2s64(0, item_array.count)),
        .item_range    = r1s64(0, item_array.count),
        .cursor_min_is_empty_selection[Axis2_Y] = 1,
      };
      Rng1S64 visible_row_range = {0};
      UI_ScrollListSignal scroll_list_signal = {0};
      UI_WidthFill UI_HeightFill
        UI_Parent(lister_box)
        UI_Focus(UI_FocusKind_On)
        UI_ScrollList(&params, &lister->scroll_pt, &cursor, 0, &visible_row_range, &scroll_list_signal)
        UI_Focus(UI_FocusKind_Null)
        RD_Palette(RD_PaletteCode_ImplicitButton)
      {
        UI_HoverCursor(OS_Cursor_HandPoint)
          for(S64 idx = visible_row_range.min; 0 <= idx && idx < visible_row_range.max && idx < item_array.count; idx += 1)
        {
          RD_ListerItem *item = &item_array.v[idx];
          B32 item_is_selected = (idx == cursor.y-1);
          UI_FocusHot(item_is_selected ? UI_FocusKind_On : UI_FocusKind_Off)
          {
            //- rjf: build the top-level box for the item
            UI_Box *item_box = ui_build_box_from_stringf(UI_BoxFlag_DrawBorder|UI_BoxFlag_DrawBackground|UI_BoxFlag_DrawHotEffects|UI_BoxFlag_DrawActiveEffects|UI_BoxFlag_MouseClickable, "autocomp_%I64x", idx);
            
            //- rjf: build item contents
            UI_Parent(item_box) UI_Padding(ui_em(1.f, 1.f))
            {
              // rjf: icon
              if(item->icon_kind != RD_IconKind_Null)
                UI_PrefWidth(ui_em(2.f, 1.f))
                UI_Column
                UI_Padding(ui_pct(1, 0))
                RD_Font(RD_FontSlot_Icons)
              {
                ui_label(rd_icon_kind_text_table[item->icon_kind]);
              }
              
              // rjf: name / description
              UI_Column UI_Padding(ui_pct(1, 0)) UI_PrefHeight(ui_em(2.f, 1.f))
              {
                // rjf: name
                UI_WidthFill UI_Row UI_PrefWidth(ui_text_dim(1, 0)) UI_TextAlignment(UI_TextAlign_Center)
                {
                  // rjf: display name
                  RD_Font(item->flags & RD_ListerItemFlag_IsNonCode ? RD_FontSlot_Main : RD_FontSlot_Code)
                  {
                    UI_Box *box = item->flags & RD_ListerItemFlag_IsNonCode ? ui_label(item->display_name).box : rd_code_label(1.f, 0, ui_top_palette()->text, item->display_name);
                    ui_box_equip_fuzzy_match_ranges(box, &item->display_name__matches);
                  }
                  
                  // rjf: kind name
                  if(flags & RD_ListerFlag_KindLabel && item->kind_name.size != 0)
                  {
                    ui_spacer(ui_em(1.f, 1.f));
                    RD_Palette(RD_PaletteCode_Floating) UI_CornerRadius(ui_top_font_size()*0.5f)
                    {
                      ui_set_next_pref_width(ui_children_sum(1));
                      ui_set_next_flags(UI_BoxFlag_DrawBorder);
                      UI_Row UI_Padding(ui_em(0.5f, 1.f))
                      {
                        UI_Box *box = ui_build_box_from_key(UI_BoxFlag_DrawText|UI_BoxFlag_DrawTextWeak, ui_key_zero());
                        ui_box_equip_display_string(box, item->kind_name);
                      }
                    }
                  }
                }
                
                // rjf: description
                if(flags & RD_ListerFlag_Descriptions)
                {
                  if(item->description.size != 0) UI_WidthFill UI_Row UI_PrefWidth(ui_text_dim(1, 0)) UI_Flags(UI_BoxFlag_DrawTextWeak)
                  {
                    UI_Box *box = ui_label(item->description).box;
                    ui_box_equip_fuzzy_match_ranges(box, &item->description__matches);
                  }
                }
              }
              
              // rjf: bindings
              if(item->flags & RD_ListerItemFlag_Bindings) RD_Palette(RD_PaletteCode_Floating) UI_Focus(UI_FocusKind_Off)
              {
                ui_set_next_flags(UI_BoxFlag_Clickable);
                UI_PrefWidth(ui_children_sum(1.f)) UI_HeightFill UI_NamedColumn(str8_lit("binding_column")) UI_Padding(ui_px(row_height_px/4.f, 0.f))
                {
                  ui_set_next_flags(UI_BoxFlag_Clickable);
                  UI_NamedRow(str8_lit("binding_row"))
                  {
                    rd_cmd_binding_buttons(item->string);
                  }
                }
              }
            }
            
            //- rjf: do interaction with top-level item
            UI_Signal item_sig = ui_signal_from_box(item_box);
            if(ui_clicked(item_sig))
            {
#if 0 // TODO(rjf): @cfg_lister
              UI_Event move_back_evt = zero_struct;
              move_back_evt.kind = UI_EventKind_Navigate;
              move_back_evt.flags = UI_EventFlag_KeepMark;
              move_back_evt.delta_2s32.x = -(S32)query_word.size;
              ui_event_list_push(ui_build_arena(), &ws->ui_events, &move_back_evt);
              UI_Event paste_evt = zero_struct;
              paste_evt.kind = UI_EventKind_Text;
              paste_evt.string = item->string;
              ui_event_list_push(ui_build_arena(), &ws->ui_events, &paste_evt);
              lister_box->default_nav_focus_hot_key = lister_box->default_nav_focus_active_key = lister_box->default_nav_focus_next_hot_key = lister_box->default_nav_focus_next_active_key = ui_key_zero();
#endif
            }
            else if(item_box->flags & UI_BoxFlag_FocusHot && !(item_box->flags & UI_BoxFlag_FocusHotDisabled))
            {
              UI_Event evt = zero_struct;
              evt.kind   = UI_EventKind_AutocompleteHint;
              evt.string = item->string;
              ui_event_list_push(ui_build_arena(), &ws->ui_events, &evt);
            }
          }
        }
      }
      
      //////////////////////////
      //- rjf: cursor -> selected item hash
      //
      if(1 <= cursor.y && cursor.y <= item_array.count)
      {
        RD_ListerItem *item = &item_array.v[cursor.y-1];
        U64 hash = rd_hash_from_lister_item(item);
        lister->selected_item_hash = hash;
      }
      else
      {
        lister->selected_item_hash = 0;
      }
      
      di_scope_close(di_scope);
    }
    
#if 0 // TODO(rjf): @cfg_lister
    ////////////////////////////
    //- rjf: prepare query state for the in-progress query command
    //
    if(ws->query_cmd_name.size != 0)
    {
      RD_CmdKindInfo *cmd_kind_info = rd_cmd_kind_info_from_string(ws->query_cmd_name);
      RD_RegSlot missing_slot = cmd_kind_info->query.slot;
      
      //- rjf: if our input is stale, w.r.t. the current query command, initialize input state
      if(ws->query_input_gen < ws->query_cmd_gen)
      {
        ws->query_input_gen = ws->query_cmd_gen;
        String8 default_query_input = {0};
        ws->query_input_string_size = Min(sizeof(ws->query_input_buffer), default_query_input.size);
        MemoryCopy(ws->query_input_buffer, default_query_input.str, ws->query_input_string_size);
        ws->query_input_cursor = ws->query_input_mark = txt_pt(1, ws->query_input_string_size+1);
      }
      
#if 0 // TODO(rjf): @cfg_lister (query state prep)
      String8 query_view_name = cmd_kind_info->query.view_name;
      if(query_view_name.size == 0)
      {
        switch(missing_slot)
        {
          default:{}break;
          case RD_RegSlot_Thread:
          case RD_RegSlot_Module:
          case RD_RegSlot_Process:
          case RD_RegSlot_Machine:
          case RD_RegSlot_CtrlEntity:{query_view_name = rd_view_rule_kind_info_table[RD_ViewRuleKind_CtrlEntityLister].string;}break;
          case RD_RegSlot_Entity:    {query_view_name = rd_view_rule_kind_info_table[RD_ViewRuleKind_EntityLister].string;}break;
          case RD_RegSlot_EntityList:{query_view_name = rd_view_rule_kind_info_table[RD_ViewRuleKind_EntityLister].string;}break;
          case RD_RegSlot_FilePath:  {query_view_name = rd_view_rule_kind_info_table[RD_ViewRuleKind_FileSystem].string;}break;
          case RD_RegSlot_PID:       {query_view_name = rd_view_rule_kind_info_table[RD_ViewRuleKind_SystemProcesses].string;}break;
        }
      }
      RD_ViewRuleInfo *view_spec = rd_view_rule_info_from_string(query_view_name);
      if(!str8_match(ws->query_view_stack_top->string, view_spec->string, 0) ||
         ws->query_view_stack_top == &rd_nil_cfg)
      {
        Temp scratch = scratch_begin(0, 0);
        
        // rjf: clear existing query stack
        rd_cfg_release(ws->query_view_stack_top);
        
        // rjf: determine default query
        String8 default_query = {0};
        switch(missing_slot)
        {
          default:
          if(cmd_kind_info->query.flags & RD_QueryFlag_KeepOldInput)
          {
            default_query = rd_push_search_string(scratch.arena);
          }break;
          case RD_RegSlot_FilePath:
          {
            default_query = path_normalized_from_string(scratch.arena, rd_state->current_path);
            default_query = push_str8f(scratch.arena, "%S/", default_query);
          }break;
        }
        
        // rjf: construct & push new view
        RD_Cfg *bucket = rd_cfg_child_from_string(rd_state->root_cfg, str8_lit("transient"));
        RD_Cfg *view = rd_cfg_new(bucket, view_spec->string);
        RD_Cfg *filter = rd_cfg_new(view, str8_lit("filter"));
        rd_cfg_new(filter, default_query);
        RD_ViewState *view_state = rd_view_state_from_cfg(view);
        view_state->filter_cursor = txt_pt(1, default_query.size+1);
        if(cmd_kind_info->query.flags & RD_QueryFlag_SelectOldInput)
        {
          view_state->filter_mark = txt_pt(1, 1);
        }
        ws->query_view_stack_top = view;
        ws->query_view_selected = 1;
        
        scratch_end(scratch);
      }
#endif
    }
    
    ////////////////////////////
    //- rjf: build query
    //
    if(query_is_open)
      UI_Focus((window_is_focused && !ui_any_ctx_menu_is_open() && !ws->menu_bar_focused && ws->query_input_selected) ? UI_FocusKind_On : UI_FocusKind_Off)
      RD_Palette(RD_PaletteCode_Floating)
    {
      String8 query_cmd_name = ws->query_cmd_name;
      RD_CmdKindInfo *query_cmd_info = rd_cmd_kind_info_from_string(query_cmd_name);
      RD_Query *query = &query_cmd_info->query;
      F32 query_view_t = ui_anim(ui_key_from_stringf(ui_key_zero(), "###%p_query_view_t", ws), 1.f);
      F32 query_squish = 0.25f - query_view_t*0.25f;
      F32 query_transparency = 1.f - query_view_t;
      
      //- rjf: calculate rectangles
      Vec2F32 window_center = center_2f32(window_rect);
      Vec2F32 query_input_dim = v2f32(dim_2f32(window_rect).x*0.5f, ui_top_font_size()*3.f);
      F32 query_input_margin = ui_top_font_size()*8.f;
      Rng2F32 query_input_rect = r2f32p(window_center.x - query_input_dim.x/2,
                                        window_rect.y0 + query_input_margin,
                                        window_center.x + query_input_dim.x/2,
                                        window_rect.y0 + query_input_margin + query_input_dim.y);
      
      //- rjf: build floating query container
      UI_Box *query_container_box = &ui_nil_box;
      UI_Rect(query_input_rect)
        UI_CornerRadius00(ui_top_font_size()*0.2f)
        UI_CornerRadius10(ui_top_font_size()*0.2f)
        UI_ChildLayoutAxis(Axis2_Y)
        UI_Squish(query_squish)
        UI_Transparency(query_transparency)
      {
        query_container_box = ui_build_box_from_stringf(UI_BoxFlag_Floating|
                                                        UI_BoxFlag_AllowOverflow|
                                                        UI_BoxFlag_Clickable|
                                                        UI_BoxFlag_Clip|
                                                        UI_BoxFlag_DisableFocusOverlay|
                                                        UI_BoxFlag_DrawBorder|
                                                        UI_BoxFlag_DrawBackground|
                                                        UI_BoxFlag_DrawBackgroundBlur|
                                                        UI_BoxFlag_DrawDropShadow,
                                                        "query_container");
      }
      
      //- rjf: set up autocompletion lister info
      if(ui_is_focus_active())
      {
        rd_set_autocomp_lister_query(.anchor_key  = query_container_box->key,
                                     .anchor_off  = v2f32(0, dim_2f32(query_container_box->rect).y - dim_2f32(query_container_box->rect).y*(1-query_view_t)*0.25f - 2.f),
                                     .flags       = 0xffffffff,
                                     .input       = str8(ws->query_input_buffer, ws->query_input_string_size),
                                     .cursor_off  = ws->query_input_cursor.column-1,
                                     .squish      = query_squish,
                                     .transparency= query_transparency);
      }
      
      //- rjf: build query text input
      B32 query_completed = 0;
      B32 query_cancelled = 0;
      UI_Parent(query_container_box)
        UI_WidthFill UI_HeightFill
        UI_Focus(UI_FocusKind_On)
      {
        ui_set_next_flags(UI_BoxFlag_DrawDropShadow|UI_BoxFlag_DrawBorder);
        UI_Row
        {
          UI_PrefWidth(ui_text_dim(0.f, 1.f)) UI_Padding(ui_em(1.f, 1.f))
          {
            RD_IconKind icon_kind = query_cmd_info->icon_kind;
            if(icon_kind != RD_IconKind_Null)
            {
              RD_Font(RD_FontSlot_Icons) ui_label(rd_icon_kind_text_table[icon_kind]);
            }
            ui_labelf("%S", query_cmd_info->display_name);
          }
          RD_Font((query->flags & RD_QueryFlag_CodeInput) ? RD_FontSlot_Code : RD_FontSlot_Main)
            UI_TextPadding(ui_top_font_size()*0.5f)
          {
            UI_Signal sig = rd_line_edit(RD_LineEditFlag_Border|
                                         (RD_LineEditFlag_CodeContents * !!(query->flags & RD_QueryFlag_CodeInput)),
                                         0,
                                         0,
                                         &ws->query_input_cursor,
                                         &ws->query_input_mark,
                                         ws->query_input_buffer,
                                         sizeof(ws->query_input_buffer),
                                         &ws->query_input_string_size,
                                         0,
                                         str8(ws->query_input_buffer, ws->query_input_string_size),
                                         str8_lit("###query_text_input"));
            if(ui_pressed(sig))
            {
              ws->query_input_selected = 1;
            }
          }
          UI_PrefWidth(ui_em(5.f, 1.f)) UI_Focus(UI_FocusKind_Off) RD_Palette(RD_PaletteCode_PositivePopButton)
          {
            if(ui_clicked(rd_icon_buttonf(RD_IconKind_RightArrow, 0, "##complete_query")))
            {
              query_completed = 1;
            }
          }
          UI_PrefWidth(ui_em(3.f, 1.f)) UI_Focus(UI_FocusKind_Off) RD_Palette(RD_PaletteCode_PlainButton)
          {
            if(ui_clicked(rd_icon_buttonf(RD_IconKind_X, 0, "##cancel_query")))
            {
              query_cancelled = 1;
            }
          }
        }
      }
      
      //- rjf: query submission
      if(((ui_is_focus_active() || (window_is_focused && !ui_any_ctx_menu_is_open() && !ws->menu_bar_focused && !ws->query_input_selected)) &&
          ui_slot_press(UI_EventActionSlot_Cancel)) || query_cancelled)
      {
        rd_cmd(RD_CmdKind_CancelQuery);
      }
      if((ui_is_focus_active() && ui_slot_press(UI_EventActionSlot_Accept)) || query_completed)
      {
        Temp scratch = scratch_begin(0, 0);
        RD_RegsScope()
        {
          rd_regs_fill_slot_from_string(query->slot, str8(ws->query_input_buffer, ws->query_input_string_size));
          rd_cmd(RD_CmdKind_CompleteQuery);
        }
        scratch_end(scratch);
      }
      
      //- rjf: take fallthrough interaction in query view
      {
        UI_Signal sig = ui_signal_from_box(query_container_box);
        if(ui_pressed(sig))
        {
          ws->query_input_selected = 1;
        }
      }
    }
    else
    {
      ws->query_input_selected = 0;
    }
    
    ////////////////////////////
    //- rjf: darken lower layer ui when query is selected
    //
    F32 query_input_selected_t = ui_anim(ui_key_from_stringf(ui_key_zero(), "%p_query_input_selected", ws), (F32)!!ws->query_input_selected);
    UI_Palette(ui_build_palette(0, .background = mix_4f32(rd_rgba_from_theme_color(RD_ThemeColor_InactivePanelOverlay), v4f32(0, 0, 0, 0), 1-query_input_selected_t)))
      UI_Rect(window_rect)
    {
      ui_build_box_from_key(UI_BoxFlag_DrawBackground, ui_key_zero());
    }
#endif
    
    ////////////////////////////
    //- rjf: top-level registers context menu
    //
    RD_Palette(RD_PaletteCode_Floating) UI_CtxMenu(rd_state->ctx_menu_key)
      UI_PrefWidth(ui_em(50.f, 1.f))
      RD_Palette(RD_PaletteCode_ImplicitButton)
    {
      Temp scratch = scratch_begin(0, 0);
      RD_Regs *regs = ws->ctx_menu_regs;
      RD_RegSlot slot = ws->ctx_menu_regs_slot;
      CTRL_Entity *ctrl_entity = &ctrl_entity_nil;
      {
        switch(slot)
        {
          default:{}break;
          
          //////////////////////
          //- rjf: source code locations
          //
          case RD_RegSlot_Cursor:
          {
            // TODO(rjf): with new registers-based commands, all of this can be deduplicated with the
            // command-based path, but I am holding off on that until post 0.9.12 - these should be
            // able to just all push commands for their corresponding actions
            //
            TXT_Scope *txt_scope = txt_scope_open();
            HS_Scope *hs_scope = hs_scope_open();
            TxtRng range = txt_rng(regs->cursor, regs->mark);
            D_LineList lines = regs->lines;
            if(!txt_pt_match(range.min, range.max) && ui_clicked(rd_cmd_spec_button(rd_cmd_kind_info_table[RD_CmdKind_Copy].string)))
            {
              U128 hash = {0};
              TXT_TextInfo info = txt_text_info_from_key_lang(txt_scope, regs->text_key, regs->lang_kind, &hash);
              String8 data = hs_data_from_hash(hs_scope, hash);
              String8 copy_data = txt_string_from_info_data_txt_rng(&info, data, range);
              os_set_clipboard_text(copy_data);
              ui_ctx_menu_close();
            }
            if(range.min.line == range.max.line && ui_clicked(rd_icon_buttonf(RD_IconKind_RightArrow, 0, "Set Next Statement")))
            {
              CTRL_Entity *thread = ctrl_entity_from_handle(d_state->ctrl_entity_store, rd_regs()->thread);
              U64 new_rip_vaddr = regs->vaddr;
              if(regs->file_path.size != 0)
              {
                for(D_LineNode *n = lines.first; n != 0; n = n->next)
                {
                  CTRL_EntityList modules = ctrl_modules_from_dbgi_key(scratch.arena, d_state->ctrl_entity_store, &n->v.dbgi_key);
                  CTRL_Entity *module = ctrl_module_from_thread_candidates(d_state->ctrl_entity_store, thread, &modules);
                  if(module != &ctrl_entity_nil)
                  {
                    new_rip_vaddr = ctrl_vaddr_from_voff(module, n->v.voff_range.min);
                    break;
                  }
                }
              }
              rd_cmd(RD_CmdKind_SetThreadIP, .vaddr = new_rip_vaddr);
              ui_ctx_menu_close();
            }
            if(range.min.line == range.max.line && ui_clicked(rd_icon_buttonf(RD_IconKind_Play, 0, "Run To Line")))
            {
              if(regs->file_path.size != 0)
              {
                rd_cmd(RD_CmdKind_RunToLine, .file_path = regs->file_path, .cursor = range.min);
              }
              else
              {
                rd_cmd(RD_CmdKind_RunToAddress, .vaddr = regs->vaddr);
              }
              ui_ctx_menu_close();
            }
            if(range.min.line == range.max.line && ui_clicked(rd_icon_buttonf(RD_IconKind_Null, 0, "Go To Name")))
            {
              U128 hash = {0};
              TXT_TextInfo info = txt_text_info_from_key_lang(txt_scope, regs->text_key, regs->lang_kind, &hash);
              String8 data = hs_data_from_hash(hs_scope, hash);
              Rng1U64 expr_off_range = {0};
              if(range.min.column != range.max.column)
              {
                expr_off_range = r1u64(txt_off_from_info_pt(&info, range.min), txt_off_from_info_pt(&info, range.max));
              }
              else
              {
                expr_off_range = txt_expr_off_range_from_info_data_pt(&info, data, range.min);
              }
              String8 expr = str8_substr(data, expr_off_range);
              rd_cmd(RD_CmdKind_GoToName, .string = expr);
              ui_ctx_menu_close();
            }
            if(range.min.line == range.max.line && ui_clicked(rd_icon_buttonf(RD_IconKind_CircleFilled, 0, "Toggle Breakpoint")))
            {
              rd_cmd(RD_CmdKind_ToggleBreakpoint,
                     .file_path = regs->file_path,
                     .cursor    = range.min,
                     .vaddr     = regs->vaddr);
              ui_ctx_menu_close();
            }
            if(range.min.line == range.max.line && ui_clicked(rd_icon_buttonf(RD_IconKind_Binoculars, 0, "Toggle Watch Expression")))
            {
              U128 hash = {0};
              TXT_TextInfo info = txt_text_info_from_key_lang(txt_scope, regs->text_key, regs->lang_kind, &hash);
              String8 data = hs_data_from_hash(hs_scope, hash);
              Rng1U64 expr_off_range = {0};
              if(range.min.column != range.max.column)
              {
                expr_off_range = r1u64(txt_off_from_info_pt(&info, range.min), txt_off_from_info_pt(&info, range.max));
              }
              else
              {
                expr_off_range = txt_expr_off_range_from_info_data_pt(&info, data, range.min);
              }
              String8 expr = str8_substr(data, expr_off_range);
              rd_cmd(RD_CmdKind_ToggleWatchExpression, .string = expr);
              ui_ctx_menu_close();
            }
            if(regs->file_path.size == 0 && range.min.line == range.max.line && ui_clicked(rd_cmd_spec_button(rd_cmd_kind_info_table[RD_CmdKind_GoToSource].string)))
            {
              rd_cmd(RD_CmdKind_GoToSource, .lines = lines);
              ui_ctx_menu_close();
            }
            if(regs->file_path.size != 0 && range.min.line == range.max.line && ui_clicked(rd_cmd_spec_button(rd_cmd_kind_info_table[RD_CmdKind_GoToDisassembly].string)))
            {
              rd_cmd(RD_CmdKind_GoToDisassembly, .lines = lines);
              ui_ctx_menu_close();
            }
            hs_scope_close(hs_scope);
            txt_scope_close(txt_scope);
          }break;
          
          //////////////////////
          //- rjf: tabs
          //
          case RD_RegSlot_View:
          {
            RD_Cfg *tab = rd_cfg_from_handle(regs->view);
            RD_RegsScope(.view = regs->view)
            {
              String8 expr = rd_view_expr_string();
              RD_ViewRuleInfo *view_rule_info = rd_view_rule_info_from_string(tab->string);
              RD_IconKind view_icon = view_rule_info->icon_kind;
              DR_FancyStringList fstrs = rd_title_fstrs_from_view(scratch.arena, view_rule_info->display_name, expr, ui_top_palette()->text, ui_top_palette()->text_weak, ui_top_font_size());
              String8 file_path = rd_file_path_from_eval_string(scratch.arena, expr);
              
              // rjf: title
              UI_Row
              {
                ui_spacer(ui_em(1.f, 1.f));
                RD_Font(RD_FontSlot_Icons)
                  UI_FontSize(rd_font_size_from_slot(RD_FontSlot_Icons))
                  UI_PrefWidth(ui_em(2.f, 1.f))
                  UI_PrefHeight(ui_pct(1, 0))
                  UI_TextAlignment(UI_TextAlign_Center)
                  UI_FlagsAdd(UI_BoxFlag_DrawTextWeak)
                  ui_label(rd_icon_kind_text_table[view_icon]);
                UI_PrefWidth(ui_text_dim(10, 1))
                {
                  UI_Box *name_box = ui_build_box_from_key(UI_BoxFlag_DrawText, ui_key_zero());
                  ui_box_equip_display_fancy_strings(name_box, &fstrs);
                }
              }
              
              RD_Palette(RD_PaletteCode_Floating) ui_divider(ui_em(1.f, 1.f));
              
              // rjf: copy name
              if(ui_clicked(rd_icon_buttonf(RD_IconKind_Clipboard, 0, "Copy Name")))
              {
                os_set_clipboard_text(dr_string_from_fancy_string_list(scratch.arena, &fstrs));
                ui_ctx_menu_close();
              }
              
              // rjf: copy full path
              if(file_path.size != 0)
              {
                UI_Signal copy_full_path_sig = rd_icon_buttonf(RD_IconKind_Clipboard, 0, "Copy Full Path");
                String8 full_path = path_normalized_from_string(scratch.arena, file_path);
                if(ui_clicked(copy_full_path_sig))
                {
                  os_set_clipboard_text(full_path);
                  ui_ctx_menu_close();
                }
                if(ui_hovering(copy_full_path_sig)) UI_Tooltip
                {
                  ui_label(full_path);
                }
              }
              
              // rjf: show in explorer
              if(file_path.size != 0)
              {
                UI_Signal sig = rd_icon_buttonf(RD_IconKind_FolderClosedFilled, 0, "Show In Explorer");
                if(ui_clicked(sig))
                {
                  String8 full_path = path_normalized_from_string(scratch.arena, file_path);
                  os_show_in_filesystem_ui(full_path);
                  ui_ctx_menu_close();
                }
              }
              
              // rjf: filter controls
              if(view_rule_info->flags & RD_ViewRuleInfoFlag_CanFilter)
              {
                if(ui_clicked(rd_cmd_spec_button(rd_cmd_kind_info_table[RD_CmdKind_Filter].string)))
                {
                  rd_cmd(RD_CmdKind_Filter);
                  ui_ctx_menu_close();
                }
                if(ui_clicked(rd_cmd_spec_button(rd_cmd_kind_info_table[RD_CmdKind_ClearFilter].string)))
                {
                  rd_cmd(RD_CmdKind_ClearFilter);
                  ui_ctx_menu_close();
                }
              }
              
              // rjf: close tab
              if(ui_clicked(rd_icon_buttonf(RD_IconKind_X, 0, "Close Tab")))
              {
                rd_cmd(RD_CmdKind_CloseTab);
                ui_ctx_menu_close();
              }
              
              // rjf: param tree editing
#if 0
              UI_TextPadding(ui_top_font_size()*1.5f) RD_Font(RD_FontSlot_Code)
              {
                Temp scratch = scratch_begin(0, 0);
                String8 schema_string = view->spec->params_schema;
                MD_TokenizeResult schema_tokenize = md_tokenize_from_text(scratch.arena, schema_string);
                MD_ParseResult schema_parse = md_parse_from_text_tokens(scratch.arena, str8_zero(), schema_string, schema_tokenize.tokens);
                MD_Node *schema_root = schema_parse.root->first;
                if(!md_node_is_nil(schema_root))
                {
                  if(!md_node_is_nil(schema_root->first))
                  {
                    RD_Palette(RD_PaletteCode_Floating) ui_divider(ui_em(1.f, 1.f));
                  }
                  for MD_EachNode(key, schema_root->first)
                  {
                    UI_Row
                    {
                      MD_Node *params = view->params_roots[view->params_write_gen%ArrayCount(view->params_roots)];
                      MD_Node *param_tree = md_child_from_string(params, key->string, 0);
                      String8 pre_edit_value = md_string_from_children(scratch.arena, param_tree);
                      UI_PrefWidth(ui_em(10.f, 1.f)) ui_label(key->string);
                      UI_Signal sig = rd_line_editf(RD_LineEditFlag_Border|RD_LineEditFlag_CodeContents, 0, 0, &ws->ctx_menu_input_cursor, &ws->ctx_menu_input_mark, ws->ctx_menu_input_buffer, ws->ctx_menu_input_buffer_size, &ws->ctx_menu_input_string_size, 0, pre_edit_value, "%S##view_param", key->string);
                      if(ui_committed(sig))
                      {
                        String8 new_string = str8(ws->ctx_menu_input_buffer, ws->ctx_menu_input_string_size);
                        rd_view_store_param(view, key->string, new_string);
                      }
                    }
                  }
                }
                scratch_end(scratch);
              }
#endif
            }
          }break;
          
          //////////////////////
          //- rjf: ctrl entities
          //
          case RD_RegSlot_Machine:     ctrl_entity = ctrl_entity_from_handle(d_state->ctrl_entity_store, regs->machine);     goto ctrl_entity_title;
          case RD_RegSlot_Process:     ctrl_entity = ctrl_entity_from_handle(d_state->ctrl_entity_store, regs->process);     goto ctrl_entity_title;
          case RD_RegSlot_Module:      ctrl_entity = ctrl_entity_from_handle(d_state->ctrl_entity_store, regs->module);      goto ctrl_entity_title;
          case RD_RegSlot_Thread:      ctrl_entity = ctrl_entity_from_handle(d_state->ctrl_entity_store, regs->thread);      goto ctrl_entity_title;
          case RD_RegSlot_CtrlEntity:  ctrl_entity = ctrl_entity_from_handle(d_state->ctrl_entity_store, regs->ctrl_entity); goto ctrl_entity_title;
          ctrl_entity_title:;
          {
            //- rjf: title
            UI_Row
              UI_PrefWidth(ui_text_dim(5, 1))
              UI_TextAlignment(UI_TextAlign_Center)
              UI_TextPadding(ui_top_font_size()*1.5f)
            {
              DR_FancyStringList fstrs = rd_title_fstrs_from_ctrl_entity(scratch.arena, ctrl_entity, ui_top_palette()->text_weak, ui_top_font_size(), 0);
              UI_Box *title_box = ui_build_box_from_key(UI_BoxFlag_DrawText, ui_key_zero());
              ui_box_equip_display_fancy_strings(title_box, &fstrs);
              if(ctrl_entity->kind == CTRL_EntityKind_Thread)
              {
                ui_spacer(ui_em(0.5f, 1.f));
                UI_FontSize(ui_top_font_size() - 1.f)
                  UI_CornerRadius(ui_top_font_size()*0.5f)
                  RD_Palette(RD_PaletteCode_NeutralPopButton)
                  UI_TextPadding(ui_top_font_size()*0.5f)
                {
                  UI_FlagsAdd(UI_BoxFlag_DrawTextWeak|UI_BoxFlag_DrawBorder) ui_label(string_from_arch(ctrl_entity->arch));
                  ui_spacer(ui_em(0.5f, 1.f));
                  UI_FlagsAdd(UI_BoxFlag_DrawTextWeak|UI_BoxFlag_DrawBorder) ui_labelf("TID: %i", (U32)ctrl_entity->id);
                }
              }
            }
            
            RD_Palette(RD_PaletteCode_Floating) ui_divider(ui_em(1.f, 1.f));
            
            //- rjf: name editor
            if(ctrl_entity->kind == CTRL_EntityKind_Thread) RD_Font(RD_FontSlot_Code) UI_TextPadding(ui_top_font_size()*1.5f)
            {
              UI_Signal sig = rd_line_editf(RD_LineEditFlag_Border|RD_LineEditFlag_CodeContents, 0, 0, &ws->ctx_menu_input_cursor, &ws->ctx_menu_input_mark, ws->ctx_menu_input_buffer, ws->ctx_menu_input_buffer_size, &ws->ctx_menu_input_string_size, 0, ctrl_entity->string, "Name###ctrl_entity_string_edit_%p", ctrl_entity);
              if(ui_committed(sig))
              {
                rd_cmd(RD_CmdKind_SetEntityName, .ctrl_entity = ctrl_entity->handle, .string = str8(ws->ctx_menu_input_buffer, ws->ctx_menu_input_string_size));
              }
            }
            
            // rjf: copy full path
            if(ctrl_entity->kind == CTRL_EntityKind_Module) if(ui_clicked(rd_icon_buttonf(RD_IconKind_Clipboard, 0, "Copy Full Path")))
            {
              os_set_clipboard_text(ctrl_entity->string);
              ui_ctx_menu_close();
            }
            
            // rjf: copy ID
            if((ctrl_entity->kind == CTRL_EntityKind_Thread ||
                ctrl_entity->kind == CTRL_EntityKind_Process) &&
               ui_clicked(rd_icon_buttonf(RD_IconKind_Clipboard, 0, "Copy ID")))
            {
              String8 string = str8_from_u64(scratch.arena, ctrl_entity->id, 10, 0, 0);
              os_set_clipboard_text(string);
              ui_ctx_menu_close();
            }
            
            // rjf: copy call stack
            if(ctrl_entity->kind == CTRL_EntityKind_Thread)
            {
              if(ui_clicked(rd_icon_buttonf(RD_IconKind_Clipboard, 0, "Copy Call Stack")))
              {
                DI_Scope *di_scope = di_scope_open();
                CTRL_Entity *process = ctrl_entity_ancestor_from_kind(ctrl_entity, CTRL_EntityKind_Process);
                CTRL_Unwind base_unwind = d_query_cached_unwind_from_thread(ctrl_entity);
                CTRL_CallStack rich_unwind = ctrl_call_stack_from_unwind(scratch.arena, di_scope, process, &base_unwind);
                String8List lines = {0};
                for(U64 frame_idx = 0; frame_idx < rich_unwind.concrete_frame_count; frame_idx += 1)
                {
                  CTRL_CallStackFrame *concrete_frame = &rich_unwind.frames[frame_idx];
                  U64 rip_vaddr = regs_rip_from_arch_block(ctrl_entity->arch, concrete_frame->regs);
                  CTRL_Entity *module = ctrl_module_from_process_vaddr(process, rip_vaddr);
                  RDI_Parsed *rdi = concrete_frame->rdi;
                  RDI_Procedure *procedure = concrete_frame->procedure;
                  for(CTRL_CallStackInlineFrame *inline_frame = concrete_frame->last_inline_frame;
                      inline_frame != 0;
                      inline_frame = inline_frame->prev)
                  {
                    RDI_InlineSite *inline_site = inline_frame->inline_site;
                    String8 name = {0};
                    name.str = rdi_string_from_idx(rdi, inline_site->name_string_idx, &name.size);
                    str8_list_pushf(scratch.arena, &lines, "0x%I64x: [inlined] \"%S\"%s%S", rip_vaddr, name, module == &ctrl_entity_nil ? "" : " in ", module->string);
                  }
                  if(procedure != 0)
                  {
                    String8 name = {0};
                    name.str = rdi_name_from_procedure(rdi, procedure, &name.size);
                    str8_list_pushf(scratch.arena, &lines, "0x%I64x: \"%S\"%s%S", rip_vaddr, name, module == &ctrl_entity_nil ? "" : " in ", module->string);
                  }
                  else if(module != &ctrl_entity_nil)
                  {
                    str8_list_pushf(scratch.arena, &lines, "0x%I64x: [??? in %S]", rip_vaddr, module->string);
                  }
                  else
                  {
                    str8_list_pushf(scratch.arena, &lines, "0x%I64x: [??? in ???]", rip_vaddr);
                  }
                }
                StringJoin join = {0};
                join.sep = join.post = str8_lit("\n");
                String8 text = str8_list_join(scratch.arena, &lines, &join);
                os_set_clipboard_text(text);
                ui_ctx_menu_close();
                di_scope_close(di_scope);
              }
            }
            
            // rjf: find
            if(ctrl_entity->kind == CTRL_EntityKind_Thread)
            {
              if(ui_clicked(rd_icon_buttonf(RD_IconKind_FileOutline, 0, "Find")))
              {
                rd_cmd(RD_CmdKind_FindThread, .thread = ctrl_entity->handle);
                ui_ctx_menu_close();
              }
            }
            
            // rjf: selection
            if(ctrl_entity->kind == CTRL_EntityKind_Thread)
            {
              B32 is_selected = ctrl_handle_match(rd_base_regs()->thread, ctrl_entity->handle);
              if(is_selected)
              {
                rd_icon_buttonf(RD_IconKind_Thread, 0, "[Selected]###select_entity");
              }
              else if(ui_clicked(rd_icon_buttonf(RD_IconKind_Thread, 0, "Select###select_entity")))
              {
                rd_cmd(RD_CmdKind_SelectThread, .thread = ctrl_entity->handle);
                ui_ctx_menu_close();
              }
            }
            
            // rjf: freezing
            if(ctrl_entity->kind == CTRL_EntityKind_Thread ||
               ctrl_entity->kind == CTRL_EntityKind_Process ||
               ctrl_entity->kind == CTRL_EntityKind_Machine)
            {
              B32 is_frozen = ctrl_entity_tree_is_frozen(ctrl_entity);
              ui_set_next_palette(rd_palette_from_code(is_frozen ? RD_PaletteCode_NegativePopButton : RD_PaletteCode_PositivePopButton));
              if(is_frozen && ui_clicked(rd_icon_buttonf(RD_IconKind_Locked, 0, "Thaw###freeze_thaw")))
              {
                rd_cmd(RD_CmdKind_ThawThread, .ctrl_entity = ctrl_entity->handle);
              }
              if(!is_frozen && ui_clicked(rd_icon_buttonf(RD_IconKind_Unlocked, 0, "Freeze###freeze_thaw")))
              {
                rd_cmd(RD_CmdKind_FreezeThread, .ctrl_entity = ctrl_entity->handle);
              }
            }
            
            // rjf: callstack
#if 0
            RD_Palette(RD_PaletteCode_Floating) ui_divider(ui_em(1.f, 1.f));
            if(ctrl_entity->kind == CTRL_EntityKind_Thread) UI_TextPadding(ui_top_font_size()*1.5f)
            {
              DI_Scope *di_scope = di_scope_open();
              CTRL_Entity *thread = ctrl_entity;
              CTRL_Entity *process = ctrl_entity_ancestor_from_kind(thread, CTRL_EntityKind_Process);
              CTRL_Unwind base_unwind = d_query_cached_unwind_from_thread(thread);
              CTRL_CallStack rich_unwind = ctrl_call_stack_from_unwind(scratch.arena, di_scope, process, &base_unwind);
              for(U64 idx = 0; idx < rich_unwind.concrete_frame_count; idx += 1)
              {
                CTRL_CallStackFrame *f = &rich_unwind.frames[idx];
                RDI_Parsed *rdi = f->rdi;
                RDI_Procedure *procedure = f->procedure;
                U64 rip_vaddr = regs_rip_from_arch_block(thread->arch, f->regs);
                CTRL_Entity *module = ctrl_module_from_process_vaddr(process, rip_vaddr);
                String8 module_name = module == &ctrl_entity_nil ? str8_lit("???") : str8_skip_last_slash(module->string);
                
                // rjf: inline frames
                for(CTRL_CallStackInlineFrame *fin = f->last_inline_frame; fin != 0; fin = fin->prev)
                {
                  UI_Box *row = ui_build_box_from_stringf(UI_BoxFlag_Clickable|UI_BoxFlag_ClickToFocus, "###callstack_row_%I64x", idx);
                  UI_Signal sig = ui_signal_from_box(row);
                  ui_push_parent(row);
                  String8 name = {0};
                  name.str = rdi_string_from_idx(rdi, fin->inline_site->name_string_idx, &name.size);
                  UI_TextAlignment(UI_TextAlign_Left) RD_Font(RD_FontSlot_Code) UI_FlagsAdd(UI_BoxFlag_DrawTextWeak) UI_PrefWidth(ui_em(16.f, 1)) ui_labelf("0x%I64x", rip_vaddr);
                  RD_Font(RD_FontSlot_Code) UI_FlagsAdd(UI_BoxFlag_DrawTextWeak) UI_PrefWidth(ui_text_dim(10, 1)) ui_label(str8_lit("[inlined]"));
                  if(name.size != 0)
                  {
                    RD_Font(RD_FontSlot_Code) UI_PrefWidth(ui_text_dim(10, 1))
                    {
                      rd_code_label(1.f, 0, rd_rgba_from_theme_color(RD_ThemeColor_CodeSymbol), name);
                    }
                  }
                  else
                  {
                    RD_Font(RD_FontSlot_Code) UI_FlagsAdd(UI_BoxFlag_DrawTextWeak) UI_PrefWidth(ui_text_dim(10, 1)) ui_labelf("[??? in %S]", module_name);
                  }
                  ui_pop_parent();
                }
                
                // rjf: concrete frame
                {
                  UI_Box *row = ui_build_box_from_stringf(UI_BoxFlag_Clickable|UI_BoxFlag_ClickToFocus, "###callstack_row_%I64x", idx);
                  UI_Signal sig = ui_signal_from_box(row);
                  ui_push_parent(row);
                  String8 name = {0};
                  name.str = rdi_name_from_procedure(rdi, procedure, &name.size);
                  UI_TextAlignment(UI_TextAlign_Left) RD_Font(RD_FontSlot_Code) UI_FlagsAdd(UI_BoxFlag_DrawTextWeak) UI_PrefWidth(ui_em(16.f, 1)) ui_labelf("0x%I64x", rip_vaddr);
                  if(name.size != 0)
                  {
                    RD_Font(RD_FontSlot_Code) UI_PrefWidth(ui_text_dim(10, 1))
                    {
                      rd_code_label(1.f, 0, rd_rgba_from_theme_color(RD_ThemeColor_CodeSymbol), name);
                    }
                  }
                  else
                  {
                    RD_Font(RD_FontSlot_Code) UI_FlagsAdd(UI_BoxFlag_DrawTextWeak) UI_PrefWidth(ui_text_dim(10, 1)) ui_labelf("[??? in %S]", module_name);
                  }
                  ui_pop_parent();
                }
              }
              di_scope_close(di_scope);
            }
#endif
            
            // rjf: color editor
#if 0
            RD_Palette(RD_PaletteCode_Floating) ui_divider(ui_em(1.f, 1.f));
            {
              UI_Padding(ui_em(1.5f, 1.f))
              {
                ui_set_next_pref_height(ui_em(9.f, 1.f));
                UI_Row UI_Padding(ui_pct(1, 0))
                {
                  UI_PrefWidth(ui_em(1.5f, 1.f)) UI_PrefHeight(ui_em(9.f, 1.f)) UI_Column UI_PrefHeight(ui_em(1.5f, 0.f))
                  {
                    Vec4F32 presets[] =
                    {
                      v4f32(1.0f, 0.2f, 0.1f, 1.0f),
                      v4f32(1.0f, 0.8f, 0.2f, 1.0f),
                      v4f32(0.3f, 0.8f, 0.2f, 1.0f),
                      v4f32(0.1f, 0.8f, 0.4f, 1.0f),
                      v4f32(0.1f, 0.6f, 0.8f, 1.0f),
                      v4f32(0.5f, 0.3f, 0.8f, 1.0f),
                      v4f32(0.8f, 0.3f, 0.5f, 1.0f),
                    };
                    UI_CornerRadius(ui_em(0.3f, 1.f).value)
                      for(U64 preset_idx = 0; preset_idx < ArrayCount(presets); preset_idx += 1)
                    {
                      ui_set_next_hover_cursor(OS_Cursor_HandPoint);
                      ui_set_next_palette(ui_build_palette(ui_top_palette(), .background = presets[preset_idx]));
                      UI_Box *box = ui_build_box_from_stringf(UI_BoxFlag_DrawBackground|
                                                              UI_BoxFlag_DrawBorder|
                                                              UI_BoxFlag_Clickable|
                                                              UI_BoxFlag_DrawHotEffects|
                                                              UI_BoxFlag_DrawActiveEffects,
                                                              "###color_preset_%i", (int)preset_idx);
                      UI_Signal sig = ui_signal_from_box(box);
                      if(ui_clicked(sig))
                      {
                        Vec3F32 hsv = hsv_from_rgb(v3f32(presets[preset_idx].x, presets[preset_idx].y, presets[preset_idx].z));
                        Vec4F32 hsva = v4f32(hsv.x, hsv.y, hsv.z, 1);
                        entity->color_hsva = hsva;
                      }
                      ui_spacer(ui_em(0.3f, 1.f));
                    }
                  }
                  
                  ui_spacer(ui_em(0.75f, 1.f));
                  
                  UI_PrefWidth(ui_em(9.f, 1.f)) UI_PrefHeight(ui_em(9.f, 1.f))
                  {
                    ui_sat_val_pickerf(entity->color_hsva.x, &entity->color_hsva.y, &entity->color_hsva.z, "###ent_satval_picker");
                  }
                  
                  ui_spacer(ui_em(0.75f, 1.f));
                  
                  UI_PrefWidth(ui_em(1.5f, 1.f)) UI_PrefHeight(ui_em(9.f, 1.f))
                    ui_hue_pickerf(&entity->color_hsva.x, entity->color_hsva.y, entity->color_hsva.z, "###ent_hue_picker");
                }
              }
              
              UI_Row UI_Padding(ui_pct(1, 0)) UI_PrefWidth(ui_em(16.f, 1.f)) UI_CornerRadius(8.f) UI_TextAlignment(UI_TextAlign_Center)
                RD_Palette(RD_PaletteCode_Floating)
              {
                if(ui_clicked(rd_icon_buttonf(RD_IconKind_Trash, 0, "Remove Color###color_toggle")))
                {
                  entity->flags &= ~RD_EntityFlag_HasColor;
                }
              }
              
              ui_spacer(ui_em(1.5f, 1.f));
            }
#endif
          }break;
        }
      }
      
      scratch_end(scratch);
    }
    
    ////////////////////////////
    //- rjf: drop-completion context menu
    //
    if(ws->drop_completion_paths.node_count != 0)
    {
      RD_Palette(RD_PaletteCode_Floating) UI_CtxMenu(rd_state->drop_completion_key)
        RD_Palette(RD_PaletteCode_ImplicitButton)
        UI_PrefWidth(ui_em(40.f, 1.f))
      {
        UI_FlagsAdd(UI_BoxFlag_DrawTextWeak)
          for(String8Node *n = ws->drop_completion_paths.first; n != 0; n = n->next)
        {
          UI_Row UI_Padding(ui_em(1.f, 1.f))
          {
            UI_PrefWidth(ui_em(2.f, 1.f)) RD_Font(RD_FontSlot_Icons) ui_label(rd_icon_kind_text_table[RD_IconKind_FileOutline]);
            UI_PrefWidth(ui_text_dim(10, 1)) ui_label(n->string);
          }
        }
        RD_Palette(RD_PaletteCode_Floating) ui_divider(ui_em(1.f, 1.f));
        if(ui_clicked(rd_icon_buttonf(RD_IconKind_Target, 0, "Add File%s As Target%s",
                                      (ws->drop_completion_paths.node_count > 1) ? "s" : "",
                                      (ws->drop_completion_paths.node_count > 1) ? "s" : "")))
        {
          for(String8Node *n = ws->drop_completion_paths.first; n != 0; n = n->next)
          {
            rd_cmd(RD_CmdKind_AddTarget, .file_path = n->string);
          }
          ui_ctx_menu_close();
        }
        if(ws->drop_completion_paths.node_count == 1)
        {
          if(ui_clicked(rd_icon_buttonf(RD_IconKind_Play, 0, "Add File%s As Target%s And Run",
                                        (ws->drop_completion_paths.node_count > 1) ? "s" : "",
                                        (ws->drop_completion_paths.node_count > 1) ? "s" : "")))
          {
            for(String8Node *n = ws->drop_completion_paths.first; n != 0; n = n->next)
            {
              rd_cmd(RD_CmdKind_AddTarget, .file_path = n->string);
            }
            CTRL_EntityList processes = ctrl_entity_list_from_kind(d_state->ctrl_entity_store, CTRL_EntityKind_Process);
            if(processes.count != 0)
            {
              rd_cmd(RD_CmdKind_KillAll);
            }
            rd_cmd(RD_CmdKind_Run);
            ui_ctx_menu_close();
          }
        }
        if(ws->drop_completion_paths.node_count == 1)
        {
          if(ui_clicked(rd_icon_buttonf(RD_IconKind_StepInto, 0, "Add File%s As Target%s And Step Into",
                                        (ws->drop_completion_paths.node_count > 1) ? "s" : "",
                                        (ws->drop_completion_paths.node_count > 1) ? "s" : "")))
          {
            for(String8Node *n = ws->drop_completion_paths.first; n != 0; n = n->next)
            {
              rd_cmd(RD_CmdKind_AddTarget, .file_path = n->string);
            }
            CTRL_EntityList processes = ctrl_entity_list_from_kind(d_state->ctrl_entity_store, CTRL_EntityKind_Process);
            if(processes.count != 0)
            {
              rd_cmd(RD_CmdKind_KillAll);
            }
            rd_cmd(RD_CmdKind_StepInto);
            ui_ctx_menu_close();
          }
        }
        if(ui_clicked(rd_icon_buttonf(RD_IconKind_Target, 0, "View File%s",
                                      (ws->drop_completion_paths.node_count > 1) ? "s" : "")))
        {
          for(String8Node *n = ws->drop_completion_paths.first; n != 0; n = n->next)
          {
            rd_cmd(RD_CmdKind_Open, .file_path = n->string);
          }
          ui_ctx_menu_close();
        }
      }
    }
    
    ////////////////////////////
    //- rjf: popup
    //
    {
      if(rd_state->popup_t > 0.005f) UI_TextAlignment(UI_TextAlign_Center) UI_Focus(rd_state->popup_active ? UI_FocusKind_Root : UI_FocusKind_Off)
      {
        Vec2F32 window_dim = dim_2f32(window_rect);
        UI_Box *bg_box = &ui_nil_box;
        UI_Palette *palette = ui_build_palette(rd_palette_from_code(RD_PaletteCode_Floating));
        palette->background.w *= rd_state->popup_t;
        UI_Rect(window_rect)
          UI_ChildLayoutAxis(Axis2_X)
          UI_Focus(UI_FocusKind_On)
          UI_BlurSize(10*rd_state->popup_t)
          UI_Palette(palette)
        {
          bg_box = ui_build_box_from_stringf(UI_BoxFlag_FixedSize|
                                             UI_BoxFlag_Floating|
                                             UI_BoxFlag_Clickable|
                                             UI_BoxFlag_Scroll|
                                             UI_BoxFlag_DefaultFocusNav|
                                             UI_BoxFlag_DisableFocusOverlay|
                                             UI_BoxFlag_DrawBackgroundBlur|
                                             UI_BoxFlag_DrawBackground, "###popup_%p", ws);
        }
        if(rd_state->popup_active) UI_Parent(bg_box) UI_Transparency(1-rd_state->popup_t)
        {
          ui_ctx_menu_close();
          UI_WidthFill UI_PrefHeight(ui_children_sum(1.f)) UI_Column UI_Padding(ui_pct(1, 0))
          {
            UI_TextRasterFlags(rd_raster_flags_from_slot(RD_FontSlot_Main)) UI_FontSize(ui_top_font_size()*2.f) UI_PrefHeight(ui_em(3.f, 1.f)) ui_label(rd_state->popup_title);
            UI_PrefHeight(ui_em(3.f, 1.f)) UI_FlagsAdd(UI_BoxFlag_DrawTextWeak) ui_label(rd_state->popup_desc);
            ui_spacer(ui_em(1.5f, 1.f));
            UI_Row UI_Padding(ui_pct(1.f, 0.f)) UI_PrefWidth(ui_em(16.f, 1.f)) UI_PrefHeight(ui_em(3.5f, 1.f)) UI_CornerRadius(ui_top_font_size()*0.5f)
            {
              RD_Palette(RD_PaletteCode_NeutralPopButton)
                if(ui_clicked(ui_buttonf("OK")) || (ui_key_match(bg_box->default_nav_focus_hot_key, ui_key_zero()) && ui_slot_press(UI_EventActionSlot_Accept)))
              {
                rd_cmd(RD_CmdKind_PopupAccept);
              }
              ui_spacer(ui_em(1.f, 1.f));
              if(ui_clicked(ui_buttonf("Cancel")) || ui_slot_press(UI_EventActionSlot_Cancel))
              {
                rd_cmd(RD_CmdKind_PopupCancel);
              }
            }
            ui_spacer(ui_em(3.f, 1.f));
          }
        }
        ui_signal_from_box(bg_box);
      }
    }
    
    ////////////////////////////
    //- rjf: top bar
    //
    ProfScope("build top bar")
    {
      os_window_clear_custom_border_data(ws->os);
      os_window_push_custom_edges(ws->os, window_edge_px);
      os_window_push_custom_title_bar(ws->os, dim_2f32(top_bar_rect).y);
      ui_set_next_flags(UI_BoxFlag_DefaultFocusNav|UI_BoxFlag_DisableFocusOverlay);
      RD_Palette(RD_PaletteCode_MenuBar)
        UI_Focus((ws->menu_bar_focused && window_is_focused && !ui_any_ctx_menu_is_open() && !ws->hover_eval_focused) ? UI_FocusKind_On : UI_FocusKind_Null)
        UI_Pane(top_bar_rect, str8_lit("###top_bar"))
        UI_WidthFill UI_Row
        UI_Focus(UI_FocusKind_Null)
      {
        UI_Key menu_bar_group_key = ui_key_from_string(ui_key_zero(), str8_lit("###top_bar_group"));
        MemoryZeroArray(ui_top_parent()->parent->corner_radii);
        
        //- rjf: left column
        ui_set_next_flags(UI_BoxFlag_Clip|UI_BoxFlag_ViewScrollX|UI_BoxFlag_ViewClamp);
        UI_WidthFill UI_NamedRow(str8_lit("###menu_bar"))
        {
          //- rjf: icon
          UI_Padding(ui_em(0.5f, 1.f))
          {
            UI_PrefWidth(ui_px(dim_2f32(top_bar_rect).y - ui_top_font_size()*0.8f, 1.f))
              UI_Column
              UI_Padding(ui_em(0.4f, 1.f))
              UI_HeightFill
            {
              R_Handle texture = rd_state->icon_texture;
              Vec2S32 texture_dim = r_size_from_tex2d(texture);
              ui_image(texture, R_Tex2DSampleKind_Linear, r2f32p(0, 0, texture_dim.x, texture_dim.y), v4f32(1, 1, 1, 1), 0, str8_lit(""));
            }
          }
          
          //- rjf: menu items
          ui_set_next_flags(UI_BoxFlag_DrawBackground);
          UI_PrefWidth(ui_children_sum(1)) UI_Row UI_PrefWidth(ui_text_dim(20, 1)) UI_GroupKey(menu_bar_group_key)
          {
            // rjf: file menu
            UI_Key file_menu_key = ui_key_from_string(ui_key_zero(), str8_lit("_file_menu_key_"));
            RD_Palette(RD_PaletteCode_Floating)
              UI_CtxMenu(file_menu_key)
              UI_PrefWidth(ui_em(50.f, 1.f))
              RD_Palette(RD_PaletteCode_ImplicitButton)
            {
              String8 cmds[] =
              {
                rd_cmd_kind_info_table[RD_CmdKind_Open].string,
                rd_cmd_kind_info_table[RD_CmdKind_OpenUser].string,
                rd_cmd_kind_info_table[RD_CmdKind_OpenProject].string,
                rd_cmd_kind_info_table[RD_CmdKind_OpenRecentProject].string,
                rd_cmd_kind_info_table[RD_CmdKind_Exit].string,
              };
              U32 codepoints[] =
              {
                'o',
                'u',
                'p',
                'r',
                'x',
              };
              Assert(ArrayCount(codepoints) == ArrayCount(cmds));
              rd_cmd_list_menu_buttons(ArrayCount(cmds), cmds, codepoints);
            }
            
            // rjf: window menu
            UI_Key window_menu_key = ui_key_from_string(ui_key_zero(), str8_lit("_window_menu_key_"));
            RD_Palette(RD_PaletteCode_Floating)
              UI_CtxMenu(window_menu_key)
              UI_PrefWidth(ui_em(50.f, 1.f))
              RD_Palette(RD_PaletteCode_ImplicitButton)
            {
              String8 cmds[] =
              {
                rd_cmd_kind_info_table[RD_CmdKind_OpenWindow].string,
                rd_cmd_kind_info_table[RD_CmdKind_CloseWindow].string,
                rd_cmd_kind_info_table[RD_CmdKind_ToggleFullscreen].string,
              };
              U32 codepoints[] =
              {
                'w',
                'c',
                'f',
              };
              Assert(ArrayCount(codepoints) == ArrayCount(cmds));
              rd_cmd_list_menu_buttons(ArrayCount(cmds), cmds, codepoints);
            }
            
            // rjf: panel menu
            UI_Key panel_menu_key = ui_key_from_string(ui_key_zero(), str8_lit("_panel_menu_key_"));
            RD_Palette(RD_PaletteCode_Floating)
              UI_CtxMenu(panel_menu_key)
              UI_PrefWidth(ui_em(50.f, 1.f))
              RD_Palette(RD_PaletteCode_ImplicitButton)
            {
              String8 cmds[] =
              {
                rd_cmd_kind_info_table[RD_CmdKind_NewPanelUp].string,
                rd_cmd_kind_info_table[RD_CmdKind_NewPanelDown].string,
                rd_cmd_kind_info_table[RD_CmdKind_NewPanelRight].string,
                rd_cmd_kind_info_table[RD_CmdKind_NewPanelLeft].string,
                rd_cmd_kind_info_table[RD_CmdKind_ClosePanel].string,
                rd_cmd_kind_info_table[RD_CmdKind_RotatePanelColumns].string,
                rd_cmd_kind_info_table[RD_CmdKind_NextPanel].string,
                rd_cmd_kind_info_table[RD_CmdKind_PrevPanel].string,
                rd_cmd_kind_info_table[RD_CmdKind_CloseTab].string,
                rd_cmd_kind_info_table[RD_CmdKind_NextTab].string,
                rd_cmd_kind_info_table[RD_CmdKind_PrevTab].string,
                rd_cmd_kind_info_table[RD_CmdKind_TabBarTop].string,
                rd_cmd_kind_info_table[RD_CmdKind_TabBarBottom].string,
                rd_cmd_kind_info_table[RD_CmdKind_ResetToDefaultPanels].string,
                rd_cmd_kind_info_table[RD_CmdKind_ResetToCompactPanels].string,
              };
              U32 codepoints[] =
              {
                'u',
                'd',
                'r',
                'l',
                'x',
                'c',
                'n',
                'p',
                't',
                'b',
                'v',
                0,
                0,
                0,
                0,
              };
              Assert(ArrayCount(codepoints) == ArrayCount(cmds));
              rd_cmd_list_menu_buttons(ArrayCount(cmds), cmds, codepoints);
            }
            
            // rjf: view menu
            UI_Key view_menu_key = ui_key_from_string(ui_key_zero(), str8_lit("_view_menu_key_"));
            RD_Palette(RD_PaletteCode_Floating)
              UI_CtxMenu(view_menu_key)
              UI_PrefWidth(ui_em(50.f, 1.f))
              RD_Palette(RD_PaletteCode_ImplicitButton)
            {
              String8 cmds[] =
              {
                rd_cmd_kind_info_table[RD_CmdKind_Targets].string,
                rd_cmd_kind_info_table[RD_CmdKind_Scheduler].string,
                rd_cmd_kind_info_table[RD_CmdKind_CallStack].string,
                rd_cmd_kind_info_table[RD_CmdKind_Modules].string,
                rd_cmd_kind_info_table[RD_CmdKind_Output].string,
                rd_cmd_kind_info_table[RD_CmdKind_Memory].string,
                rd_cmd_kind_info_table[RD_CmdKind_Disassembly].string,
                rd_cmd_kind_info_table[RD_CmdKind_Watch].string,
                rd_cmd_kind_info_table[RD_CmdKind_Locals].string,
                rd_cmd_kind_info_table[RD_CmdKind_Registers].string,
                rd_cmd_kind_info_table[RD_CmdKind_Globals].string,
                rd_cmd_kind_info_table[RD_CmdKind_ThreadLocals].string,
                rd_cmd_kind_info_table[RD_CmdKind_Types].string,
                rd_cmd_kind_info_table[RD_CmdKind_Procedures].string,
                rd_cmd_kind_info_table[RD_CmdKind_Breakpoints].string,
                rd_cmd_kind_info_table[RD_CmdKind_WatchPins].string,
                rd_cmd_kind_info_table[RD_CmdKind_FilePathMap].string,
                rd_cmd_kind_info_table[RD_CmdKind_AutoViewRules].string,
                rd_cmd_kind_info_table[RD_CmdKind_Settings].string,
                rd_cmd_kind_info_table[RD_CmdKind_GettingStarted].string,
              };
              U32 codepoints[] =
              {
                't',
                's',
                'k',
                'd',
                'o',
                'm',
                'y',
                'w',
                'l',
                'r',
                0,
                0,
                0,
                0,
                'b',
                'h',
                'p',
                'v',
                'g',
                0,
              };
              Assert(ArrayCount(codepoints) == ArrayCount(cmds));
              rd_cmd_list_menu_buttons(ArrayCount(cmds), cmds, codepoints);
            }
            
            // rjf: targets menu
            UI_Key targets_menu_key = ui_key_from_string(ui_key_zero(), str8_lit("_targets_menu_key_"));
            RD_Palette(RD_PaletteCode_Floating)
              UI_CtxMenu(targets_menu_key)
              UI_PrefWidth(ui_em(50.f, 1.f))
              RD_Palette(RD_PaletteCode_ImplicitButton)
            {
              Temp scratch = scratch_begin(0, 0);
              String8 cmds[] =
              {
                rd_cmd_kind_info_table[RD_CmdKind_AddTarget].string,
              };
              U32 codepoints[] =
              {
                'a',
              };
              Assert(ArrayCount(codepoints) == ArrayCount(cmds));
              rd_cmd_list_menu_buttons(ArrayCount(cmds), cmds, codepoints);
              scratch_end(scratch);
            }
            
            // rjf: ctrl menu
            UI_Key ctrl_menu_key = ui_key_from_string(ui_key_zero(), str8_lit("_ctrl_menu_key_"));
            RD_Palette(RD_PaletteCode_Floating)
              UI_CtxMenu(ctrl_menu_key)
              UI_PrefWidth(ui_em(50.f, 1.f))
              RD_Palette(RD_PaletteCode_ImplicitButton)
            {
              String8 cmds[] =
              {
                rd_cmd_kind_info_table[D_CmdKind_Run].string,
                rd_cmd_kind_info_table[D_CmdKind_KillAll].string,
                rd_cmd_kind_info_table[D_CmdKind_Restart].string,
                rd_cmd_kind_info_table[D_CmdKind_Halt].string,
                rd_cmd_kind_info_table[D_CmdKind_SoftHaltRefresh].string,
                rd_cmd_kind_info_table[D_CmdKind_StepInto].string,
                rd_cmd_kind_info_table[D_CmdKind_StepOver].string,
                rd_cmd_kind_info_table[D_CmdKind_StepOut].string,
                rd_cmd_kind_info_table[D_CmdKind_Attach].string,
              };
              U32 codepoints[] =
              {
                'r',
                'k',
                's',
                'h',
                'f',
                'i',
                'o',
                't',
                'a',
              };
              Assert(ArrayCount(codepoints) == ArrayCount(cmds));
              rd_cmd_list_menu_buttons(ArrayCount(cmds), cmds, codepoints);
            }
            
            // rjf: help menu
            UI_Key help_menu_key = ui_key_from_string(ui_key_zero(), str8_lit("_help_menu_key_"));
            RD_Palette(RD_PaletteCode_Floating)
              UI_CtxMenu(help_menu_key)
              UI_PrefWidth(ui_em(50.f, 1.f))
              RD_Palette(RD_PaletteCode_ImplicitButton)
            {
              UI_Row UI_TextAlignment(UI_TextAlign_Center) UI_FlagsAdd(UI_BoxFlag_DrawTextWeak)
                ui_label(str8_lit(BUILD_TITLE_STRING_LITERAL));
              ui_spacer(ui_em(1.f, 1.f));
              UI_PrefHeight(ui_children_sum(1)) UI_Row UI_Padding(ui_pct(1, 0))
              {
                R_Handle texture = rd_state->icon_texture;
                Vec2S32 texture_dim = r_size_from_tex2d(texture);
                UI_PrefWidth(ui_px(ui_top_font_size()*10.f, 1.f))
                  UI_PrefHeight(ui_px(ui_top_font_size()*10.f, 1.f))
                  ui_image(texture, R_Tex2DSampleKind_Linear, r2f32p(0, 0, texture_dim.x, texture_dim.y), v4f32(1, 1, 1, 1), 0, str8_lit(""));
              }
              ui_spacer(ui_em(1.f, 1.f));
              UI_Row
                UI_PrefWidth(ui_text_dim(10, 1))
                UI_TextAlignment(UI_TextAlign_Center)
                UI_Padding(ui_pct(1, 0))
              {
                ui_labelf("Search for commands by pressing ");
                UI_Flags(UI_BoxFlag_DrawBorder)
                  UI_TextAlignment(UI_TextAlign_Center)
                  rd_cmd_binding_buttons(rd_cmd_kind_info_table[RD_CmdKind_RunCommand].string);
              }
              ui_spacer(ui_em(1.f, 1.f));
              RD_Palette(RD_PaletteCode_NeutralPopButton)
                UI_Row UI_Padding(ui_pct(1, 0)) UI_TextAlignment(UI_TextAlign_Center) UI_PrefWidth(ui_text_dim(10, 1))
                UI_CornerRadius(ui_top_font_size()*0.5f)
              {
                String8 url = str8_lit("https://github.com/EpicGamesExt/raddebugger/issues");
                UI_Signal sig = ui_button(str8_lit("Submit Request, Issue, or Bug Report"));
                if(ui_clicked(sig))
                {
                  os_open_in_browser(url);
                }
              }
              ui_spacer(ui_em(0.5f, 1.f));
            }
            
            // rjf: buttons
            UI_TextAlignment(UI_TextAlign_Center) UI_HeightFill
            {
              // rjf: set up table
              struct
              {
                String8 name;
                U32 codepoint;
                OS_Key key;
                UI_Key menu_key;
              }
              items[] =
              {
                {str8_lit("File"),     'f', OS_Key_F, file_menu_key},
                {str8_lit("Window"),   'w', OS_Key_W, window_menu_key},
                {str8_lit("Panel"),    'p', OS_Key_P, panel_menu_key},
                {str8_lit("View"),     'v', OS_Key_V, view_menu_key},
                {str8_lit("Targets"),  't', OS_Key_T, targets_menu_key},
                {str8_lit("Control"),  'c', OS_Key_C, ctrl_menu_key},
                {str8_lit("Help"),     'h', OS_Key_H, help_menu_key},
              };
              
              // rjf: determine if one of the menus is already open
              B32 menu_open = 0;
              U64 open_menu_idx = 0;
              for(U64 idx = 0; idx < ArrayCount(items); idx += 1)
              {
                if(ui_ctx_menu_is_open(items[idx].menu_key))
                {
                  menu_open = 1;
                  open_menu_idx = idx;
                  break;
                }
              }
              
              // rjf: navigate between menus
              U64 open_menu_idx_prime = open_menu_idx;
              if(menu_open && ws->menu_bar_focused && window_is_focused)
              {
                for(UI_Event *evt = 0; ui_next_event(&evt);)
                {
                  B32 taken = 0;
                  if(evt->delta_2s32.x > 0)
                  {
                    taken = 1;
                    open_menu_idx_prime += 1;
                    open_menu_idx_prime = open_menu_idx_prime%ArrayCount(items);
                  }
                  if(evt->delta_2s32.x < 0)
                  {
                    taken = 1;
                    open_menu_idx_prime = open_menu_idx_prime > 0 ? open_menu_idx_prime-1 : (ArrayCount(items)-1);
                  }
                  if(taken)
                  {
                    ui_eat_event(evt);
                  }
                }
              }
              
              // rjf: make ui
              for(U64 idx = 0; idx < ArrayCount(items); idx += 1)
              {
                ui_set_next_fastpath_codepoint(items[idx].codepoint);
                B32 alt_fastpath_key = 0;
                if(ui_key_press(OS_Modifier_Alt, items[idx].key))
                {
                  alt_fastpath_key = 1;
                }
                if((ws->menu_bar_key_held || ws->menu_bar_focused) && !ui_any_ctx_menu_is_open())
                {
                  ui_set_next_flags(UI_BoxFlag_DrawTextFastpathCodepoint);
                }
                UI_Signal sig = rd_menu_bar_button(items[idx].name);
                os_window_push_custom_title_bar_client_area(ws->os, sig.box->rect);
                if(menu_open)
                {
                  if((ui_hovering(sig) && !ui_ctx_menu_is_open(items[idx].menu_key)) || (open_menu_idx_prime == idx && open_menu_idx_prime != open_menu_idx))
                  {
                    ui_ctx_menu_open(items[idx].menu_key, sig.box->key, v2f32(0, sig.box->rect.y1-sig.box->rect.y0));
                  }
                }
                else if(ui_pressed(sig) || alt_fastpath_key)
                {
                  if(ui_ctx_menu_is_open(items[idx].menu_key))
                  {
                    ui_ctx_menu_close();
                  }
                  else
                  {
                    ui_ctx_menu_open(items[idx].menu_key, sig.box->key, v2f32(0, sig.box->rect.y1-sig.box->rect.y0));
                  }
                }
              }
            }
          }
          
          ui_spacer(ui_em(0.75f, 1));
          
          // rjf: conversion task visualization
          UI_PrefWidth(ui_text_dim(10, 1)) UI_HeightFill
            RD_Palette(RD_PaletteCode_NeutralPopButton)
          {
            Temp scratch = scratch_begin(0, 0);
            RD_CfgList tasks = rd_cfg_top_level_list_from_string(scratch.arena, str8_lit("conversion_task"));
            for(RD_CfgNode *n = tasks.first; n != 0; n = n->next)
            {
              RD_Cfg *task = n->v;
              F32 task_t = ui_anim(ui_key_from_stringf(ui_key_zero(), "task_anim_%p_%I64u", task, task->gen), 1.f);
              if(task_t > 0.5f)
              {
                String8 rdi_path = task->first->string;
                String8 rdi_name = str8_skip_last_slash(rdi_path);
                String8 task_text = push_str8f(scratch.arena, "Creating %S...", rdi_name);
                UI_Key key = ui_key_from_stringf(ui_key_zero(), "task_%p", task);
                UI_Box *box = ui_build_box_from_key(UI_BoxFlag_DrawHotEffects|UI_BoxFlag_DrawText|UI_BoxFlag_DrawBorder|UI_BoxFlag_DrawBackground|UI_BoxFlag_Clickable, key);
                os_window_push_custom_title_bar_client_area(ws->os, box->rect);
                UI_Signal sig = ui_signal_from_box(box);
                if(ui_hovering(sig)) UI_Tooltip
                {
                  ui_label(rdi_path);
                }
                ui_box_equip_display_string(box, task_text);
              }
            }
            scratch_end(scratch);
          }
        }
        
        //- rjf: center column
        UI_PrefWidth(ui_children_sum(1.f)) UI_Row
          UI_PrefWidth(ui_em(2.25f, 1))
          RD_Font(RD_FontSlot_Icons)
          UI_FontSize(ui_top_font_size()*0.85f)
        {
          Temp scratch = scratch_begin(0, 0);
          RD_CfgList targets = rd_cfg_top_level_list_from_string(scratch.arena, str8_lit("target"));
          CTRL_EntityList processes = ctrl_entity_list_from_kind(d_state->ctrl_entity_store, CTRL_EntityKind_Process);
          B32 have_targets = (targets.count != 0);
          B32 can_send_signal = !d_ctrl_targets_running();
          B32 can_play  = (have_targets && (can_send_signal || d_ctrl_last_run_frame_idx()+4 > d_frame_index()));
          B32 can_pause = (!can_send_signal);
          B32 can_stop  = (processes.count != 0);
          B32 can_step =  (processes.count != 0 && can_send_signal);
          
          //- rjf: play button
          if(can_play || !have_targets || processes.count == 0)
            UI_TextAlignment(UI_TextAlign_Center)
            UI_Flags((can_play ? 0 : UI_BoxFlag_Disabled))
            UI_Palette(ui_build_palette(ui_top_palette(), .text = rd_rgba_from_theme_color(RD_ThemeColor_TextPositive)))
          {
            UI_Signal sig = ui_button(rd_icon_kind_text_table[RD_IconKind_Play]);
            os_window_push_custom_title_bar_client_area(ws->os, sig.box->rect);
            if(ui_hovering(sig) && !can_play)
            {
              UI_Tooltip
                RD_Font(RD_FontSlot_Main)
                UI_FontSize(rd_font_size_from_slot(RD_FontSlot_Main))
                ui_labelf("Disabled: %s", have_targets ? "Targets are currently running" : "No active targets exist");
            }
            if(ui_hovering(sig) && can_play)
            {
              UI_Tooltip
                RD_Font(RD_FontSlot_Main)
                UI_FontSize(rd_font_size_from_slot(RD_FontSlot_Main))
              {
                if(can_stop)
                {
                  ui_labelf("Resume all processes");
                }
                else
                {
                  ui_labelf("Launch all active targets:");
                  for(RD_CfgNode *n = targets.first; n != 0; n = n->next)
                  {
                    RD_Cfg *target = n->v;
                    DR_FancyStringList title_fstrs = rd_title_fstrs_from_cfg(ui_build_arena(), target, ui_top_palette()->text_weak, ui_top_font_size());
                    UI_Box *box = ui_build_box_from_key(UI_BoxFlag_DrawText, ui_key_zero());
                    ui_box_equip_display_fancy_strings(box, &title_fstrs);
                  }
                }
              }
            }
            if(ui_clicked(sig))
            {
              rd_cmd(RD_CmdKind_Run);
            }
          }
          
          //- rjf: restart button
          else UI_TextAlignment(UI_TextAlign_Center)
            UI_Palette(ui_build_palette(ui_top_palette(), .text = rd_rgba_from_theme_color(RD_ThemeColor_TextPositive)))
          {
            UI_Signal sig = ui_button(rd_icon_kind_text_table[RD_IconKind_Redo]);
            os_window_push_custom_title_bar_client_area(ws->os, sig.box->rect);
            if(ui_hovering(sig))
            {
              UI_Tooltip
                RD_Font(RD_FontSlot_Main)
                UI_FontSize(rd_font_size_from_slot(RD_FontSlot_Main))
              {
                ui_labelf("Restart");
              }
            }
            if(ui_clicked(sig))
            {
              rd_cmd(RD_CmdKind_Restart);
            }
          }
          
          //- rjf: pause button
          UI_TextAlignment(UI_TextAlign_Center) UI_Flags(can_pause ? 0 : UI_BoxFlag_Disabled)
            UI_Palette(ui_build_palette(ui_top_palette(), .text = rd_rgba_from_theme_color(RD_ThemeColor_TextNeutral)))
          {
            UI_Signal sig = ui_button(rd_icon_kind_text_table[RD_IconKind_Pause]);
            os_window_push_custom_title_bar_client_area(ws->os, sig.box->rect);
            if(ui_hovering(sig) && !can_pause)
            {
              UI_Tooltip
                RD_Font(RD_FontSlot_Main)
                UI_FontSize(rd_font_size_from_slot(RD_FontSlot_Main))
                ui_labelf("Disabled: Already halted");
            }
            if(ui_hovering(sig) && can_pause)
            {
              UI_Tooltip
                RD_Font(RD_FontSlot_Main)
                UI_FontSize(rd_font_size_from_slot(RD_FontSlot_Main))
                ui_labelf("Halt all attached processes");
            }
            if(ui_clicked(sig))
            {
              rd_cmd(RD_CmdKind_Halt);
            }
          }
          
          //- rjf: stop button
          UI_TextAlignment(UI_TextAlign_Center) UI_Flags(can_stop ? 0 : UI_BoxFlag_Disabled)
            UI_Palette(ui_build_palette(ui_top_palette(), .text = rd_rgba_from_theme_color(RD_ThemeColor_TextNegative)))
          {
            UI_Signal sig = {0};
            {
              sig = ui_button(rd_icon_kind_text_table[RD_IconKind_Stop]);
              os_window_push_custom_title_bar_client_area(ws->os, sig.box->rect);
            }
            if(ui_hovering(sig) && !can_stop)
            {
              UI_Tooltip
                RD_Font(RD_FontSlot_Main)
                UI_FontSize(rd_font_size_from_slot(RD_FontSlot_Main))
                ui_labelf("Disabled: No processes are running");
            }
            if(ui_hovering(sig) && can_stop)
            {
              UI_Tooltip
                RD_Font(RD_FontSlot_Main)
                UI_FontSize(rd_font_size_from_slot(RD_FontSlot_Main))
                ui_labelf("Kill all attached processes");
            }
            if(ui_clicked(sig))
            {
              rd_cmd(RD_CmdKind_KillAll);
            }
          }
          
          //- rjf: step over button
          UI_TextAlignment(UI_TextAlign_Center) UI_Flags((can_play ? 0 : UI_BoxFlag_Disabled))
          {
            UI_Signal sig = ui_button(rd_icon_kind_text_table[RD_IconKind_StepOver]);
            os_window_push_custom_title_bar_client_area(ws->os, sig.box->rect);
            if(ui_hovering(sig))
            {
              if(can_play)
              {
                UI_Tooltip
                  RD_Font(RD_FontSlot_Main)
                  UI_FontSize(rd_font_size_from_slot(RD_FontSlot_Main))
                  ui_labelf("Step Over");
              }
              else
              {
                UI_Tooltip
                  RD_Font(RD_FontSlot_Main)
                  UI_FontSize(rd_font_size_from_slot(RD_FontSlot_Main))
                  ui_labelf("Disabled: %s", have_targets ? "Targets are currently running" : "No active targets exist");
              }
            }
            if(ui_clicked(sig))
            {
              rd_cmd(RD_CmdKind_StepOver);
            }
          }
          
          //- rjf: step into button
          UI_TextAlignment(UI_TextAlign_Center) UI_Flags((can_play ? 0 : UI_BoxFlag_Disabled))
          {
            UI_Signal sig = ui_button(rd_icon_kind_text_table[RD_IconKind_StepInto]);
            os_window_push_custom_title_bar_client_area(ws->os, sig.box->rect);
            if(ui_hovering(sig))
            {
              if(can_play)
              {
                UI_Tooltip
                  RD_Font(RD_FontSlot_Main)
                  UI_FontSize(rd_font_size_from_slot(RD_FontSlot_Main))
                  ui_labelf("Step Into");
              }
              else
              {
                UI_Tooltip
                  RD_Font(RD_FontSlot_Main)
                  UI_FontSize(rd_font_size_from_slot(RD_FontSlot_Main))
                  ui_labelf("Disabled: %s", have_targets ? "Targets are currently running" : "No active targets exist");
              }
            }
            if(ui_clicked(sig))
            {
              rd_cmd(RD_CmdKind_StepInto);
            }
          }
          
          //- rjf: step out button
          UI_TextAlignment(UI_TextAlign_Center) UI_Flags(can_step ? 0 : UI_BoxFlag_Disabled)
          {
            UI_Signal sig = ui_button(rd_icon_kind_text_table[RD_IconKind_StepOut]);
            os_window_push_custom_title_bar_client_area(ws->os, sig.box->rect);
            if(ui_hovering(sig) && !can_step && can_pause)
            {
              UI_Tooltip
                RD_Font(RD_FontSlot_Main)
                UI_FontSize(rd_font_size_from_slot(RD_FontSlot_Main))
                ui_labelf("Disabled: Running");
            }
            if(ui_hovering(sig) && !can_step && !can_stop)
            {
              UI_Tooltip
                RD_Font(RD_FontSlot_Main)
                UI_FontSize(rd_font_size_from_slot(RD_FontSlot_Main))
                ui_labelf("Disabled: No processes are running");
            }
            if(ui_hovering(sig) && can_step)
            {
              UI_Tooltip
                RD_Font(RD_FontSlot_Main)
                UI_FontSize(rd_font_size_from_slot(RD_FontSlot_Main))
                ui_labelf("Step Out");
            }
            if(ui_clicked(sig))
            {
              rd_cmd(RD_CmdKind_StepOut);
            }
          }
          
          scratch_end(scratch);
        }
        
        //- rjf: right column
        UI_WidthFill UI_Row
        {
          B32 do_user_prof = (dim_2f32(top_bar_rect).x > ui_top_font_size()*80);
          
          ui_spacer(ui_pct(1, 0));
          
          // rjf: loaded user viz
          if(do_user_prof) RD_Palette(RD_PaletteCode_NeutralPopButton)
          {
            ui_set_next_pref_width(ui_children_sum(1));
            ui_set_next_child_layout_axis(Axis2_X);
            ui_set_next_hover_cursor(OS_Cursor_HandPoint);
            UI_Box *user_box = ui_build_box_from_stringf(UI_BoxFlag_Clickable|
                                                         UI_BoxFlag_DrawBorder|
                                                         UI_BoxFlag_DrawBackground|
                                                         UI_BoxFlag_DrawHotEffects|
                                                         UI_BoxFlag_DrawActiveEffects,
                                                         "###loaded_user_button");
            os_window_push_custom_title_bar_client_area(ws->os, user_box->rect);
            UI_Parent(user_box) UI_PrefWidth(ui_text_dim(10, 0)) UI_TextAlignment(UI_TextAlign_Center)
            {
              String8 user_path = rd_state->user_path;
              user_path = str8_chop_last_dot(user_path);
              RD_Font(RD_FontSlot_Icons)
                UI_TextRasterFlags(rd_raster_flags_from_slot(RD_FontSlot_Icons))
                ui_label(rd_icon_kind_text_table[RD_IconKind_Person]);
              ui_label(str8_skip_last_slash(user_path));
            }
            UI_Signal user_sig = ui_signal_from_box(user_box);
            if(ui_clicked(user_sig))
            {
              rd_cmd(RD_CmdKind_RunCommand, .cmd_name = rd_cmd_kind_info_table[RD_CmdKind_OpenUser].string);
            }
          }
          
          if(do_user_prof)
          {
            ui_spacer(ui_em(0.75f, 0));
          }
          
          // rjf: loaded project viz
          if(do_user_prof) RD_Palette(RD_PaletteCode_NeutralPopButton)
          {
            ui_set_next_pref_width(ui_children_sum(1));
            ui_set_next_child_layout_axis(Axis2_X);
            ui_set_next_hover_cursor(OS_Cursor_HandPoint);
            UI_Box *prof_box = ui_build_box_from_stringf(UI_BoxFlag_Clickable|
                                                         UI_BoxFlag_DrawBorder|
                                                         UI_BoxFlag_DrawBackground|
                                                         UI_BoxFlag_DrawHotEffects|
                                                         UI_BoxFlag_DrawActiveEffects,
                                                         "###loaded_project_button");
            os_window_push_custom_title_bar_client_area(ws->os, prof_box->rect);
            UI_Parent(prof_box) UI_PrefWidth(ui_text_dim(10, 0)) UI_TextAlignment(UI_TextAlign_Center)
            {
              String8 prof_path = rd_state->project_path;
              prof_path = str8_chop_last_dot(prof_path);
              RD_Font(RD_FontSlot_Icons)
                ui_label(rd_icon_kind_text_table[RD_IconKind_Briefcase]);
              ui_label(str8_skip_last_slash(prof_path));
            }
            UI_Signal prof_sig = ui_signal_from_box(prof_box);
            if(ui_clicked(prof_sig))
            {
              rd_cmd(RD_CmdKind_RunCommand, .cmd_name = rd_cmd_kind_info_table[RD_CmdKind_OpenProject].string);
            }
          }
          
          if(do_user_prof)
          {
            ui_spacer(ui_em(0.75f, 0));
          }
          
          // rjf: close dropdown
          UI_Key close_ctx_menu_key = ui_key_from_stringf(ui_key_zero(), "###close_ctx_menu");
          UI_CtxMenu(close_ctx_menu_key) RD_Palette(RD_PaletteCode_ImplicitButton)
          {
            if(ui_clicked(rd_icon_buttonf(RD_IconKind_Window, 0, "Close Window")))
            {
              rd_cmd(RD_CmdKind_CloseWindow);
            }
            if(ui_clicked(rd_icon_buttonf(RD_IconKind_X, 0, "Exit")))
            {
              rd_cmd(RD_CmdKind_Exit);
            }
          }
          
          // rjf: min/max/close buttons
          {
            UI_Signal min_sig = {0};
            UI_Signal max_sig = {0};
            UI_Signal cls_sig = {0};
            Vec2F32 bar_dim = dim_2f32(top_bar_rect);
            F32 button_dim = floor_f32(bar_dim.y);
            UI_PrefWidth(ui_px(button_dim, 1.f))
            {
              min_sig = rd_icon_buttonf(RD_IconKind_Minus,  0, "##minimize");
              max_sig = rd_icon_buttonf(RD_IconKind_Window, 0, "##maximize");
            }
            UI_PrefWidth(ui_px(button_dim, 1.f))
              RD_Palette(RD_PaletteCode_NegativePopButton)
            {
              cls_sig = rd_icon_buttonf(RD_IconKind_X,      0, "##close");
            }
            if(ui_clicked(min_sig))
            {
              os_window_set_minimized(ws->os, 1);
            }
            if(ui_clicked(max_sig))
            {
              os_window_set_maximized(ws->os, !os_window_is_maximized(ws->os));
            }
            if(ui_clicked(cls_sig))
            {
              if(ws->order_next != &rd_nil_window_state ||
                 ws->order_prev != &rd_nil_window_state)
              {
                ui_ctx_menu_open(close_ctx_menu_key, cls_sig.box->key, v2f32(0, dim_2f32(cls_sig.box->rect).y));
              }
              else
              {
                rd_cmd(RD_CmdKind_Exit);
              }
            }
            os_window_push_custom_title_bar_client_area(ws->os, min_sig.box->rect);
            os_window_push_custom_title_bar_client_area(ws->os, max_sig.box->rect);
            os_window_push_custom_title_bar_client_area(ws->os, pad_2f32(cls_sig.box->rect, 2.f));
          }
        }
      }
    }
    
    ////////////////////////////
    //- rjf: bottom bar
    //
    ProfScope("build bottom bar")
    {
      B32 is_running = d_ctrl_targets_running() && d_ctrl_last_run_frame_idx() < d_frame_index();
      CTRL_Event stop_event = d_ctrl_last_stop_event();
      UI_Palette *positive_scheme = rd_palette_from_code(RD_PaletteCode_PositivePopButton);
      UI_Palette *running_scheme  = rd_palette_from_code(RD_PaletteCode_NeutralPopButton);
      UI_Palette *negative_scheme = rd_palette_from_code(RD_PaletteCode_NegativePopButton);
      UI_Palette *palette = running_scheme;
      if(!is_running)
      {
        switch(stop_event.cause)
        {
          default:
          case CTRL_EventCause_Finished:
          {
            palette = positive_scheme;
          }break;
          case CTRL_EventCause_UserBreakpoint:
          case CTRL_EventCause_InterruptedByException:
          case CTRL_EventCause_InterruptedByTrap:
          case CTRL_EventCause_InterruptedByHalt:
          {
            palette = negative_scheme;
          }break;
        }
      }
      if(ws->error_t > 0.01f)
      {
        UI_Palette *blended_scheme = push_array(ui_build_arena(), UI_Palette, 1);
        MemoryCopyStruct(blended_scheme, palette);
        for EachEnumVal(UI_ColorCode, code)
        {
          for(U64 idx = 0; idx < 4; idx += 1)
          {
            blended_scheme->colors[code].v[idx] += (negative_scheme->colors[code].v[idx] - blended_scheme->colors[code].v[idx]) * ws->error_t;
          }
        }
        palette = blended_scheme;
      }
      UI_Flags(UI_BoxFlag_DrawBackground) UI_CornerRadius(0)
        UI_Palette(palette)
        UI_Pane(bottom_bar_rect, str8_lit("###bottom_bar")) UI_WidthFill UI_Row
        UI_Flags(0)
      {
        // rjf: developer frame-time indicator
        if(DEV_updating_indicator)
        {
          F32 animation_t = pow_f32(sin_f32(rd_state->time_in_seconds/2.f), 2.f);
          ui_spacer(ui_em(0.3f, 1.f));
          ui_spacer(ui_em(1.5f*animation_t, 1.f));
          UI_PrefWidth(ui_text_dim(10, 1)) ui_labelf("*");
          ui_spacer(ui_em(1.5f*(1-animation_t), 1.f));
        }
        
        // rjf: status
        {
          ui_spacer(ui_em(1.f, 1.f));
          if(is_running)
          {
            ui_label(str8_lit("Running"));
          }
          else
          {
            Temp scratch = scratch_begin(0, 0);
            DR_FancyStringList explanation_fstrs = rd_stop_explanation_fstrs_from_ctrl_event(scratch.arena, &stop_event);
            UI_Box *box = ui_build_box_from_key(UI_BoxFlag_DrawText, ui_key_zero());
            ui_box_equip_display_fancy_strings(box, &explanation_fstrs);
            scratch_end(scratch);
          }
        }
        
        ui_spacer(ui_pct(1, 0));
        
        // rjf: bind change visualization
        if(rd_state->bind_change_active)
        {
          RD_CmdKindInfo *info = rd_cmd_kind_info_from_string(rd_state->bind_change_cmd_name);
          UI_PrefWidth(ui_text_dim(10, 1))
            UI_Flags(UI_BoxFlag_DrawBackground)
            UI_TextAlignment(UI_TextAlign_Center)
            UI_CornerRadius(4)
            RD_Palette(RD_PaletteCode_NeutralPopButton)
            ui_labelf("Currently rebinding \"%S\" hotkey", info->display_name);
        }
        
        // rjf: error visualization
        else if(ws->error_t >= 0.01f)
        {
          ws->error_t -= rd_state->frame_dt/8.f;
          rd_request_frame();
          String8 error_string = str8(ws->error_buffer, ws->error_string_size);
          if(error_string.size != 0)
          {
            ui_set_next_pref_width(ui_children_sum(1));
            UI_CornerRadius(4)
              UI_Row
              UI_PrefWidth(ui_text_dim(10, 1))
              UI_TextAlignment(UI_TextAlign_Center)
            {
              RD_Font(RD_FontSlot_Icons)
                UI_FontSize(rd_font_size_from_slot(RD_FontSlot_Icons))
                ui_label(rd_icon_kind_text_table[RD_IconKind_WarningBig]);
              rd_label(error_string);
            }
          }
        }
      }
    }
    
    ////////////////////////////
    //- rjf: build hover eval
    //
    ProfScope("build hover eval")
    {
      B32 build_hover_eval = hover_eval_is_open;
      
      // rjf: disable hover eval if hovered view is actively scrolling
      if(hover_eval_is_open)
      {
#if 0 // TODO(rjf): @cfg_panels
        for(RD_Panel *panel = ws->root_panel;
            !rd_panel_is_nil(panel);
            panel = rd_panel_rec_depth_first_pre(panel).next)
        {
          if(!rd_panel_is_nil(panel->first)) { continue; }
          Rng2F32 panel_rect = rd_target_rect_from_panel(content_rect, ws->root_panel, panel);
          RD_View *view = rd_selected_tab_from_panel(panel);
          if(!rd_view_is_nil(view) &&
             contains_2f32(panel_rect, ui_mouse()) &&
             (abs_f32(view->scroll_pos.x.off) > 0.01f ||
              abs_f32(view->scroll_pos.y.off) > 0.01f))
          {
            build_hover_eval = 0;
            ws->hover_eval_first_frame_idx = rd_state->frame_index;
          }
        }
#endif
      }
      
      // rjf: reset open animation
      if(ws->hover_eval_string.size == 0)
      {
        ws->hover_eval_open_t = 0;
        ws->hover_eval_num_visible_rows_t = 0;
      }
      
      // rjf: reset animation, but request frames if we're waiting to open
      if(ws->hover_eval_string.size != 0 && !hover_eval_is_open && ws->hover_eval_last_frame_idx < ws->hover_eval_first_frame_idx+20 && rd_state->frame_index-ws->hover_eval_last_frame_idx < 50)
      {
        rd_request_frame();
        ws->hover_eval_num_visible_rows_t = 0;
        ws->hover_eval_open_t = 0;
      }
      
      // rjf: reset focus state if hover eval is not being built
      if(!build_hover_eval || ws->hover_eval_string.size == 0 || !hover_eval_is_open)
      {
        ws->hover_eval_focused = 0;
      }
      
      // rjf: build hover eval
      if(build_hover_eval && ws->hover_eval_string.size != 0 && hover_eval_is_open)
        RD_Font(RD_FontSlot_Code)
        UI_FontSize(rd_font_size_from_slot(RD_FontSlot_Main))
        RD_Palette(RD_PaletteCode_Floating)
      {
        RD_Cfg *root = rd_immediate_cfg_from_keyf("hover_eval_%p", ws);
        RD_Cfg *view = rd_cfg_child_from_string_or_alloc(root, str8_lit("watch"));
        RD_Cfg *expr = rd_cfg_child_from_string_or_alloc(view, str8_lit("expression"));
        RD_RegsScope(.panel = rd_handle_zero(), .view = rd_handle_from_cfg(view))
        {
          RD_ViewRuleInfo *view_rule_info = rd_view_rule_info_from_string(view->string);
          rd_cfg_release_all_children(expr);
          rd_cfg_new(expr, ws->hover_eval_string);
          {
            ui_set_next_fixed_x(ws->hover_eval_spawn_pos.x);
            ui_set_next_fixed_y(ws->hover_eval_spawn_pos.y);
            ui_set_next_pref_width(ui_em(60.f, 1.f));
            ui_set_next_pref_height(ui_em(40.f, 1.f));
            ui_set_next_child_layout_axis(Axis2_Y);
            UI_Box *container = ui_build_box_from_key(UI_BoxFlag_Floating, ui_key_zero());
            UI_Parent(container)
            {
              UI_Row
              {
              }
              ui_set_next_pref_width(ui_pct(1, 0));
              ui_set_next_pref_height(ui_pct(1, 0));
              ui_set_next_child_layout_axis(Axis2_Y);
              UI_Box *view_contents_container = ui_build_box_from_stringf(UI_BoxFlag_DrawBorder|UI_BoxFlag_DrawBackground|UI_BoxFlag_Clip, "###view_contents_container");
              UI_Parent(view_contents_container) UI_Focus(UI_FocusKind_Off) UI_WidthFill
              {
                RD_ViewRuleUIFunctionType *view_ui = view_rule_info->ui;
                String8 params_string = rd_string_from_cfg_tree(scratch.arena, view);
                MD_Node *params = md_tree_from_string(scratch.arena, params_string)->first;
                view_ui(expr->first->string, params, view_contents_container->rect);
              }
            }
          }
        }
        
#if 0 // TODO(rjf): @cfg
        Temp scratch = scratch_begin(0, 0);
        DI_Scope *scope = di_scope_open();
        String8 expr = ws->hover_eval_string;
        E_Eval eval = e_eval_from_string(scratch.arena, expr);
        EV_ViewRuleList top_level_view_rules = {0};
        
        //- rjf: build if good
        if(!e_type_key_match(eval.type_key, e_type_key_zero()) && !ui_any_ctx_menu_is_open())
          UI_Focus((hover_eval_is_open && !ui_any_ctx_menu_is_open() && ws->hover_eval_focused && !query_is_open) ? UI_FocusKind_Null : UI_FocusKind_Off)
        {
          //- rjf: eval -> viz artifacts
          F32 row_height = floor_f32(ui_top_font_size()*2.8f);
          RD_CfgTable cfg_table = {0};
          U64 expr_hash = d_hash_from_string(expr);
          String8 ev_view_key_string = push_str8f(scratch.arena, "eval_hover_%I64x", expr_hash);
          EV_View *ev_view = rd_ev_view_from_key(d_hash_from_string(ev_view_key_string));
          EV_Key parent_key = ev_key_make(5381, 1);
          EV_Key key = ev_key_make(ev_hash_from_key(parent_key), 1);
          EV_BlockTree block_tree = ev_block_tree_from_string(scratch.arena, ev_view, str8_zero(), expr, &top_level_view_rules);
          EV_BlockRangeList block_ranges = ev_block_range_list_from_tree(scratch.arena, &block_tree);
          // EV_BlockList viz_blocks = ev_block_list_from_view_expr_keys(scratch.arena, ev_view, str8_zero(), &top_level_view_rules, expr, parent_key, key, 0);
          CTRL_Entity *entity = rd_ctrl_entity_from_eval_space(eval.space);
          U32 default_radix = (entity->kind == CTRL_EntityKind_Thread ? 16 : 10);
          EV_WindowedRowList rows = ev_windowed_row_list_from_block_range_list(scratch.arena, ev_view, str8_zero(), &block_ranges, r1u64(0, 50));
          // EV_WindowedRowList viz_rows = ev_windowed_row_list_from_block_list(scratch.arena, ev_view, r1s64(0, 50), &viz_blocks);
          
          //- rjf: animate
          {
            B32 do_menu_animations = rd_setting_b32_from_name(str8_lit("menu_animations"));
            F32 rate = do_menu_animations ? 1 - pow_f32(2, (-60.f * rd_state->frame_dt)) : 1.f;
            
            // rjf: animate height
            {
              F32 hover_eval_container_height_target = row_height * Min(30, block_tree.total_row_count);
              ws->hover_eval_num_visible_rows_t += (hover_eval_container_height_target - ws->hover_eval_num_visible_rows_t) * rate;
              if(abs_f32(hover_eval_container_height_target - ws->hover_eval_num_visible_rows_t) > 0.5f)
              {
                rd_request_frame();
              }
              else
              {
                ws->hover_eval_num_visible_rows_t = hover_eval_container_height_target;
              }
            }
            
            // rjf: animate open
            {
              F32 diff = 1.f - ws->hover_eval_open_t;
              ws->hover_eval_open_t += diff*rate;
              if(abs_f32(diff) < 0.01f)
              {
                ws->hover_eval_open_t = 1.f;
              }
              else
              {
                rd_request_frame();
              }
            }
          }
          
          //- rjf: calculate width
          F32 width_px = 40.f*ui_top_font_size();
          F32 expr_column_width_px = 15.f*ui_top_font_size();
          F32 value_column_width_px = 25.f*ui_top_font_size();
          if(rows.first != 0)
          {
            EV_Row *row = rows.first;
            E_Eval row_eval = e_eval_from_expr(scratch.arena, row->expr);
            String8 row_expr_string = ev_expr_string_from_row(scratch.arena, row, 0);
            String8 row_display_value = rd_value_string_from_eval(scratch.arena, EV_StringFlag_ReadOnlyDisplayRules, default_radix, ui_top_font(), ui_top_font_size(), 500.f, row_eval, row->member, row->view_rules);
            expr_column_width_px = fnt_dim_from_tag_size_string(ui_top_font(), ui_top_font_size(), 0, 0, row_expr_string).x + ui_top_font_size()*10.f;
            value_column_width_px = fnt_dim_from_tag_size_string(ui_top_font(), ui_top_font_size(), 0, 0, row_display_value).x + ui_top_font_size()*5.f;
            F32 total_dim_px = (expr_column_width_px + value_column_width_px);
            width_px = Min(80.f*ui_top_font_size(), total_dim_px*1.5f);
          }
          
          //- rjf: build hover eval box
          F32 hover_eval_container_height = ws->hover_eval_num_visible_rows_t;
          F32 corner_radius = ui_top_font_size()*0.25f;
          ui_set_next_fixed_x(ws->hover_eval_spawn_pos.x);
          ui_set_next_fixed_y(ws->hover_eval_spawn_pos.y);
          ui_set_next_pref_width(ui_px(width_px, 1.f));
          ui_set_next_pref_height(ui_px(hover_eval_container_height, 1.f));
          ui_set_next_corner_radius_00(0);
          ui_set_next_corner_radius_01(corner_radius);
          ui_set_next_corner_radius_10(corner_radius);
          ui_set_next_corner_radius_11(corner_radius);
          ui_set_next_child_layout_axis(Axis2_Y);
          ui_set_next_squish(0.25f-0.25f*ws->hover_eval_open_t);
          ui_set_next_transparency(1.f-ws->hover_eval_open_t);
          UI_Focus(UI_FocusKind_On)
          {
            hover_eval_box = ui_build_box_from_stringf(UI_BoxFlag_DrawBorder|
                                                       UI_BoxFlag_DrawBackground|
                                                       UI_BoxFlag_DrawBackgroundBlur|
                                                       UI_BoxFlag_DrawDropShadow|
                                                       UI_BoxFlag_DisableFocusOverlay|
                                                       UI_BoxFlag_Clip|
                                                       UI_BoxFlag_AllowOverflowY|
                                                       UI_BoxFlag_ViewScroll|
                                                       UI_BoxFlag_ViewClamp|
                                                       UI_BoxFlag_Floating|
                                                       UI_BoxFlag_AnimatePos|
                                                       UI_BoxFlag_Clickable|
                                                       UI_BoxFlag_DefaultFocusNav,
                                                       "###hover_eval");
          }
          
          //- rjf: build contents
          UI_Parent(hover_eval_box) UI_PrefHeight(ui_px(row_height, 1.f))
          {
            //- rjf: build rows
            for(EV_Row *row = rows.first; row != 0; row = row->next)
            {
              //- rjf: unpack row
              U64 row_depth = ev_depth_from_block(row->block);
              E_Eval row_eval = e_eval_from_expr(scratch.arena, row->expr);
              String8 row_expr_string = ev_expr_string_from_row(scratch.arena, row, 0);
              String8 row_edit_value = rd_value_string_from_eval(scratch.arena, 0, default_radix, ui_top_font(), ui_top_font_size(), 500.f, row_eval, row->member, row->view_rules);
              String8 row_display_value = rd_value_string_from_eval(scratch.arena, EV_StringFlag_ReadOnlyDisplayRules, default_radix, ui_top_font(), ui_top_font_size(), 500.f, row_eval, row->member, row->view_rules);
              B32 row_is_editable   = ev_row_is_editable(row);
              B32 row_is_expandable = ev_row_is_expandable(row);
              
              //- rjf: determine if row's data is fresh and/or bad
              B32 row_is_fresh = 0;
              B32 row_is_bad = 0;
              switch(row_eval.mode)
              {
                default:{}break;
                case E_Mode_Offset:
                {
                  CTRL_Entity *space_entity = rd_ctrl_entity_from_eval_space(row_eval.space);
                  if(space_entity->kind == CTRL_EntityKind_Process)
                  {
                    U64 size = e_type_byte_size_from_key(row_eval.type_key);
                    size = Min(size, 64);
                    Rng1U64 vaddr_rng = r1u64(row_eval.value.u64, row_eval.value.u64+size);
                    CTRL_ProcessMemorySlice slice = ctrl_query_cached_data_from_process_vaddr_range(scratch.arena, space_entity->handle, vaddr_rng, 0);
                    for(U64 idx = 0; idx < (slice.data.size+63)/64; idx += 1)
                    {
                      if(slice.byte_changed_flags[idx] != 0)
                      {
                        row_is_fresh = 1;
                      }
                      if(slice.byte_bad_flags[idx] != 0)
                      {
                        row_is_bad = 1;
                      }
                    }
                  }
                }break;
              }
              
              //- rjf: build row
              UI_WidthFill UI_Row
              {
                ui_spacer(ui_em(0.5f, 1.f));
                if(row_depth > 0)
                {
                  for(U64 indent = 0; indent < row_depth; indent += 1)
                  {
                    ui_spacer(ui_em(0.5f, 1.f));
                    UI_Flags(UI_BoxFlag_DrawSideLeft) ui_spacer(ui_em(1.f, 1.f));
                  }
                }
                U64 row_hash = ev_hash_from_key(row->key);
                B32 row_is_expanded = ev_expansion_from_key(ev_view, row->key);
                if(row_is_expandable)
                  UI_PrefWidth(ui_em(1.f, 1)) 
                  if(ui_pressed(ui_expanderf(row->block->rows_default_expanded ? !row_is_expanded : row_is_expanded, "###%I64x_%I64x_is_expanded", row->key.parent_hash, row->key.child_id)))
                {
                  ev_key_set_expansion(ev_view, row->block->key, row->key, !row_is_expanded);
                }
                if(!row_is_expandable)
                {
                  UI_PrefWidth(ui_em(1.f, 1))
                    UI_Flags(UI_BoxFlag_DrawTextWeak)
                    RD_Font(RD_FontSlot_Icons)
                    ui_label(rd_icon_kind_text_table[RD_IconKind_Dot]);
                }
                UI_WidthFill UI_TextRasterFlags(rd_raster_flags_from_slot(RD_FontSlot_Code))
                {
                  UI_PrefWidth(ui_px(expr_column_width_px, 1.f)) rd_code_label(1.f, 1, rd_rgba_from_theme_color(RD_ThemeColor_CodeDefault), row_expr_string);
                  ui_spacer(ui_em(1.5f, 1.f));
                  if(row_is_editable)
                  {
                    if(row_is_fresh)
                    {
                      Vec4F32 rgba = rd_rgba_from_theme_color(RD_ThemeColor_HighlightOverlay);
                      ui_set_next_palette(ui_build_palette(ui_top_palette(), .background = rgba));
                    }
                    UI_Signal sig = rd_line_editf(RD_LineEditFlag_CodeContents|
                                                  RD_LineEditFlag_DisplayStringIsCode|
                                                  RD_LineEditFlag_PreferDisplayString|
                                                  RD_LineEditFlag_Border,
                                                  0, 0, &ws->hover_eval_txt_cursor, &ws->hover_eval_txt_mark, ws->hover_eval_txt_buffer, sizeof(ws->hover_eval_txt_buffer), &ws->hover_eval_txt_size, 0, row_edit_value, "%S###val_%I64x", row_display_value, row_hash);
                    if(ui_pressed(sig))
                    {
                      ws->hover_eval_focused = 1;
                    }
                    if(ui_committed(sig))
                    {
                      String8 commit_string = str8(ws->hover_eval_txt_buffer, ws->hover_eval_txt_size);
                      B32 success = rd_commit_eval_value_string(row_eval, commit_string, 1);
                      if(success == 0)
                      {
                        log_user_error(str8_lit("Could not commit value successfully."));
                      }
                    }
                  }
                  else
                  {
                    if(row_is_fresh)
                    {
                      Vec4F32 rgba = rd_rgba_from_theme_color(RD_ThemeColor_HighlightOverlay);
                      ui_set_next_palette(ui_build_palette(ui_top_palette(), .background = rgba));
                      ui_set_next_flags(UI_BoxFlag_DrawBackground);
                    }
                    rd_code_label(1.f, 1, rd_rgba_from_theme_color(RD_ThemeColor_CodeDefault), row_display_value);
                  }
                }
                if(row == rows.first)
                {
                  UI_TextAlignment(UI_TextAlign_Center) UI_PrefWidth(ui_em(3.f, 1.f))
                    UI_CornerRadius00(0)
                    UI_CornerRadius01(0)
                    UI_CornerRadius10(0)
                    UI_CornerRadius11(0)
                  {
                    UI_Signal watch_sig = rd_icon_buttonf(RD_IconKind_List, 0, "###watch_hover_eval");
                    if(ui_hovering(watch_sig)) UI_Tooltip RD_Font(RD_FontSlot_Main) UI_FontSize(rd_font_size_from_slot(RD_FontSlot_Main))
                    {
                      ui_labelf("Add the hovered expression to an opened watch view.");
                    }
                    if(ui_clicked(watch_sig))
                    {
                      rd_cmd(RD_CmdKind_ToggleWatchExpression, .string = expr);
                    }
                  }
                  if(ws->hover_eval_file_path.size != 0 || ws->hover_eval_vaddr != 0)
                    UI_TextAlignment(UI_TextAlign_Center) UI_PrefWidth(ui_em(3.f, 1.f))
                    UI_CornerRadius10(corner_radius)
                    UI_CornerRadius11(corner_radius)
                  {
                    UI_Signal pin_sig = rd_icon_buttonf(RD_IconKind_Pin, 0, "###pin_hover_eval");
                    if(ui_hovering(pin_sig)) UI_Tooltip RD_Font(RD_FontSlot_Main) UI_FontSize(rd_font_size_from_slot(RD_FontSlot_Main))
                      UI_CornerRadius00(0)
                      UI_CornerRadius01(0)
                      UI_CornerRadius10(0)
                      UI_CornerRadius11(0)
                    {
                      ui_labelf("Pin the hovered expression to this code location.");
                    }
                    if(ui_clicked(pin_sig))
                    {
                      rd_cmd(RD_CmdKind_ToggleWatchPin,
                             .file_path  = ws->hover_eval_file_path,
                             .cursor     = ws->hover_eval_file_pt,
                             .vaddr      = ws->hover_eval_vaddr,
                             .string     = expr);
                    }
                  }
                }
              }
            }
            UI_PrefWidth(ui_px(0, 0)) ui_spacer(ui_px(hover_eval_container_height-row_height, 1.f));
          }
          
          //- rjf: interact
          {
            UI_Signal hover_eval_sig = ui_signal_from_box(hover_eval_box);
            if(ui_mouse_over(hover_eval_sig))
            {
              ws->hover_eval_last_frame_idx = rd_state->frame_index;
            }
            else if(ws->hover_eval_last_frame_idx+2 < rd_state->frame_index)
            {
              rd_request_frame();
            }
            if(ui_pressed(hover_eval_sig))
            {
              ws->hover_eval_focused = 1;
            }
          }
        }
        
        di_scope_close(scope);
        scratch_end(scratch);
#endif
      }
    }
    
    ////////////////////////////
    //- rjf: panel non-leaf UI (drag boundaries, drag/drop sites)
    //
    B32 is_changing_panel_boundaries = 0;
    ProfScope("non-leaf panel UI")
      for(RD_PanelNode *panel = panel_tree.root;
          panel != &rd_nil_panel_node;
          panel = rd_panel_node_rec__depth_first_pre(panel_tree.root, panel).next)
    {
      //////////////////////////
      //- rjf: continue on leaf panels
      //
      if(panel->first == &rd_nil_panel_node)
      {
        continue;
      }
      
      //////////////////////////
      //- rjf: grab info
      //
      Axis2 split_axis = panel->split_axis;
      Rng2F32 panel_rect = rd_target_rect_from_panel_node(content_rect, panel_tree.root, panel);
      
      //////////////////////////
      //- rjf: boundary tab-drag/drop sites
      //
      {
        RD_Cfg *drag_view = rd_cfg_from_handle(rd_state->drag_drop_regs->view);
        if(rd_drag_is_active() && rd_state->drag_drop_regs_slot == RD_RegSlot_View && drag_view != &rd_nil_cfg)
        {
          //- rjf: params
          F32 drop_site_major_dim_px = ceil_f32(ui_top_font_size()*7.f);
          F32 drop_site_minor_dim_px = ceil_f32(ui_top_font_size()*5.f);
          F32 corner_radius = ui_top_font_size()*0.5f;
          F32 padding = ceil_f32(ui_top_font_size()*0.5f);
          
          //- rjf: special case - build Y boundary drop sites on root panel
          //
          // (this does not naturally follow from the below algorithm, since the
          // root level panel only splits on X)
          if(panel == panel_tree.root) UI_CornerRadius(corner_radius)
          {
            Vec2F32 panel_rect_center = center_2f32(panel_rect);
            Axis2 axis = axis2_flip(panel_tree.root->split_axis);
            for EachEnumVal(Side, side)
            {
              UI_Key key = ui_key_from_stringf(ui_key_zero(), "root_extra_split_%i", side);
              Rng2F32 site_rect = panel_rect;
              site_rect.p0.v[axis2_flip(axis)] = panel_rect_center.v[axis2_flip(axis)] - drop_site_major_dim_px/2;
              site_rect.p1.v[axis2_flip(axis)] = panel_rect_center.v[axis2_flip(axis)] + drop_site_major_dim_px/2;
              site_rect.p0.v[axis] = panel_rect.v[side].v[axis] - drop_site_minor_dim_px/2;
              site_rect.p1.v[axis] = panel_rect.v[side].v[axis] + drop_site_minor_dim_px/2;
              
              // rjf: build
              UI_Box *site_box = &ui_nil_box;
              {
                UI_Rect(site_rect)
                {
                  site_box = ui_build_box_from_key(UI_BoxFlag_DropSite, key);
                  ui_signal_from_box(site_box);
                }
                UI_Box *site_box_viz = &ui_nil_box;
                UI_Parent(site_box) UI_WidthFill UI_HeightFill
                  UI_Padding(ui_px(padding, 1.f))
                  UI_Column
                  UI_Padding(ui_px(padding, 1.f))
                {
                  ui_set_next_child_layout_axis(axis2_flip(axis));
                  if(ui_key_match(key, ui_drop_hot_key()))
                  {
                    ui_set_next_palette(ui_build_palette(ui_top_palette(), .border = rd_rgba_from_theme_color(RD_ThemeColor_Hover)));
                  }
                  site_box_viz = ui_build_box_from_key(UI_BoxFlag_DrawBackground|
                                                       UI_BoxFlag_DrawBorder|
                                                       UI_BoxFlag_DrawDropShadow|
                                                       UI_BoxFlag_DrawBackgroundBlur, ui_key_zero());
                }
                UI_Parent(site_box_viz) UI_WidthFill UI_HeightFill UI_Padding(ui_px(padding, 1.f))
                {
                  ui_set_next_child_layout_axis(axis);
                  UI_Box *row_or_column = ui_build_box_from_key(0, ui_key_zero()); UI_Parent(row_or_column) UI_Padding(ui_px(padding, 1.f))
                  {
                    ui_build_box_from_key(UI_BoxFlag_DrawBorder, ui_key_zero());
                    ui_spacer(ui_px(padding, 1.f));
                    ui_build_box_from_key(UI_BoxFlag_DrawBorder, ui_key_zero());
                  }
                }
              }
              
              // rjf: viz
              if(ui_key_match(site_box->key, ui_drop_hot_key()))
              {
                Rng2F32 future_split_rect = site_rect;
                future_split_rect.p0.v[axis] -= drop_site_major_dim_px;
                future_split_rect.p1.v[axis] += drop_site_major_dim_px;
                future_split_rect.p0.v[axis2_flip(axis)] = panel_rect.p0.v[axis2_flip(axis)];
                future_split_rect.p1.v[axis2_flip(axis)] = panel_rect.p1.v[axis2_flip(axis)];
                UI_Rect(future_split_rect) RD_Palette(RD_PaletteCode_DropSiteOverlay)
                {
                  ui_build_box_from_key(UI_BoxFlag_DrawBackground, ui_key_zero());
                }
              }
              
              // rjf: drop
              if(ui_key_match(site_box->key, ui_drop_hot_key()) && rd_drag_drop())
              {
                Dir2 dir = (axis == Axis2_Y ? (side == Side_Min ? Dir2_Up : Dir2_Down) :
                            axis == Axis2_X ? (side == Side_Min ? Dir2_Left : Dir2_Right) :
                            Dir2_Invalid);
                if(dir != Dir2_Invalid)
                {
                  RD_PanelNode *split_panel = panel;
                  rd_cmd(RD_CmdKind_SplitPanel,
                         .dst_panel  = rd_handle_from_cfg(split_panel->cfg),
                         .panel      = rd_state->drag_drop_regs->panel,
                         .view       = rd_state->drag_drop_regs->view,
                         .dir2       = dir);
                }
              }
            }
          }
          
          //- rjf: iterate all children, build boundary drop sites
          Axis2 split_axis = panel->split_axis;
          UI_CornerRadius(corner_radius) for(RD_PanelNode *child = panel->first;; child = child->next)
          {
            // rjf: form rect
            Rng2F32 child_rect = rd_target_rect_from_panel_node_child(panel_rect, panel, child);
            Vec2F32 child_rect_center = center_2f32(child_rect);
            UI_Key key = ui_key_from_stringf(ui_key_zero(), "drop_boundary_%p_%p", panel, child);
            Rng2F32 site_rect = r2f32(child_rect_center, child_rect_center);
            site_rect.p0.v[split_axis] = child_rect.p0.v[split_axis] - drop_site_minor_dim_px/2;
            site_rect.p1.v[split_axis] = child_rect.p0.v[split_axis] + drop_site_minor_dim_px/2;
            site_rect.p0.v[axis2_flip(split_axis)] -= drop_site_major_dim_px/2;
            site_rect.p1.v[axis2_flip(split_axis)] += drop_site_major_dim_px/2;
            
            // rjf: build
            UI_Box *site_box = &ui_nil_box;
            {
              UI_Rect(site_rect)
              {
                site_box = ui_build_box_from_key(UI_BoxFlag_DropSite, key);
                ui_signal_from_box(site_box);
              }
              UI_Box *site_box_viz = &ui_nil_box;
              UI_Parent(site_box) UI_WidthFill UI_HeightFill
                UI_Padding(ui_px(padding, 1.f))
                UI_Column
                UI_Padding(ui_px(padding, 1.f))
              {
                ui_set_next_child_layout_axis(axis2_flip(split_axis));
                if(ui_key_match(key, ui_drop_hot_key()))
                {
                  ui_set_next_palette(ui_build_palette(ui_top_palette(), .border = rd_rgba_from_theme_color(RD_ThemeColor_Hover)));
                }
                site_box_viz = ui_build_box_from_key(UI_BoxFlag_DrawBackground|
                                                     UI_BoxFlag_DrawBorder|
                                                     UI_BoxFlag_DrawDropShadow|
                                                     UI_BoxFlag_DrawBackgroundBlur, ui_key_zero());
              }
              UI_Parent(site_box_viz) UI_WidthFill UI_HeightFill UI_Padding(ui_px(padding, 1.f))
              {
                ui_set_next_child_layout_axis(split_axis);
                UI_Box *row_or_column = ui_build_box_from_key(0, ui_key_zero()); UI_Parent(row_or_column) UI_Padding(ui_px(padding, 1.f))
                {
                  ui_build_box_from_key(UI_BoxFlag_DrawBorder, ui_key_zero());
                  ui_spacer(ui_px(padding, 1.f));
                  ui_build_box_from_key(UI_BoxFlag_DrawBorder, ui_key_zero());
                }
              }
            }
            
            // rjf: viz
            if(ui_key_match(site_box->key, ui_drop_hot_key()))
            {
              Rng2F32 future_split_rect = site_rect;
              future_split_rect.p0.v[split_axis] -= drop_site_major_dim_px;
              future_split_rect.p1.v[split_axis] += drop_site_major_dim_px;
              future_split_rect.p0.v[axis2_flip(split_axis)] = child_rect.p0.v[axis2_flip(split_axis)];
              future_split_rect.p1.v[axis2_flip(split_axis)] = child_rect.p1.v[axis2_flip(split_axis)];
              UI_Rect(future_split_rect) RD_Palette(RD_PaletteCode_DropSiteOverlay)
              {
                ui_build_box_from_key(UI_BoxFlag_DrawBackground, ui_key_zero());
              }
            }
            
            // rjf: drop
            if(ui_key_match(site_box->key, ui_drop_hot_key()) && rd_drag_drop())
            {
              Dir2 dir = (panel->split_axis == Axis2_X ? Dir2_Left : Dir2_Up);
              RD_PanelNode *split_panel = child;
              if(split_panel == &rd_nil_panel_node)
              {
                split_panel = panel->last;
                dir = (panel->split_axis == Axis2_X ? Dir2_Right : Dir2_Down);
              }
              rd_cmd(RD_CmdKind_SplitPanel,
                     .dst_panel  = rd_handle_from_cfg(split_panel->cfg),
                     .panel      = rd_state->drag_drop_regs->panel,
                     .view       = rd_state->drag_drop_regs->view,
                     .dir2       = dir);
            }
            
            // rjf: exit on opl child
            if(child == &rd_nil_panel_node)
            {
              break;
            }
          }
        }
      }
      
      //////////////////////////
      //- rjf: do UI for drag boundaries between all children
      //
      for(RD_PanelNode *child = panel->first;
          child != &rd_nil_panel_node && child->next != &rd_nil_panel_node;
          child = child->next)
      {
        RD_PanelNode *min_child = child;
        RD_PanelNode *max_child = min_child->next;
        Rng2F32 min_child_rect = rd_target_rect_from_panel_node_child(panel_rect, panel, min_child);
        Rng2F32 max_child_rect = rd_target_rect_from_panel_node_child(panel_rect, panel, max_child);
        Rng2F32 boundary_rect = {0};
        {
          boundary_rect.p0.v[split_axis] = min_child_rect.p1.v[split_axis] - ui_top_font_size()/3;
          boundary_rect.p1.v[split_axis] = max_child_rect.p0.v[split_axis] + ui_top_font_size()/3;
          boundary_rect.p0.v[axis2_flip(split_axis)] = panel_rect.p0.v[axis2_flip(split_axis)];
          boundary_rect.p1.v[axis2_flip(split_axis)] = panel_rect.p1.v[axis2_flip(split_axis)];
        }
        
        UI_Rect(boundary_rect)
        {
          ui_set_next_hover_cursor(split_axis == Axis2_X ? OS_Cursor_LeftRight : OS_Cursor_UpDown);
          UI_Box *box = ui_build_box_from_stringf(UI_BoxFlag_Clickable, "###%p_%p", min_child->cfg, max_child->cfg);
          UI_Signal sig = ui_signal_from_box(box);
          if(ui_double_clicked(sig))
          {
            ui_kill_action();
            F32 sum_pct = min_child->pct_of_parent + max_child->pct_of_parent;
            min_child->pct_of_parent = 0.5f * sum_pct;
            max_child->pct_of_parent = 0.5f * sum_pct;
            rd_cfg_equip_stringf(min_child->cfg, "%f", min_child->pct_of_parent);
            rd_cfg_equip_stringf(max_child->cfg, "%f", max_child->pct_of_parent);
          }
          else if(ui_pressed(sig))
          {
            Vec2F32 v = {min_child->pct_of_parent, max_child->pct_of_parent};
            ui_store_drag_struct(&v);
          }
          else if(ui_dragging(sig))
          {
            Vec2F32 v = *ui_get_drag_struct(Vec2F32);
            Vec2F32 mouse_delta      = ui_drag_delta();
            F32 total_size           = dim_2f32(panel_rect).v[split_axis];
            F32 min_pct__before      = v.v[0];
            F32 min_pixels__before   = min_pct__before * total_size;
            F32 min_pixels__after    = min_pixels__before + mouse_delta.v[split_axis];
            if(min_pixels__after < 50.f)
            {
              min_pixels__after = 50.f;
            }
            F32 min_pct__after       = min_pixels__after / total_size;
            F32 pct_delta            = min_pct__after - min_pct__before;
            F32 max_pct__before      = v.v[1];
            F32 max_pct__after       = max_pct__before - pct_delta;
            F32 max_pixels__after    = max_pct__after * total_size;
            if(max_pixels__after < 50.f)
            {
              max_pixels__after = 50.f;
              max_pct__after = max_pixels__after / total_size;
              pct_delta = -(max_pct__after - max_pct__before);
              min_pct__after = min_pct__before + pct_delta;
            }
            min_child->pct_of_parent = min_pct__after;
            max_child->pct_of_parent = max_pct__after;
            rd_cfg_equip_stringf(min_child->cfg, "%f", min_pct__after);
            rd_cfg_equip_stringf(max_child->cfg, "%f", max_pct__after);
            is_changing_panel_boundaries = 1;
          }
        }
      }
    }
    
    ////////////////////////////
    //- rjf: animate panels
    //
#if 0 // TODO(rjf): @cfg_panels
    {
      F32 rate = rd_setting_val_from_code(RD_SettingCode_MenuAnimations).s32 ? 1 - pow_f32(2, (-50.f * rd_state->frame_dt)) : 1.f;
      Vec2F32 content_rect_dim = dim_2f32(content_rect);
      if(content_rect_dim.x > 0 && content_rect_dim.y > 0)
      {
        for(RD_PanelNode *panel = panel_tree.root;
            panel != &rd_nil_panel_node;
            panel = rd_panel_node_rec__depth_first_pre(panel).next)
        {
          Rng2F32 target_rect_px = rd_target_rect_from_panel_node(content_rect, panel_tree.root, panel);
          Rng2F32 target_rect_pct = r2f32p(target_rect_px.x0/content_rect_dim.x,
                                           target_rect_px.y0/content_rect_dim.y,
                                           target_rect_px.x1/content_rect_dim.x,
                                           target_rect_px.y1/content_rect_dim.y);
          if(abs_f32(target_rect_pct.x0 - panel->animated_rect_pct.x0) > 0.005f ||
             abs_f32(target_rect_pct.y0 - panel->animated_rect_pct.y0) > 0.005f ||
             abs_f32(target_rect_pct.x1 - panel->animated_rect_pct.x1) > 0.005f ||
             abs_f32(target_rect_pct.y1 - panel->animated_rect_pct.y1) > 0.005f)
          {
            rd_request_frame();
          }
          panel->animated_rect_pct.x0 += rate * (target_rect_pct.x0 - panel->animated_rect_pct.x0);
          panel->animated_rect_pct.y0 += rate * (target_rect_pct.y0 - panel->animated_rect_pct.y0);
          panel->animated_rect_pct.x1 += rate * (target_rect_pct.x1 - panel->animated_rect_pct.x1);
          panel->animated_rect_pct.y1 += rate * (target_rect_pct.y1 - panel->animated_rect_pct.y1);
          if(ws->frames_alive < 5 || is_changing_panel_boundaries)
          {
            panel->animated_rect_pct = target_rect_pct;
          }
        }
      }
    }
#endif
    
    ////////////////////////////
    //- rjf: panel leaf UI
    //
    ProfScope("leaf panel UI")
      for(RD_PanelNode *panel = panel_tree.root;
          panel != &rd_nil_panel_node;
          panel = rd_panel_node_rec__depth_first_pre(panel_tree.root, panel).next)
    {
      if(panel->first != &rd_nil_panel_node) {continue;}
      B32 panel_is_focused = (window_is_focused &&
                              !ws->menu_bar_focused &&
                              !query_is_open &&
                              !ui_any_ctx_menu_is_open() &&
                              !ws->hover_eval_focused &&
                              panel_tree.focused == panel);
      RD_Cfg *selected_tab = panel->selected_tab;
      RD_ViewState *selected_tab_view_state = rd_view_state_from_cfg(selected_tab);
      F32 selected_tab_is_filtering_t = ui_anim(ui_key_from_stringf(ui_key_zero(), "###is_filtering_t_%p", selected_tab), (F32)!!selected_tab_view_state->is_filtering);
      ProfScope("leaf panel UI work - %.*s", str8_varg(selected_tab->string))
        UI_Focus(panel_is_focused ? UI_FocusKind_Null : UI_FocusKind_Off)
      {
        //////////////////////////
        //- rjf: calculate UI rectangles
        //
        Vec2F32 content_rect_dim = dim_2f32(content_rect);
        Rng2F32 target_rect_px = rd_target_rect_from_panel_node(content_rect, panel_tree.root, panel);
        Rng2F32 target_rect_pct = r2f32p(target_rect_px.x0 / content_rect_dim.x,
                                         target_rect_px.y0 / content_rect_dim.y,
                                         target_rect_px.x1 / content_rect_dim.x,
                                         target_rect_px.y1 / content_rect_dim.y);
        // TODO(rjf): @cfg_panels animate `target_rect_pct`
        Rng2F32 panel_rect_pct = target_rect_pct;
        Rng2F32 panel_rect = r2f32p(panel_rect_pct.x0*content_rect_dim.x,
                                    panel_rect_pct.y0*content_rect_dim.y,
                                    panel_rect_pct.x1*content_rect_dim.x,
                                    panel_rect_pct.y1*content_rect_dim.y);
        panel_rect = pad_2f32(panel_rect, -1.f);
        F32 tab_bar_rheight = ui_top_font_size()*3.f;
        F32 tab_bar_vheight = ui_top_font_size()*2.6f;
        F32 tab_bar_rv_diff = tab_bar_rheight - tab_bar_vheight;
        F32 tab_spacing = ui_top_font_size()*0.4f;
        F32 filter_bar_height = ui_top_font_size()*3.f;
        Rng2F32 tab_bar_rect = r2f32p(panel_rect.x0, panel_rect.y0, panel_rect.x1, panel_rect.y0 + tab_bar_vheight);
        Rng2F32 content_rect = r2f32p(panel_rect.x0, panel_rect.y0+tab_bar_vheight, panel_rect.x1, panel_rect.y1);
        Rng2F32 filter_rect = {0};
        if(panel->tab_side == Side_Max)
        {
          tab_bar_rect.y0 = panel_rect.y1 - tab_bar_vheight;
          tab_bar_rect.y1 = panel_rect.y1;
          content_rect.y0 = panel_rect.y0;
          content_rect.y1 = panel_rect.y1 - tab_bar_vheight;
        }
        if(selected_tab_is_filtering_t > 0.01f)
        {
          filter_rect.x0 = content_rect.x0;
          filter_rect.y0 = content_rect.y0;
          filter_rect.x1 = content_rect.x1;
          content_rect.y0 += filter_bar_height*selected_tab_is_filtering_t;
          filter_rect.y1 = content_rect.y0;
        }
        
        //////////////////////////
        //- rjf: build combined split+movetab drag/drop sites
        //
        {
          RD_Cfg *view = rd_cfg_from_handle(rd_state->drag_drop_regs->view);
          if(rd_drag_is_active() && rd_state->drag_drop_regs_slot == RD_RegSlot_View && view != &rd_nil_cfg && contains_2f32(panel_rect, ui_mouse()))
          {
            F32 drop_site_dim_px = ceil_f32(ui_top_font_size()*7.f);
            Vec2F32 drop_site_half_dim = v2f32(drop_site_dim_px/2, drop_site_dim_px/2);
            Vec2F32 panel_center = center_2f32(panel_rect);
            F32 corner_radius = ui_top_font_size()*0.5f;
            F32 padding = ceil_f32(ui_top_font_size()*0.5f);
            struct
            {
              UI_Key key;
              Dir2 split_dir;
              Rng2F32 rect;
            }
            sites[] =
            {
              {
                ui_key_from_stringf(ui_key_zero(), "drop_split_center_%p", panel->cfg),
                Dir2_Invalid,
                r2f32(sub_2f32(panel_center, drop_site_half_dim),
                      add_2f32(panel_center, drop_site_half_dim))
              },
              {
                ui_key_from_stringf(ui_key_zero(), "drop_split_up_%p", panel->cfg),
                Dir2_Up,
                r2f32p(panel_center.x-drop_site_half_dim.x,
                       panel_center.y-drop_site_half_dim.y - drop_site_half_dim.y*2,
                       panel_center.x+drop_site_half_dim.x,
                       panel_center.y+drop_site_half_dim.y - drop_site_half_dim.y*2),
              },
              {
                ui_key_from_stringf(ui_key_zero(), "drop_split_down_%p", panel->cfg),
                Dir2_Down,
                r2f32p(panel_center.x-drop_site_half_dim.x,
                       panel_center.y-drop_site_half_dim.y + drop_site_half_dim.y*2,
                       panel_center.x+drop_site_half_dim.x,
                       panel_center.y+drop_site_half_dim.y + drop_site_half_dim.y*2),
              },
              {
                ui_key_from_stringf(ui_key_zero(), "drop_split_left_%p", panel->cfg),
                Dir2_Left,
                r2f32p(panel_center.x-drop_site_half_dim.x - drop_site_half_dim.x*2,
                       panel_center.y-drop_site_half_dim.y,
                       panel_center.x+drop_site_half_dim.x - drop_site_half_dim.x*2,
                       panel_center.y+drop_site_half_dim.y),
              },
              {
                ui_key_from_stringf(ui_key_zero(), "drop_split_right_%p", panel->cfg),
                Dir2_Right,
                r2f32p(panel_center.x-drop_site_half_dim.x + drop_site_half_dim.x*2,
                       panel_center.y-drop_site_half_dim.y,
                       panel_center.x+drop_site_half_dim.x + drop_site_half_dim.x*2,
                       panel_center.y+drop_site_half_dim.y),
              },
            };
            UI_CornerRadius(corner_radius)
              for(U64 idx = 0; idx < ArrayCount(sites); idx += 1)
            {
              UI_Key key = sites[idx].key;
              Dir2 dir = sites[idx].split_dir;
              Rng2F32 rect = sites[idx].rect;
              Axis2 split_axis = axis2_from_dir2(dir);
              Side split_side = side_from_dir2(dir);
              if(dir != Dir2_Invalid && split_axis == panel->parent->split_axis)
              {
                continue;
              }
              UI_Box *site_box = &ui_nil_box;
              {
                UI_Rect(rect)
                {
                  site_box = ui_build_box_from_key(UI_BoxFlag_DropSite, key);
                  ui_signal_from_box(site_box);
                }
                UI_Box *site_box_viz = &ui_nil_box;
                UI_Parent(site_box) UI_WidthFill UI_HeightFill
                  UI_Padding(ui_px(padding, 1.f))
                  UI_Column
                  UI_Padding(ui_px(padding, 1.f))
                {
                  ui_set_next_child_layout_axis(axis2_flip(split_axis));
                  if(ui_key_match(key, ui_drop_hot_key()))
                  {
                    ui_set_next_palette(ui_build_palette(ui_top_palette(), .border = rd_rgba_from_theme_color(RD_ThemeColor_Hover)));
                  }
                  site_box_viz = ui_build_box_from_key(UI_BoxFlag_DrawBackground|
                                                       UI_BoxFlag_DrawBorder|
                                                       UI_BoxFlag_DrawDropShadow|
                                                       UI_BoxFlag_DrawBackgroundBlur, ui_key_zero());
                }
                if(dir != Dir2_Invalid)
                {
                  UI_Parent(site_box_viz) UI_WidthFill UI_HeightFill UI_Padding(ui_px(padding, 1.f))
                  {
                    ui_set_next_child_layout_axis(split_axis);
                    UI_Box *row_or_column = ui_build_box_from_key(0, ui_key_zero()); UI_Parent(row_or_column) UI_Padding(ui_px(padding, 1.f))
                    {
                      if(split_side == Side_Min) { ui_set_next_flags(UI_BoxFlag_DrawBackground); }
                      RD_Palette(RD_PaletteCode_DropSiteOverlay) ui_build_box_from_key(UI_BoxFlag_DrawBorder, ui_key_zero());
                      ui_spacer(ui_px(padding, 1.f));
                      if(split_side == Side_Max) { ui_set_next_flags(UI_BoxFlag_DrawBackground); }
                      RD_Palette(RD_PaletteCode_DropSiteOverlay) ui_build_box_from_key(UI_BoxFlag_DrawBorder, ui_key_zero());
                    }
                  }
                }
                else
                {
                  UI_Parent(site_box_viz) UI_WidthFill UI_HeightFill UI_Padding(ui_px(padding, 1.f))
                  {
                    ui_set_next_child_layout_axis(split_axis);
                    UI_Box *row_or_column = ui_build_box_from_key(0, ui_key_zero());
                    UI_Parent(row_or_column) UI_Padding(ui_px(padding, 1.f)) RD_Palette(RD_PaletteCode_DropSiteOverlay)
                    {
                      ui_build_box_from_key(UI_BoxFlag_DrawBorder|UI_BoxFlag_DrawBackground, ui_key_zero());
                    }
                  }
                }
              }
              if(ui_key_match(site_box->key, ui_drop_hot_key()) && rd_drag_drop())
              {
                if(dir != Dir2_Invalid)
                {
                  rd_cmd(RD_CmdKind_SplitPanel,
                         .dst_panel = rd_handle_from_cfg(panel->cfg),
                         .panel = rd_state->drag_drop_regs->panel,
                         .view = rd_state->drag_drop_regs->view,
                         .dir2 = dir);
                }
                else
                {
                  rd_cmd(RD_CmdKind_MoveTab,
                         .dst_panel = rd_handle_from_cfg(panel->cfg),
                         .panel = rd_state->drag_drop_regs->panel,
                         .view = rd_state->drag_drop_regs->view,
                         .prev_view = rd_handle_from_cfg(rd_cfg_list_last(&panel->tabs)));
                }
              }
            }
            for(U64 idx = 0; idx < ArrayCount(sites); idx += 1)
            {
              B32 is_drop_hot = ui_key_match(ui_drop_hot_key(), sites[idx].key);
              if(is_drop_hot)
              {
                Axis2 split_axis = axis2_from_dir2(sites[idx].split_dir);
                Side split_side = side_from_dir2(sites[idx].split_dir);
                Rng2F32 future_split_rect = panel_rect;
                if(sites[idx].split_dir != Dir2_Invalid)
                {
                  Vec2F32 panel_center = center_2f32(panel_rect);
                  future_split_rect.v[side_flip(split_side)].v[split_axis] = panel_center.v[split_axis];
                }
                UI_Rect(future_split_rect) RD_Palette(RD_PaletteCode_DropSiteOverlay)
                {
                  ui_build_box_from_key(UI_BoxFlag_DrawBackground, ui_key_zero());
                }
              }
            }
          }
        }
        
        //////////////////////////
        //- rjf: build catch-all panel drop-site
        //
        B32 catchall_drop_site_hovered = 0;
        if(rd_drag_is_active() && ui_key_match(ui_key_zero(), ui_drop_hot_key()))
        {
          UI_Rect(panel_rect)
          {
            UI_Key key = ui_key_from_stringf(ui_key_zero(), "catchall_drop_site_%p", panel->cfg);
            UI_Box *catchall_drop_site = ui_build_box_from_key(UI_BoxFlag_DropSite, key);
            ui_signal_from_box(catchall_drop_site);
            catchall_drop_site_hovered = ui_key_match(key, ui_drop_hot_key());
          }
        }
        
        //////////////////////////
        //- rjf: build filtering box
        //
        {
          RD_Cfg *view = selected_tab;
          RD_ViewRuleInfo *view_rule_info = rd_view_rule_info_from_string(view->string);
          RD_ViewState *view_state = rd_view_state_from_cfg(view);
          UI_Focus(UI_FocusKind_On) RD_RegsScope(.view = rd_handle_from_cfg(view))
          {
            if(view_state->is_filtering && ui_is_focus_active() && ui_slot_press(UI_EventActionSlot_Accept))
            {
              rd_cmd(RD_CmdKind_ApplyFilter);
            }
            if(view_state->is_filtering || selected_tab_is_filtering_t > 0.01f)
            {
              UI_Box *filter_box = &ui_nil_box;
              UI_Rect(filter_rect)
              {
                ui_set_next_child_layout_axis(Axis2_X);
                filter_box = ui_build_box_from_stringf(UI_BoxFlag_DrawBackground|UI_BoxFlag_Clip|UI_BoxFlag_DrawBorder, "filter_box_%p", view);
              }
              UI_Parent(filter_box) UI_WidthFill UI_HeightFill
              {
                UI_PrefWidth(ui_em(3.f, 1.f))
                  UI_FlagsAdd(UI_BoxFlag_DrawTextWeak)
                  RD_Font(RD_FontSlot_Icons)
                  UI_TextAlignment(UI_TextAlign_Center)
                  ui_label(rd_icon_kind_text_table[RD_IconKind_Find]);
                UI_PrefWidth(ui_text_dim(10, 1))
                {
                  ui_label(str8_lit("Filter"));
                }
                ui_spacer(ui_em(0.5f, 1.f));
                RD_Font(view_rule_info->flags & RD_ViewRuleInfoFlag_FilterIsCode ? RD_FontSlot_Code : RD_FontSlot_Main)
                  UI_Focus(view_state->is_filtering ? UI_FocusKind_On : UI_FocusKind_Off)
                  UI_TextPadding(ui_top_font_size()*0.5f)
                {
                  UI_Signal sig = rd_line_edit(RD_LineEditFlag_CodeContents*!!(view_rule_info->flags & RD_ViewRuleInfoFlag_FilterIsCode),
                                               0,
                                               0,
                                               &view_state->filter_cursor,
                                               &view_state->filter_mark,
                                               view_state->filter_buffer,
                                               sizeof(view_state->filter_buffer),
                                               &view_state->filter_string_size,
                                               0,
                                               str8(view_state->filter_buffer, view_state->filter_string_size),
                                               str8_lit("###filter_text_input"));
                  if(ui_pressed(sig))
                  {
                    rd_cmd(RD_CmdKind_FocusPanel);
                  }
                }
              }
            }
          }
        }
        
        //////////////////////////
        //- rjf: panel not selected? -> darken
        //
        if(panel != panel_tree.focused)
        {
          UI_Palette(ui_build_palette(0, .background = rd_rgba_from_theme_color(RD_ThemeColor_InactivePanelOverlay)))
            UI_Rect(content_rect)
          {
            ui_build_box_from_key(UI_BoxFlag_DrawBackground, ui_key_zero());
          }
        }
        
        //////////////////////////
        //- rjf: build panel container box
        //
        UI_Box *panel_box = &ui_nil_box;
        UI_Rect(content_rect) UI_ChildLayoutAxis(Axis2_Y) UI_CornerRadius(0) UI_Focus(UI_FocusKind_On)
        {
          UI_Key panel_key = ui_key_from_stringf(ui_key_zero(), "panel_box_%p", panel->cfg);
          panel_box = ui_build_box_from_key(UI_BoxFlag_MouseClickable|
                                            UI_BoxFlag_Clip|
                                            UI_BoxFlag_DrawBorder|
                                            UI_BoxFlag_DisableFocusOverlay|
                                            ((panel_tree.focused != panel)*UI_BoxFlag_DisableFocusBorder),
                                            panel_key);
        }
        
        //////////////////////////
        //- rjf: loading animation for stable view
        //
        UI_Box *loading_overlay_container = &ui_nil_box;
        UI_Parent(panel_box) UI_WidthFill UI_HeightFill
        {
          loading_overlay_container = ui_build_box_from_key(UI_BoxFlag_FloatingX|UI_BoxFlag_FloatingY, ui_key_zero());
        }
        
        //////////////////////////
        //- rjf: build selected tab view
        //
        UI_Parent(panel_box)
          UI_Focus(panel_is_focused ? UI_FocusKind_Null : UI_FocusKind_Off)
          UI_WidthFill
        {
          //- rjf: push interaction registers, fill with per-view states
          rd_push_regs(.panel = rd_handle_from_cfg(panel->cfg),
                       .view  = rd_handle_from_cfg(selected_tab));
          {
            String8 view_expr = rd_view_expr_string();
            String8 view_file_path = rd_file_path_from_eval_string(rd_frame_arena(), view_expr);
            if(view_file_path.size != 0)
            {
              rd_regs()->file_path = view_file_path;
            }
          }
          
          //- rjf: build view container
          UI_Box *view_container_box = &ui_nil_box;
          UI_FixedWidth(dim_2f32(content_rect).x)
            UI_FixedHeight(dim_2f32(content_rect).y)
            UI_ChildLayoutAxis(Axis2_Y)
          {
            view_container_box = ui_build_box_from_key(0, ui_key_zero());
          }
          
          //- rjf: build empty view
          UI_Parent(view_container_box) if(selected_tab == &rd_nil_cfg)
          {
            RD_VIEW_RULE_UI_FUNCTION_NAME(empty)(str8_zero(), &md_nil_node, content_rect);
          }
          
          //- rjf: build tab view
          UI_Parent(view_container_box) if(selected_tab != &rd_nil_cfg) ProfScope("build tab view")
          {
            String8 view_expr = rd_expr_from_cfg(selected_tab);
            String8 params_string = rd_string_from_cfg_tree(scratch.arena, selected_tab);
            MD_Node *params = md_tree_from_string(scratch.arena, params_string)->first;
            RD_ViewRuleInfo *view_rule_info = rd_view_rule_info_from_string(selected_tab->string);
            RD_ViewRuleUIFunctionType *view_ui = view_rule_info->ui;
            view_ui(view_expr, params, content_rect);
          }
          
          //- rjf: pop interaction registers; commit if this is the selected view
          RD_Regs *view_regs = rd_pop_regs();
          if(panel_tree.focused == panel)
          {
            MemoryCopyStruct(rd_regs(), view_regs);
          }
        }
        
        ////////////////////////
        //- rjf: loading? -> fill loading overlay container
        //
        {
          F32 selected_tab_loading_t = ui_anim(ui_key_from_stringf(ui_key_zero(), "###is_view_loading_%p", selected_tab), selected_tab_view_state->loading_t_target);
          if(selected_tab_loading_t > 0.01f) UI_Parent(loading_overlay_container)
          {
            rd_loading_overlay(panel_rect, selected_tab_loading_t, selected_tab_view_state->loading_progress_v, selected_tab_view_state->loading_progress_v_target);
          }
        }
        
        //////////////////////////
        //- rjf: take events to automatically start/end filtering, if applicable
        //
        UI_Focus(UI_FocusKind_On)
        {
          RD_Cfg *view = selected_tab;
          RD_ViewState *view_state = selected_tab_view_state;
          RD_ViewRuleInfo *view_rule_info = rd_view_rule_info_from_string(view->string);
          if(ui_is_focus_active() && view_rule_info->flags & RD_ViewRuleInfoFlag_TypingAutomaticallyFilters && !view_state->is_filtering)
          {
            for(UI_Event *evt = 0; ui_next_event(&evt);)
            {
              if(evt->flags & UI_EventFlag_Paste)
              {
                ui_eat_event(evt);
                rd_cmd(RD_CmdKind_Filter);
                rd_cmd(RD_CmdKind_Paste);
              }
              else if(evt->string.size != 0 && evt->kind == UI_EventKind_Text)
              {
                ui_eat_event(evt);
                rd_cmd(RD_CmdKind_Filter);
                rd_cmd(RD_CmdKind_InsertText, .string = evt->string);
              }
            }
          }
          if(view_rule_info->flags & RD_ViewRuleInfoFlag_CanFilter && (view_state->filter_string_size != 0 || view_state->is_filtering) && ui_is_focus_active() && ui_slot_press(UI_EventActionSlot_Cancel))
          {
            rd_cmd(RD_CmdKind_ClearFilter);
          }
        }
        
        //////////////////////////
        //- rjf: consume panel fallthrough interaction events
        //
        UI_Signal panel_sig = ui_signal_from_box(panel_box);
        if(ui_pressed(panel_sig))
        {
          rd_cmd(RD_CmdKind_FocusPanel, .panel = rd_handle_from_cfg(panel->cfg));
        }
        if(ui_right_clicked(panel_sig))
        {
          rd_cmd(RD_CmdKind_PushQuery,
                 .view         = rd_handle_from_cfg(panel->selected_tab),
                 .file_path    = rd_file_path_from_eval_string(rd_frame_arena(), rd_expr_from_cfg(panel->selected_tab)),
                 .ui_key       = panel_box->key,
                 .off_px       = sub_2f32(ui_mouse(), panel_box->rect.p0),
                 .reg_slot     = RD_RegSlot_View,
                 .lister_flags = RD_ListerFlag_LineEdit|RD_ListerFlag_Commands|RD_ListerFlag_Settings);
        }
        
        //////////////////////////
        //- rjf: build tab bar
        //
        UI_Focus(UI_FocusKind_Off)
        {
          Temp scratch = scratch_begin(0, 0);
          
          // rjf: types
          typedef struct DropSite DropSite;
          struct DropSite
          {
            F32 p;
            RD_Cfg *prev_view;
          };
          
          // rjf: prep output data
          RD_Cfg *next_selected_tab = selected_tab;
          UI_Box *tab_bar_box = &ui_nil_box;
          U64 drop_site_count = panel->tabs.count+1;
          DropSite *drop_sites = push_array(scratch.arena, DropSite, drop_site_count);
          F32 drop_site_max_p = 0;
          U64 view_idx = 0;
          
          // rjf: build
          UI_CornerRadius(0)
          {
            UI_Rect(tab_bar_rect) tab_bar_box = ui_build_box_from_stringf(UI_BoxFlag_Clip|UI_BoxFlag_AllowOverflowY|UI_BoxFlag_ViewClampX|UI_BoxFlag_ViewScrollX|UI_BoxFlag_Clickable, "tab_bar_%p", panel->cfg);
            if(panel->tab_side == Side_Max)
            {
              tab_bar_box->view_off.y = tab_bar_box->view_off_target.y = (tab_bar_rheight - tab_bar_vheight);
            }
            else
            {
              tab_bar_box->view_off.y = tab_bar_box->view_off_target.y = 0;
            }
          }
          UI_Parent(tab_bar_box) UI_PrefHeight(ui_pct(1, 0))
          {
            Temp scratch = scratch_begin(0, 0);
            F32 corner_radius = ui_em(0.6f, 1.f).value;
            ui_spacer(ui_px(1.f, 1.f));
            
            // rjf: build tabs
            RD_Cfg *tab = &rd_nil_cfg;
            RD_Cfg *prev_tab = &rd_nil_cfg;
            UI_PrefWidth(ui_em(18.f, 0.5f))
              UI_CornerRadius00(panel->tab_side == Side_Min ? corner_radius : 0)
              UI_CornerRadius01(panel->tab_side == Side_Min ? 0 : corner_radius)
              UI_CornerRadius10(panel->tab_side == Side_Min ? corner_radius : 0)
              UI_CornerRadius11(panel->tab_side == Side_Min ? 0 : corner_radius)
              for(RD_CfgNode *tab_n = panel->tabs.first; tab_n != 0; tab_n = tab_n->next, view_idx += 1)
            {
              prev_tab = tab;
              tab = tab_n->v;
              RD_ViewRuleInfo *tab_view_rule_info = rd_view_rule_info_from_string(tab->string);
              temp_end(scratch);
              if(rd_cfg_is_project_filtered(tab)) { continue; }
              
              // rjf: if before this tab is the prev-view of the current tab drag,
              // draw empty space
              if(rd_drag_is_active() && rd_state->drag_drop_regs_slot == RD_RegSlot_View && catchall_drop_site_hovered)
              {
#if 0 // TODO(rjf): @cfg_dragdrop
                RD_Panel *dst_panel = rd_panel_from_handle(rd_last_drag_drop_panel);
                RD_View *drag_view = rd_view_from_handle(rd_state->drag_drop_regs->view);
                RD_View *dst_prev_view = rd_view_from_handle(rd_last_drag_drop_prev_tab);
                if(dst_panel == panel &&
                   ((!rd_view_is_nil(view) && dst_prev_view == view->order_prev && drag_view != view && drag_view != view->order_prev) ||
                    (rd_view_is_nil(view) && dst_prev_view == panel->last_tab_view && drag_view != panel->last_tab_view)))
                {
                  UI_PrefWidth(ui_em(9.f, 0.2f)) UI_Column
                  {
                    ui_spacer(ui_em(0.2f, 1.f));
                    UI_CornerRadius00(corner_radius)
                      UI_CornerRadius10(corner_radius)
                      RD_Palette(RD_PaletteCode_DropSiteOverlay)
                    {
                      ui_build_box_from_key(UI_BoxFlag_DrawBackground|UI_BoxFlag_DrawBorder, ui_key_zero());
                    }
                  }
                }
#endif
              }
              
              // rjf: end on nil view
              if(tab == &rd_nil_cfg)
              {
                break;
              }
              
              // rjf: build per-tab info
              RD_RegsScope(.panel = rd_handle_from_cfg(panel->cfg), .view = rd_handle_from_cfg(tab))
              {
                // rjf: gather info for this tab
                B32 view_is_selected = (tab == panel->selected_tab);
                RD_IconKind icon_kind = tab_view_rule_info->icon_kind;
                String8 view_expr = rd_view_expr_string();
                DR_FancyStringList title_fstrs = rd_title_fstrs_from_view(scratch.arena, tab_view_rule_info->display_name, view_expr, ui_top_palette()->text, ui_top_palette()->text_weak, ui_top_font_size());
                
                // rjf: begin vertical region for this tab
                ui_set_next_child_layout_axis(Axis2_Y);
                UI_Box *tab_column_box = ui_build_box_from_stringf(!is_changing_panel_boundaries*UI_BoxFlag_AnimatePosX, "tab_column_%p", tab);
                
                // rjf: build tab container box
                UI_Parent(tab_column_box) UI_PrefHeight(ui_px(tab_bar_vheight, 1)) RD_Palette(view_is_selected ? RD_PaletteCode_Tab : RD_PaletteCode_TabInactive)
                {
                  if(panel->tab_side == Side_Max)
                  {
                    ui_spacer(ui_px(tab_bar_rv_diff-1.f, 1.f));
                  }
                  else
                  {
                    ui_spacer(ui_px(1.f, 1.f));
                  }
                  ui_set_next_hover_cursor(OS_Cursor_HandPoint);
                  UI_Box *tab_box = ui_build_box_from_stringf(UI_BoxFlag_DrawHotEffects|
                                                              UI_BoxFlag_DrawBackground|
                                                              UI_BoxFlag_DrawBorder|
                                                              (UI_BoxFlag_DrawDropShadow*view_is_selected)|
                                                              UI_BoxFlag_Clickable,
                                                              "tab_%p", tab);
                  
                  // rjf: build tab contents
                  UI_Parent(tab_box)
                  {
                    UI_WidthFill UI_Row
                    {
                      ui_spacer(ui_em(0.5f, 1.f));
                      if(icon_kind != RD_IconKind_Null)
                      {
                        UI_FlagsAdd(UI_BoxFlag_DrawTextWeak)
                          RD_Font(RD_FontSlot_Icons)
                          UI_TextAlignment(UI_TextAlign_Center)
                          UI_PrefWidth(ui_em(1.75f, 1.f))
                          ui_label(rd_icon_kind_text_table[icon_kind]);
                      }
                      UI_PrefWidth(ui_text_dim(10, 0))
                      {
                        UI_Box *name_box = ui_build_box_from_key(UI_BoxFlag_DrawText, ui_key_zero());
                        ui_box_equip_display_fancy_strings(name_box, &title_fstrs);
                      }
                    }
                    UI_PrefWidth(ui_em(2.35f, 1.f)) UI_TextAlignment(UI_TextAlign_Center)
                      RD_Font(RD_FontSlot_Icons)
                      UI_FontSize(rd_font_size_from_slot(RD_FontSlot_Icons)*0.75f)
                      UI_Flags(UI_BoxFlag_DrawTextWeak)
                      UI_CornerRadius00(0)
                      UI_CornerRadius01(0)
                    {
                      UI_Palette *palette = ui_build_palette(ui_top_palette());
                      palette->background = v4f32(0, 0, 0, 0);
                      ui_set_next_palette(palette);
                      UI_Signal sig = ui_buttonf("%S###close_view_%p", rd_icon_kind_text_table[RD_IconKind_X], tab);
                      if(ui_clicked(sig) || ui_middle_clicked(sig))
                      {
                        rd_cmd(RD_CmdKind_CloseTab);
                      }
                    }
                  }
                  
                  // rjf: consume events for tab clicking
                  {
                    UI_Signal sig = ui_signal_from_box(tab_box);
                    if(ui_pressed(sig))
                    {
                      next_selected_tab = tab;
                      rd_cmd(RD_CmdKind_FocusPanel);
                    }
                    else if(ui_dragging(sig) && !rd_drag_is_active() && length_2f32(ui_drag_delta()) > 10.f)
                    {
                      rd_drag_begin(RD_RegSlot_View);
                    }
                    else if(ui_right_clicked(sig))
                    {
                      rd_cmd(RD_CmdKind_PushQuery,
                             .reg_slot     = RD_RegSlot_View,
                             .ui_key       = sig.box->key,
                             .off_px       = v2f32(0, sig.box->rect.y1 - sig.box->rect.y0),
                             .lister_flags = RD_ListerFlag_LineEdit|RD_ListerFlag_Commands|RD_ListerFlag_Settings);
                    }
                    else if(ui_middle_clicked(sig))
                    {
                      rd_cmd(RD_CmdKind_CloseTab);
                    }
                  }
                }
                
                // rjf: space for next tab
                {
                  ui_spacer(ui_em(0.3f, 1.f));
                }
                
                // rjf: store off drop-site
                drop_sites[view_idx].p = tab_column_box->rect.x0 - tab_spacing/2;
                drop_sites[view_idx].prev_view = prev_tab;
                drop_site_max_p = Max(tab_column_box->rect.x1, drop_site_max_p);
              }
            }
            
            // rjf: build add-new-tab button
            UI_TextAlignment(UI_TextAlign_Center)
              UI_PrefWidth(ui_px(tab_bar_vheight, 1.f))
              UI_PrefHeight(ui_px(tab_bar_vheight, 1.f))
              UI_Column
            {
              if(panel->tab_side == Side_Max)
              {
                ui_spacer(ui_px(tab_bar_rv_diff-1.f, 1.f));
              }
              else
              {
                ui_spacer(ui_px(1.f, 1.f));
              }
              UI_CornerRadius00(panel->tab_side == Side_Min ? corner_radius : 0)
                UI_CornerRadius10(panel->tab_side == Side_Min ? corner_radius : 0)
                UI_CornerRadius01(panel->tab_side == Side_Max ? corner_radius : 0)
                UI_CornerRadius11(panel->tab_side == Side_Max ? corner_radius : 0)
                RD_Font(RD_FontSlot_Icons)
                UI_FontSize(ui_top_font_size())
                UI_FlagsAdd(UI_BoxFlag_DrawTextWeak)
                UI_HoverCursor(OS_Cursor_HandPoint)
                RD_Palette(RD_PaletteCode_ImplicitButton)
              {
                UI_Box *add_new_box = ui_build_box_from_stringf(UI_BoxFlag_DrawBackground|
                                                                UI_BoxFlag_DrawText|
                                                                UI_BoxFlag_DrawBorder|
                                                                UI_BoxFlag_DrawHotEffects|
                                                                UI_BoxFlag_DrawActiveEffects|
                                                                UI_BoxFlag_Clickable|
                                                                UI_BoxFlag_DisableTextTrunc,
                                                                "%S##add_new_tab_button_%p",
                                                                rd_icon_kind_text_table[RD_IconKind_Add],
                                                                panel->cfg);
                UI_Signal sig = ui_signal_from_box(add_new_box);
                if(ui_clicked(sig))
                {
                  rd_cmd(RD_CmdKind_FocusPanel);
                  UI_Key view_menu_key = ui_key_from_string(ui_key_zero(), str8_lit("_view_menu_key_"));
                  ui_ctx_menu_open(view_menu_key, add_new_box->key, v2f32(0, tab_bar_vheight));
                }
              }
            }
            
            scratch_end(scratch);
          }
          
          // rjf: interact with tab bar
          ui_signal_from_box(tab_bar_box);
          
          // rjf: fill out last drop site
          {
            drop_sites[drop_site_count-1].p = drop_site_max_p;
            drop_sites[drop_site_count-1].prev_view = rd_cfg_list_last(&panel->tabs);
          }
          
          // rjf: more precise drop-sites on tab bar
          {
            Vec2F32 mouse = ui_mouse();
            RD_Cfg *drag_tab = rd_cfg_from_handle(rd_state->drag_drop_regs->view);
            if(rd_drag_is_active() && rd_state->drag_drop_regs_slot == RD_RegSlot_View && window_is_focused && contains_2f32(panel_rect, mouse) && drag_tab != &rd_nil_cfg)
            {
              // rjf: mouse => hovered drop site
              F32 min_distance = 0;
              DropSite *active_drop_site = 0;
              if(catchall_drop_site_hovered)
              {
                for(U64 drop_site_idx = 0; drop_site_idx < drop_site_count; drop_site_idx += 1)
                {
                  F32 distance = abs_f32(drop_sites[drop_site_idx].p - mouse.x);
                  if(drop_site_idx == 0 || distance < min_distance)
                  {
                    active_drop_site = &drop_sites[drop_site_idx];
                    min_distance = distance;
                  }
                }
              }
              
              // rjf: store closest prev-view
              if(active_drop_site != 0)
              {
                rd_last_drag_drop_prev_tab = rd_handle_from_cfg(active_drop_site->prev_view);
              }
              else
              {
                rd_last_drag_drop_prev_tab = rd_handle_zero();
              }
              
              // rjf: vis
              if(drag_tab != &rd_nil_cfg && active_drop_site != 0) 
              {
                RD_Palette(RD_PaletteCode_DropSiteOverlay) UI_Rect(tab_bar_rect)
                  ui_build_box_from_key(UI_BoxFlag_DrawBackground, ui_key_zero());
              }
              
              // rjf: drop
              if(catchall_drop_site_hovered && (active_drop_site != 0 && rd_drag_drop()))
              {
                RD_Cfg *drag_view = rd_cfg_from_handle(rd_state->drag_drop_regs->view);
                RD_Cfg *src_panel = rd_cfg_from_handle(rd_state->drag_drop_regs->panel);
                RD_Cfg *dst_panel = panel->cfg;
                if(dst_panel != &rd_nil_cfg && drag_view != &rd_nil_cfg)
                {
                  rd_cmd(RD_CmdKind_MoveTab,
                         .panel     = rd_handle_from_cfg(src_panel),
                         .dst_panel = rd_handle_from_cfg(dst_panel),
                         .view      = rd_handle_from_cfg(drag_view),
                         .prev_view = rd_handle_from_cfg(active_drop_site->prev_view));
                }
              }
            }
          }
          
          // rjf: apply tab change
          {
            rd_cmd(RD_CmdKind_FocusTab, .view = rd_handle_from_cfg(next_selected_tab));
          }
          
          scratch_end(scratch);
        }
        
        //////////////////////////
        //- rjf: less granular panel-wide drop-site
        //
        if(catchall_drop_site_hovered)
        {
#if 0 // TODO(rjf): @cfg_dragdrop
          rd_last_drag_drop_panel = rd_handle_from_panel(panel);
          
          RD_View *dragged_view = rd_view_from_handle(rd_state->drag_drop_regs->view);
          B32 view_is_in_panel = 0;
          for(RD_View *view = panel->first_tab_view; !rd_view_is_nil(view); view = view->order_next)
          {
            if(rd_view_is_project_filtered(view)) { continue; }
            if(view == dragged_view)
            {
              view_is_in_panel = 1;
              break;
            }
          }
          
          if(view_is_in_panel == 0)
          {
            // rjf: vis
            {
              RD_Palette(RD_PaletteCode_DropSiteOverlay) UI_Rect(content_rect)
                ui_build_box_from_key(UI_BoxFlag_DrawBackground, ui_key_zero());
            }
            
            // rjf: drop
            {
              if(rd_drag_drop())
              {
                RD_Panel *src_panel = rd_panel_from_handle(rd_state->drag_drop_regs->panel);
                RD_View *view = rd_view_from_handle(rd_state->drag_drop_regs->view);
                if(rd_state->drag_drop_regs_slot == RD_RegSlot_View && !rd_view_is_nil(view))
                {
                  rd_cmd(RD_CmdKind_MoveTab,
                         .prev_view = rd_handle_from_view(panel->last_tab_view),
                         .panel = rd_handle_from_panel(src_panel),
                         .dst_panel = rd_handle_from_panel(panel),
                         .view = rd_handle_from_view(view));
                }
              }
            }
          }
#endif
        }
        
        //////////////////////////
        //- rjf: accept file drops
        //
        {
          for(UI_Event *evt = 0; ui_next_event(&evt);)
          {
            if(evt->kind == UI_EventKind_FileDrop && contains_2f32(content_rect, evt->pos))
            {
              B32 need_drop_completion = 0;
              arena_clear(ws->drop_completion_arena);
              MemoryZeroStruct(&ws->drop_completion_paths);
              for(String8Node *n = evt->paths.first; n != 0; n = n->next)
              {
                Temp scratch = scratch_begin(0, 0);
                String8 path = path_normalized_from_string(scratch.arena, n->string);
                if(str8_match(str8_skip_last_dot(path), str8_lit("exe"), StringMatchFlag_CaseInsensitive))
                {
                  str8_list_push(ws->drop_completion_arena, &ws->drop_completion_paths, push_str8_copy(ws->drop_completion_arena, path));
                  need_drop_completion = 1;
                }
                else
                {
                  rd_cmd(RD_CmdKind_Open, .file_path = path);
                }
                scratch_end(scratch);
              }
              if(need_drop_completion)
              {
                ui_ctx_menu_open(rd_state->drop_completion_key, ui_key_zero(), evt->pos);
              }
              ui_eat_event(evt);
            }
          }
        }
      }
    }
    
    ////////////////////////////
    //- rjf: drag/drop cancelling
    //
    if(rd_drag_is_active() && ui_slot_press(UI_EventActionSlot_Cancel))
    {
      rd_drag_kill();
      ui_kill_action();
    }
    
    ////////////////////////////
    //- rjf: font size changing
    //
    for(UI_Event *evt = 0; ui_next_event(&evt);)
    {
      if(evt->kind == UI_EventKind_Scroll && evt->modifiers & OS_Modifier_Ctrl)
      {
        ui_eat_event(evt);
        if(evt->delta_2f32.y < 0)
        {
          rd_cmd(RD_CmdKind_IncUIFontScale);
        }
        else if(evt->delta_2f32.y > 0)
        {
          rd_cmd(RD_CmdKind_DecUIFontScale);
        }
      }
    }
    
    ui_end_build();
  }
  
  //////////////////////////////
  //- rjf: ensure hover eval is in-bounds
  //
  if(!ui_box_is_nil(hover_eval_box))
  {
    UI_Box *root = hover_eval_box;
    Rng2F32 window_rect = os_client_rect_from_window(ui_window());
    Rng2F32 root_rect = root->rect;
    Vec2F32 shift =
    {
      -ClampBot(0, root_rect.x1 - window_rect.x1),
      -ClampBot(0, root_rect.y1 - window_rect.y1),
    };
    Rng2F32 new_root_rect = shift_2f32(root_rect, shift);
    root->fixed_position = new_root_rect.p0;
    root->fixed_size = dim_2f32(new_root_rect);
    root->rect = new_root_rect;
    for(Axis2 axis = (Axis2)0; axis < Axis2_COUNT; axis = (Axis2)(axis + 1))
    {
      ui_calc_sizes_standalone__in_place_rec(root, axis);
      ui_calc_sizes_upwards_dependent__in_place_rec(root, axis);
      ui_calc_sizes_downwards_dependent__in_place_rec(root, axis);
      ui_layout_enforce_constraints__in_place_rec(root, axis);
      ui_layout_position__in_place_rec(root, axis);
    }
  }
  
  //////////////////////////////
  //- rjf: attach lister boxes to root, or hide if it has not been renewed
  //
  for(ListerTask *task = first_lister_task; task != 0; task = task->next)
  {
    RD_Lister *lister = task->lister;
    RD_Regs *regs = lister->regs;
    UI_Box *lister_box = ui_box_from_key(task->root_key);
    if(!ui_box_is_nil(lister_box) && (lister != ws->autocomp_lister || ws->autocomp_lister_last_frame_idx+1 >= rd_state->frame_index+1))
    {
      UI_Box *anchor_box = ui_box_from_key(lister->regs->ui_key);
      if(!ui_box_is_nil(anchor_box))
      {
        Vec2F32 size = lister_box->fixed_size;
        lister_box->fixed_position = v2f32(anchor_box->rect.x0 + lister->regs->off_px.x,
                                           anchor_box->rect.y0 + lister->regs->off_px.y);
        lister_box->rect = r2f32(lister_box->fixed_position, add_2f32(lister_box->fixed_position, size));
        for(Axis2 axis = (Axis2)0; axis < Axis2_COUNT; axis = (Axis2)(axis + 1))
        {
          ui_calc_sizes_standalone__in_place_rec(lister_box, axis);
          ui_calc_sizes_upwards_dependent__in_place_rec(lister_box, axis);
          ui_calc_sizes_downwards_dependent__in_place_rec(lister_box, axis);
          ui_layout_enforce_constraints__in_place_rec(lister_box, axis);
          ui_layout_position__in_place_rec(lister_box, axis);
        }
      }
    }
    else if(!ui_box_is_nil(lister_box) && lister == ws->autocomp_lister && ws->autocomp_lister_last_frame_idx+1 < rd_state->frame_index+1)
    {
      UI_Box *anchor_box = ui_box_from_key(lister->regs->ui_key);
      if(!ui_box_is_nil(anchor_box))
      {
        Vec2F32 size = lister_box->fixed_size;
        Rng2F32 window_rect = os_client_rect_from_window(ws->os);
        lister_box->fixed_position = v2f32(window_rect.x1, window_rect.y1);
        lister_box->rect = r2f32(lister_box->fixed_position, add_2f32(lister_box->fixed_position, size));
      }
    }
  }
  
  //////////////////////////////
  //- rjf: hover eval cancelling
  //
  if(ws->hover_eval_string.size != 0 && ui_slot_press(UI_EventActionSlot_Cancel))
  {
    MemoryZeroStruct(&ws->hover_eval_string);
    arena_clear(ws->hover_eval_arena);
    rd_request_frame();
  }
  
  //////////////////////////////
  //- rjf: animate
  //
  if(ui_animating_from_state(ws->ui))
  {
    rd_request_frame();
  }
  
  //////////////////////////////
  //- rjf: draw UI
  //
  ws->draw_bucket = dr_bucket_make();
  DR_BucketScope(ws->draw_bucket)
    ProfScope("draw UI")
  {
    Temp scratch = scratch_begin(0, 0);
    
    //- rjf: unpack settings
    B32 do_background_blur = rd_setting_b32_from_name(str8_lit("background_blur"));
    
    //- rjf: set up heatmap buckets
    F32 heatmap_bucket_size = 32.f;
    U64 *heatmap_buckets = 0;
    U64 heatmap_bucket_pitch = 0;
    U64 heatmap_bucket_count = 0;
    if(DEV_draw_ui_box_heatmap)
    {
      Rng2F32 rect = os_client_rect_from_window(ws->os);
      Vec2F32 size = dim_2f32(rect);
      Vec2S32 buckets_dim = {(S32)(size.x/heatmap_bucket_size), (S32)(size.y/heatmap_bucket_size)};
      heatmap_bucket_pitch = buckets_dim.x;
      heatmap_bucket_count = buckets_dim.x*buckets_dim.y;
      heatmap_buckets = push_array(scratch.arena, U64, heatmap_bucket_count);
    }
    
    //- rjf: draw background color
    {
      Vec4F32 bg_color = rd_rgba_from_theme_color(RD_ThemeColor_BaseBackground);
      dr_rect(os_client_rect_from_window(ws->os), bg_color, 0, 0, 0);
    }
    
    //- rjf: draw window border
    {
      Vec4F32 color = rd_rgba_from_theme_color(RD_ThemeColor_BaseBorder);
      dr_rect(os_client_rect_from_window(ws->os), color, 0, 1.f, 0.5f);
    }
    
    //- rjf: recurse & draw
    U64 total_heatmap_sum_count = 0;
    for(UI_Box *box = ui_root_from_state(ws->ui); !ui_box_is_nil(box);)
    {
      // rjf: get recursion
      UI_BoxRec rec = ui_box_rec_df_post(box, &ui_nil_box);
      
      // rjf: sum to box heatmap
      if(DEV_draw_ui_box_heatmap)
      {
        Vec2F32 center = center_2f32(box->rect);
        Vec2S32 p = v2s32(center.x / heatmap_bucket_size, center.y / heatmap_bucket_size);
        U64 bucket_idx = p.y * heatmap_bucket_pitch + p.x;
        if(bucket_idx < heatmap_bucket_count)
        {
          heatmap_buckets[bucket_idx] += 1;
          total_heatmap_sum_count += 1;
        }
      }
      
      // rjf: push transparency
      if(box->transparency != 0)
      {
        dr_push_transparency(box->transparency);
      }
      
      // rjf: push squish
      if(box->squish != 0)
      {
        Vec2F32 box_dim = dim_2f32(box->rect);
        Mat3x3F32 box2origin_xform = make_translate_3x3f32(v2f32(-box->rect.x0 - box_dim.x/2, -box->rect.y0));
        Mat3x3F32 scale_xform = make_scale_3x3f32(v2f32(1-box->squish, 1-box->squish));
        Mat3x3F32 origin2box_xform = make_translate_3x3f32(v2f32(box->rect.x0 + box_dim.x/2, box->rect.y0));
        Mat3x3F32 xform = mul_3x3f32(origin2box_xform, mul_3x3f32(scale_xform, box2origin_xform));
        dr_push_xform2d(xform);
        dr_push_tex2d_sample_kind(R_Tex2DSampleKind_Linear);
      }
      
      // rjf: draw drop shadow
      if(box->flags & UI_BoxFlag_DrawDropShadow)
      {
        Rng2F32 drop_shadow_rect = shift_2f32(pad_2f32(box->rect, 8), v2f32(4, 4));
        Vec4F32 drop_shadow_color = rd_rgba_from_theme_color(RD_ThemeColor_DropShadow);
        dr_rect(drop_shadow_rect, drop_shadow_color, 0.8f, 0, 8.f);
      }
      
      // rjf: blur background
      if(box->flags & UI_BoxFlag_DrawBackgroundBlur && do_background_blur)
      {
        R_PassParams_Blur *params = dr_blur(pad_2f32(box->rect, 1.f), box->blur_size*(1-box->transparency), 0);
        MemoryCopyArray(params->corner_radii, box->corner_radii);
      }
      
      // rjf: draw background
      if(box->flags & UI_BoxFlag_DrawBackground)
      {
        // rjf: main rectangle
        {
          R_Rect2DInst *inst = dr_rect(pad_2f32(box->rect, 1), box->palette->colors[UI_ColorCode_Background], 0, 0, 1.f);
          MemoryCopyArray(inst->corner_radii, box->corner_radii);
        }
        
        // rjf: hot effect extension
        if(box->flags & UI_BoxFlag_DrawHotEffects)
        {
          F32 effective_active_t = box->active_t;
          if(!(box->flags & UI_BoxFlag_DrawActiveEffects))
          {
            effective_active_t = 0;
          }
          F32 t = box->hot_t*(1-effective_active_t);
          
          // rjf: brighten
          {
            R_Rect2DInst *inst = dr_rect(box->rect, v4f32(0, 0, 0, 0), 0, 0, 1.f);
            Vec4F32 color = rd_rgba_from_theme_color(RD_ThemeColor_Hover);
            color.w *= t*0.2f;
            inst->colors[Corner_00] = color;
            inst->colors[Corner_01] = color;
            inst->colors[Corner_10] = color;
            inst->colors[Corner_11] = color;
            inst->colors[Corner_10].w *= t;
            inst->colors[Corner_11].w *= t;
            MemoryCopyArray(inst->corner_radii, box->corner_radii);
          }
          
          // rjf: slight emboss fadeoff
          if(0)
          {
            Rng2F32 rect = r2f32p(box->rect.x0,
                                  box->rect.y0,
                                  box->rect.x1,
                                  box->rect.y1);
            R_Rect2DInst *inst = dr_rect(rect, v4f32(0, 0, 0, 0), 0, 0, 1.f);
            inst->colors[Corner_00] = v4f32(0.f, 0.f, 0.f, 0.0f*t);
            inst->colors[Corner_01] = v4f32(0.f, 0.f, 0.f, 0.3f*t);
            inst->colors[Corner_10] = v4f32(0.f, 0.f, 0.f, 0.0f*t);
            inst->colors[Corner_11] = v4f32(0.f, 0.f, 0.f, 0.3f*t);
            MemoryCopyArray(inst->corner_radii, box->corner_radii);
          }
        }
        
        // rjf: active effect extension
        if(box->flags & UI_BoxFlag_DrawActiveEffects)
        {
          Vec4F32 shadow_color = rd_rgba_from_theme_color(RD_ThemeColor_Hover);
          shadow_color.x *= 0.3f;
          shadow_color.y *= 0.3f;
          shadow_color.z *= 0.3f;
          shadow_color.w *= 0.5f*box->active_t;
          Vec2F32 shadow_size =
          {
            (box->rect.x1 - box->rect.x0)*0.60f*box->active_t,
            (box->rect.y1 - box->rect.y0)*0.60f*box->active_t,
          };
          shadow_size.x = Clamp(0, shadow_size.x, box->font_size*2.f);
          shadow_size.y = Clamp(0, shadow_size.y, box->font_size*2.f);
          
          // rjf: top -> bottom dark effect
          {
            R_Rect2DInst *inst = dr_rect(r2f32p(box->rect.x0, box->rect.y0, box->rect.x1, box->rect.y0 + shadow_size.y), v4f32(0, 0, 0, 0), 0, 0, 1.f);
            inst->colors[Corner_00] = inst->colors[Corner_10] = shadow_color;
            inst->colors[Corner_01] = inst->colors[Corner_11] = v4f32(0.f, 0.f, 0.f, 0.0f);
            MemoryCopyArray(inst->corner_radii, box->corner_radii);
          }
          
          // rjf: bottom -> top light effect
          {
            R_Rect2DInst *inst = dr_rect(r2f32p(box->rect.x0, box->rect.y1 - shadow_size.y, box->rect.x1, box->rect.y1), v4f32(0, 0, 0, 0), 0, 0, 1.f);
            inst->colors[Corner_00] = inst->colors[Corner_10] = v4f32(0, 0, 0, 0);
            inst->colors[Corner_01] = inst->colors[Corner_11] = v4f32(0.4f, 0.4f, 0.4f, 0.4f*box->active_t);
            MemoryCopyArray(inst->corner_radii, box->corner_radii);
          }
          
          // rjf: left -> right dark effect
          {
            R_Rect2DInst *inst = dr_rect(r2f32p(box->rect.x0, box->rect.y0, box->rect.x0 + shadow_size.x, box->rect.y1), v4f32(0, 0, 0, 0), 0, 0, 1.f);
            inst->colors[Corner_10] = inst->colors[Corner_11] = v4f32(0.f, 0.f, 0.f, 0.f);
            inst->colors[Corner_00] = shadow_color;
            inst->colors[Corner_01] = shadow_color;
            MemoryCopyArray(inst->corner_radii, box->corner_radii);
          }
          
          // rjf: right -> left dark effect
          {
            R_Rect2DInst *inst = dr_rect(r2f32p(box->rect.x1 - shadow_size.x, box->rect.y0, box->rect.x1, box->rect.y1), v4f32(0, 0, 0, 0), 0, 0, 1.f);
            inst->colors[Corner_00] = inst->colors[Corner_01] = v4f32(0.f, 0.f, 0.f, 0.f);
            inst->colors[Corner_10] = shadow_color;
            inst->colors[Corner_11] = shadow_color;
            MemoryCopyArray(inst->corner_radii, box->corner_radii);
          }
        }
      }
      
      // rjf: draw string
      if(box->flags & UI_BoxFlag_DrawText)
      {
        Vec2F32 text_position = ui_box_text_position(box);
        if(DEV_draw_ui_text_pos)
        {
          dr_rect(r2f32p(text_position.x-4, text_position.y-4, text_position.x+4, text_position.y+4),
                  v4f32(1, 0, 1, 1), 1, 0, 1);
        }
        F32 max_x = 100000.f;
        FNT_Run ellipses_run = {0};
        if(!(box->flags & UI_BoxFlag_DisableTextTrunc))
        {
          max_x = (box->rect.x1-text_position.x);
          ellipses_run = fnt_push_run_from_string(scratch.arena, box->font, box->font_size, 0, box->tab_size, 0, str8_lit("..."));
        }
        dr_truncated_fancy_run_list(text_position, &box->display_string_runs, max_x, ellipses_run);
        if(box->flags & UI_BoxFlag_HasFuzzyMatchRanges)
        {
          Vec4F32 match_color = rd_rgba_from_theme_color(RD_ThemeColor_HighlightOverlay);
          dr_truncated_fancy_run_fuzzy_matches(text_position, &box->display_string_runs, max_x, &box->fuzzy_match_ranges, match_color);
        }
      }
      
      // rjf: draw focus viz
      if(DEV_draw_ui_focus_debug)
      {
        B32 focused = (box->flags & (UI_BoxFlag_FocusHot|UI_BoxFlag_FocusActive) &&
                       box->flags & UI_BoxFlag_Clickable);
        B32 disabled = 0;
        for(UI_Box *p = box; !ui_box_is_nil(p); p = p->parent)
        {
          if(p->flags & (UI_BoxFlag_FocusHotDisabled|UI_BoxFlag_FocusActiveDisabled))
          {
            disabled = 1;
            break;
          }
        }
        if(focused)
        {
          Vec4F32 color = v4f32(0.3f, 0.8f, 0.3f, 1.f);
          if(disabled)
          {
            color = v4f32(0.8f, 0.3f, 0.3f, 1.f);
          }
          dr_rect(r2f32p(box->rect.x0-6, box->rect.y0-6, box->rect.x0+6, box->rect.y0+6), color, 2, 0, 1);
          dr_rect(box->rect, color, 2, 2, 1);
        }
        if(box->flags & (UI_BoxFlag_FocusHot|UI_BoxFlag_FocusActive))
        {
          if(box->flags & (UI_BoxFlag_FocusHotDisabled|UI_BoxFlag_FocusActiveDisabled))
          {
            dr_rect(r2f32p(box->rect.x0-6, box->rect.y0-6, box->rect.x0+6, box->rect.y0+6), v4f32(1, 0, 0, 0.2f), 2, 0, 1);
          }
          else
          {
            dr_rect(r2f32p(box->rect.x0-6, box->rect.y0-6, box->rect.x0+6, box->rect.y0+6), v4f32(0, 1, 0, 0.2f), 2, 0, 1);
          }
        }
      }
      
      // rjf: push clip
      if(box->flags & UI_BoxFlag_Clip)
      {
        Rng2F32 top_clip = dr_top_clip();
        Rng2F32 new_clip = pad_2f32(box->rect, -1);
        if(top_clip.x1 != 0 || top_clip.y1 != 0)
        {
          new_clip = intersect_2f32(new_clip, top_clip);
        }
        dr_push_clip(new_clip);
      }
      
      // rjf: custom draw list
      if(box->flags & UI_BoxFlag_DrawBucket)
      {
        Mat3x3F32 xform = make_translate_3x3f32(box->position_delta);
        DR_XForm2DScope(xform)
        {
          dr_sub_bucket(box->draw_bucket);
        }
      }
      
      // rjf: call custom draw callback
      if(box->custom_draw != 0)
      {
        box->custom_draw(box, box->custom_draw_user_data);
      }
      
      // rjf: pop
      {
        S32 pop_idx = 0;
        for(UI_Box *b = box; !ui_box_is_nil(b) && pop_idx <= rec.pop_count; b = b->parent)
        {
          pop_idx += 1;
          if(b == box && rec.push_count != 0)
          {
            continue;
          }
          
          // rjf: pop clips
          if(b->flags & UI_BoxFlag_Clip)
          {
            dr_pop_clip();
          }
          
          // rjf: draw overlay
          if(b->flags & UI_BoxFlag_DrawOverlay)
          {
            R_Rect2DInst *inst = dr_rect(b->rect, b->palette->colors[UI_ColorCode_Overlay], 0, 0, 1.f);
            MemoryCopyArray(inst->corner_radii, b->corner_radii);
          }
          
          // rjf: draw border
          if(b->flags & UI_BoxFlag_DrawBorder)
          {
            Rng2F32 b_border_rect = pad_2f32(b->rect, 1.f);
            R_Rect2DInst *inst = dr_rect(b_border_rect, b->palette->colors[UI_ColorCode_Border], 0, 1.f, 1.f);
            MemoryCopyArray(inst->corner_radii, b->corner_radii);
            
            // rjf: hover effect
            if(b->flags & UI_BoxFlag_DrawHotEffects)
            {
              Vec4F32 color = rd_rgba_from_theme_color(RD_ThemeColor_Hover);
              color.w *= b->hot_t;
              R_Rect2DInst *inst = dr_rect(b_border_rect, color, 0, 1.f, 1.f);
              MemoryCopyArray(inst->corner_radii, b->corner_radii);
            }
          }
          
          // rjf: debug border rendering
          if(0)
          {
            R_Rect2DInst *inst = dr_rect(b->rect, v4f32(1, 0, 1, 0.25f), 0, 1.f, 1.f);
            MemoryCopyArray(inst->corner_radii, b->corner_radii);
          }
          
          // rjf: draw sides
          {
            Rng2F32 r = b->rect;
            F32 half_thickness = 1.f;
            F32 softness = 0.5f;
            if(b->flags & UI_BoxFlag_DrawSideTop)
            {
              dr_rect(r2f32p(r.x0, r.y0-half_thickness, r.x1, r.y0+half_thickness), b->palette->colors[UI_ColorCode_Border], 0, 0, softness);
            }
            if(b->flags & UI_BoxFlag_DrawSideBottom)
            {
              dr_rect(r2f32p(r.x0, r.y1-half_thickness, r.x1, r.y1+half_thickness), b->palette->colors[UI_ColorCode_Border], 0, 0, softness);
            }
            if(b->flags & UI_BoxFlag_DrawSideLeft)
            {
              dr_rect(r2f32p(r.x0-half_thickness, r.y0, r.x0+half_thickness, r.y1), b->palette->colors[UI_ColorCode_Border], 0, 0, softness);
            }
            if(b->flags & UI_BoxFlag_DrawSideRight)
            {
              dr_rect(r2f32p(r.x1-half_thickness, r.y0, r.x1+half_thickness, r.y1), b->palette->colors[UI_ColorCode_Border], 0, 0, softness);
            }
          }
          
          // rjf: draw focus overlay
          if(b->flags & UI_BoxFlag_Clickable && !(b->flags & UI_BoxFlag_DisableFocusOverlay) && b->focus_hot_t > 0.01f)
          {
            Vec4F32 color = rd_rgba_from_theme_color(RD_ThemeColor_Focus);
            color.w *= 0.2f*b->focus_hot_t;
            R_Rect2DInst *inst = dr_rect(b->rect, color, 0, 0, 0.f);
            MemoryCopyArray(inst->corner_radii, b->corner_radii);
          }
          
          // rjf: draw focus border
          if(b->flags & UI_BoxFlag_Clickable && !(b->flags & UI_BoxFlag_DisableFocusBorder) && b->focus_active_t > 0.01f)
          {
            Vec4F32 color = rd_rgba_from_theme_color(RD_ThemeColor_Focus);
            color.w *= b->focus_active_t;
            R_Rect2DInst *inst = dr_rect(b->rect, color, 0, 1.f, 1.f);
            MemoryCopyArray(inst->corner_radii, b->corner_radii);
          }
          
          // rjf: disabled overlay
          if(b->disabled_t >= 0.005f)
          {
            Vec4F32 color = rd_rgba_from_theme_color(RD_ThemeColor_DisabledOverlay);
            color.w *= b->disabled_t;
            R_Rect2DInst *inst = dr_rect(b->rect, color, 0, 0, 1);
            MemoryCopyArray(inst->corner_radii, b->corner_radii);
          }
          
          // rjf: pop squish
          if(b->squish != 0)
          {
            dr_pop_xform2d();
            dr_pop_tex2d_sample_kind();
          }
          
          // rjf: pop transparency
          if(b->transparency != 0)
          {
            dr_pop_transparency();
          }
        }
      }
      
      // rjf: next
      box = rec.next;
    }
    
    //- rjf: draw heatmap
    if(DEV_draw_ui_box_heatmap)
    {
      U64 uniform_dist_count = total_heatmap_sum_count / heatmap_bucket_count;
      uniform_dist_count = ClampBot(uniform_dist_count, 10);
      for(U64 bucket_idx = 0; bucket_idx < heatmap_bucket_count; bucket_idx += 1)
      {
        U64 x = bucket_idx % heatmap_bucket_pitch;
        U64 y = bucket_idx / heatmap_bucket_pitch;
        U64 bucket = heatmap_buckets[bucket_idx];
        F32 pct = (F32)bucket / uniform_dist_count;
        pct = Clamp(0, pct, 1);
        Vec3F32 hsv = v3f32((1-pct) * 0.9411f, 1, 0.5f);
        Vec3F32 rgb = rgb_from_hsv(hsv);
        Rng2F32 rect = r2f32p(x*heatmap_bucket_size, y*heatmap_bucket_size, (x+1)*heatmap_bucket_size, (y+1)*heatmap_bucket_size);
        dr_rect(rect, v4f32(rgb.x, rgb.y, rgb.z, 0.3f), 0, 0, 0);
      }
    }
    
    //- rjf: draw border/overlay color to signify error
    if(ws->error_t > 0.01f)
    {
      Vec4F32 color = rd_rgba_from_theme_color(RD_ThemeColor_NegativePopButtonBackground);
      color.w *= ws->error_t;
      Rng2F32 rect = os_client_rect_from_window(ws->os);
      dr_rect(pad_2f32(rect, 24.f), color, 0, 16.f, 12.f);
      dr_rect(rect, v4f32(color.x, color.y, color.z, color.w*0.05f), 0, 0, 0);
    }
    
    //- rjf: scratch debug mouse drawing
    if(DEV_scratch_mouse_draw)
    {
#if 1
      Vec2F32 p = add_2f32(os_mouse_from_window(ws->os), v2f32(30, 0));
      dr_rect(os_client_rect_from_window(ws->os), v4f32(0, 0, 0, 0.9f), 0, 0, 0);
      FNT_Run trailer_run = fnt_push_run_from_string(scratch.arena, rd_font_from_slot(RD_FontSlot_Main), 16.f, 0, 0, 0, str8_lit("..."));
      DR_FancyStringList strs = {0};
      DR_FancyString str = {rd_font_from_slot(RD_FontSlot_Main), str8_lit("Shift + F5"), v4f32(1, 1, 1, 1), 72.f, 0.f};
      dr_fancy_string_list_push(scratch.arena, &strs, &str);
      DR_FancyRunList runs = dr_fancy_run_list_from_fancy_string_list(scratch.arena, 0, FNT_RasterFlag_Smooth, &strs);
      dr_truncated_fancy_run_list(p, &runs, 1000000.f, trailer_run);
      dr_rect(r2f32(p, add_2f32(p, runs.dim)), v4f32(1, 0, 0, 0.5f), 0, 1, 0);
      dr_rect(r2f32(sub_2f32(p, v2f32(4, 4)), add_2f32(p, v2f32(4, 4))), v4f32(1, 0, 1, 1), 0, 0, 0);
#else
      Vec2F32 p = add_2f32(os_mouse_from_window(ws->os), v2f32(30, 0));
      dr_rect(os_client_rect_from_window(ws->os), v4f32(0, 0, 0, 0.4f), 0, 0, 0);
      DR_FancyStringList strs = {0};
      DR_FancyString str1 = {rd_font_from_slot(RD_FontSlot_Main), str8_lit("T"), v4f32(1, 1, 1, 1), 16.f, 4.f};
      dr_fancy_string_list_push(scratch.arena, &strs, &str1);
      DR_FancyString str2 = {rd_font_from_slot(RD_FontSlot_Main), str8_lit("his is a test of some "), v4f32(1, 0.5f, 0.5f, 1), 14.f, 0.f};
      dr_fancy_string_list_push(scratch.arena, &strs, &str2);
      DR_FancyString str3 = {rd_font_from_slot(RD_FontSlot_Code), str8_lit("very fancy text!"), v4f32(1, 0.8f, 0.4f, 1), 18.f, 4.f, 4.f};
      dr_fancy_string_list_push(scratch.arena, &strs, &str3);
      DR_FancyRunList runs = dr_fancy_run_list_from_fancy_string_list(scratch.arena, 0, 0, &strs);
      FNT_Run trailer_run = fnt_push_run_from_string(scratch.arena, rd_font_from_slot(RD_FontSlot_Main), 16.f, 0, 0, 0, str8_lit("..."));
      F32 limit = 500.f + sin_f32(rd_state->time_in_seconds/10.f)*200.f;
      dr_truncated_fancy_run_list(p, &runs, limit, trailer_run);
      dr_rect(r2f32p(p.x+limit, 0, p.x+limit+2.f, 1000), v4f32(1, 0, 0, 1), 0, 0, 0);
      rd_request_frame();
#endif
    }
    
    scratch_end(scratch);
  }
  
  //////////////////////////////
  //- rjf: increment per-window frame counter
  //
  ws->frames_alive += 1;
  
  ProfEnd();
  scratch_end(scratch);
}

#if COMPILER_MSVC && !BUILD_DEBUG
#pragma optimize("", on)
#endif

////////////////////////////////
//~ rjf: Eval Visualization

typedef struct RD_CtrlEntityExpandAccel RD_CtrlEntityExpandAccel;
struct RD_CtrlEntityExpandAccel
{
  CTRL_EntityArray entities;
};

#if 0 // TODO(rjf): @cfg
typedef struct RD_EntityExpandAccel RD_EntityExpandAccel;
struct RD_EntityExpandAccel
{
  RD_EntityArray entities;
};

//- rjf: control entity hierarchies

EV_VIEW_RULE_EXPR_EXPAND_INFO_FUNCTION_DEF(scheduler_machine)
{
  EV_ExpandInfo info = {0};
  Temp scratch = scratch_begin(&arena, 1);
  E_Eval eval = e_eval_from_expr(scratch.arena, expr);
  CTRL_Entity *machine = rd_ctrl_entity_from_eval_space(eval.space);
  if(machine->kind == CTRL_EntityKind_Machine)
  {
    CTRL_EntityList processes = {0};
    for(CTRL_Entity *child = machine->first; child != &ctrl_entity_nil; child = child->next)
    {
      if(child->kind == CTRL_EntityKind_Process)
      {
        ctrl_entity_list_push(scratch.arena, &processes, child);
      }
    }
    CTRL_EntityArray *processes_array = push_array(arena, CTRL_EntityArray, 1);
    *processes_array = ctrl_entity_array_from_list(arena, &processes);
    info.user_data = processes_array;
    info.row_count = processes.count;
    info.rows_default_expanded = 1;
  }
  scratch_end(scratch);
  return info;
}

EV_VIEW_RULE_EXPR_EXPAND_RANGE_INFO_FUNCTION_DEF(scheduler_machine)
{
  EV_ExpandRangeInfo info = {0};
  {
    CTRL_EntityArray *processes = (CTRL_EntityArray *)user_data;
    if(processes != 0)
    {
      info.row_exprs_count = dim_1u64(idx_range);
      info.row_strings     = push_array(arena, String8,    info.row_exprs_count);
      info.row_view_rules  = push_array(arena, String8,    info.row_exprs_count);
      info.row_exprs       = push_array(arena, E_Expr *,   info.row_exprs_count);
      info.row_members     = push_array(arena, E_Member *, info.row_exprs_count);
      U64 row_expr_idx = 0;
      for(U64 idx = idx_range.min; idx < idx_range.max; idx += 1, row_expr_idx += 1)
      {
        CTRL_Entity *process = processes->v[idx];
        E_Expr *expr = e_push_expr(arena, E_ExprKind_LeafOffset, 0);
        expr->space    = rd_eval_space_from_ctrl_entity(process, RD_EvalSpaceKind_MetaCtrlEntity);
        expr->mode     = E_Mode_Offset;
        expr->type_key = e_type_key_cons_base(type(CTRL_ProcessMetaEval));;
        info.row_exprs[row_expr_idx]   = expr;
        info.row_members[row_expr_idx] = &e_member_nil;
      }
    }
  }
  return info;
}

EV_VIEW_RULE_EXPR_EXPAND_ID_FROM_NUM_FUNCTION_DEF(scheduler_machine)
{
  return num;
}

EV_VIEW_RULE_EXPR_EXPAND_NUM_FROM_ID_FUNCTION_DEF(scheduler_machine)
{
  return id;
}

EV_VIEW_RULE_EXPR_EXPAND_INFO_FUNCTION_DEF(scheduler_process)
{
  EV_ExpandInfo info = {0};
  Temp scratch = scratch_begin(&arena, 1);
  E_Eval eval = e_eval_from_expr(scratch.arena, expr);
  CTRL_Entity *process = rd_ctrl_entity_from_eval_space(eval.space);
  if(process->kind == CTRL_EntityKind_Process)
  {
    CTRL_EntityList threads = {0};
    for(CTRL_Entity *child = process->first; child != &ctrl_entity_nil; child = child->next)
    {
      if(child->kind == CTRL_EntityKind_Thread)
      {
        B32 is_in_filter = 1;
        if(filter.size != 0)
        {
          is_in_filter = 0;
          FuzzyMatchRangeList matches = fuzzy_match_find(scratch.arena, filter, child->string);
          if(matches.count == matches.needle_part_count)
          {
            is_in_filter = 1;
          }
          else
          {
            DI_Scope *di_scope = di_scope_open();
            CTRL_Unwind unwind = d_query_cached_unwind_from_thread(child);
            CTRL_CallStack call_stack = ctrl_call_stack_from_unwind(scratch.arena, di_scope, process, &unwind);
            for(U64 idx = 0; idx < call_stack.concrete_frame_count && idx < 5; idx += 1)
            {
              CTRL_CallStackFrame *f = &call_stack.frames[idx];
              String8 name = {0};
              name.str = rdi_string_from_idx(f->rdi, f->procedure->name_string_idx, &name.size);
              FuzzyMatchRangeList matches = fuzzy_match_find(scratch.arena, filter, name);
              if(matches.count == matches.needle_part_count)
              {
                is_in_filter = 1;
                break;
              }
            }
            di_scope_close(di_scope);
          }
        }
        if(is_in_filter)
        {
          ctrl_entity_list_push(scratch.arena, &threads, child);
        }
      }
    }
    CTRL_EntityArray *threads_array = push_array(arena, CTRL_EntityArray, 1);
    *threads_array = ctrl_entity_array_from_list(arena, &threads);
    info.user_data = threads_array;
    info.row_count = threads.count;
  }
  scratch_end(scratch);
  return info;
}

EV_VIEW_RULE_EXPR_EXPAND_RANGE_INFO_FUNCTION_DEF(scheduler_process)
{
  EV_ExpandRangeInfo info = {0};
  {
    CTRL_EntityArray *threads = (CTRL_EntityArray *)user_data;
    if(threads != 0)
    {
      info.row_exprs_count = dim_1u64(idx_range);
      info.row_strings     = push_array(arena, String8,    info.row_exprs_count);
      info.row_view_rules  = push_array(arena, String8,    info.row_exprs_count);
      info.row_exprs       = push_array(arena, E_Expr *,   info.row_exprs_count);
      info.row_members     = push_array(arena, E_Member *, info.row_exprs_count);
      U64 row_expr_idx = 0;
      for(U64 idx = idx_range.min; idx < idx_range.max; idx += 1, row_expr_idx += 1)
      {
        CTRL_Entity *thread = threads->v[idx];
        E_Expr *expr = e_push_expr(arena, E_ExprKind_LeafOffset, 0);
        expr->space    = rd_eval_space_from_ctrl_entity(thread, RD_EvalSpaceKind_MetaCtrlEntity);
        expr->mode     = E_Mode_Offset;
        expr->type_key = e_type_key_cons_base(type(CTRL_ThreadMetaEval));
        info.row_exprs[row_expr_idx]   = expr;
        info.row_members[row_expr_idx] = &e_member_nil;
      }
    }
  }
  return info;
}

EV_VIEW_RULE_EXPR_EXPAND_ID_FROM_NUM_FUNCTION_DEF(scheduler_process)
{
  return num;
}

EV_VIEW_RULE_EXPR_EXPAND_NUM_FROM_ID_FUNCTION_DEF(scheduler_process)
{
  return id;
}
#endif

//- rjf: debug info tables

E_LOOKUP_INFO_FUNCTION_DEF(watches)
{
  E_LookupInfo result = {0};
  Temp scratch = scratch_begin(&arena, 1);
  {
    RD_CfgList cfgs_list = rd_cfg_top_level_list_from_string(scratch.arena, str8_lit("watch"));
    RD_CfgList cfgs_list__filtered = cfgs_list;
    if(filter.size != 0)
    {
      MemoryZeroStruct(&cfgs_list__filtered);
      for(RD_CfgNode *n = cfgs_list.first; n != 0; n = n->next)
      {
        String8 expr = rd_expr_from_cfg(n->v);
        FuzzyMatchRangeList matches = fuzzy_match_find(scratch.arena, filter, expr);
        if(matches.count == matches.needle_part_count)
        {
          rd_cfg_list_push(scratch.arena, &cfgs_list__filtered, n->v);
        }
      }
    }
    RD_CfgArray *cfgs = push_array(arena, RD_CfgArray, 1);
    cfgs[0] = rd_cfg_array_from_list(arena, &cfgs_list__filtered);
    result.user_data = cfgs;
    result.idxed_expr_count = cfgs->count + 1;
  }
  scratch_end(scratch);
  return result;
}

E_LOOKUP_RANGE_FUNCTION_DEF(watches)
{
  RD_CfgArray *cfgs = (RD_CfgArray *)user_data;
  Rng1U64 legal_idx_range = r1u64(0, cfgs->count);
  Rng1U64 read_range = intersect_1u64(idx_range, legal_idx_range);
  U64 read_range_count = dim_1u64(read_range);
  for(U64 idx = 0; idx < read_range_count; idx += 1)
  {
    U64 cfg_idx = read_range.min + idx;
    if(cfg_idx < cfgs->count)
    {
      String8 expr_string = rd_cfg_child_from_string(cfgs->v[idx], str8_lit("expression"))->first->string;
      exprs[idx] = e_parse_expr_from_text(arena, expr_string);
      exprs_strings[idx] = push_str8_copy(arena, expr_string);
    }
  }
}

E_LOOKUP_INFO_FUNCTION_DEF(locals)
{
  E_LookupInfo result = {0};
  Temp scratch = scratch_begin(&arena, 1);
  {
    E_String2NumMapNodeArray nodes = e_string2num_map_node_array_from_map(scratch.arena, e_parse_state->ctx->locals_map);
    e_string2num_map_node_array_sort__in_place(&nodes);
    String8List exprs_filtered = {0};
    for EachIndex(idx, nodes.count)
    {
      String8 local_expr_string = nodes.v[idx]->string;
      FuzzyMatchRangeList matches = fuzzy_match_find(scratch.arena, filter, local_expr_string);
      if(matches.count == matches.needle_part_count)
      {
        str8_list_push(scratch.arena, &exprs_filtered, local_expr_string);
      }
    }
    String8Array *accel = push_array(arena, String8Array, 1);
    *accel = str8_array_from_list(arena, &exprs_filtered);
    result.user_data = accel;
    result.idxed_expr_count = accel->count;
  }
  scratch_end(scratch);
  return result;
}

E_LOOKUP_RANGE_FUNCTION_DEF(locals)
{
  String8Array *accel = (String8Array *)user_data;
  Rng1U64 legal_idx_range = r1u64(0, accel->count);
  Rng1U64 read_range = intersect_1u64(idx_range, legal_idx_range);
  U64 read_range_count = dim_1u64(read_range);
  for(U64 idx = 0; idx < read_range_count; idx += 1)
  {
    String8 expr_string = accel->v[read_range.min + idx];
    exprs[idx] = e_parse_expr_from_text(arena, expr_string);
    exprs_strings[idx] = push_str8_copy(arena, expr_string);
  }
}

E_LOOKUP_INFO_FUNCTION_DEF(registers)
{
  Temp scratch = scratch_begin(&arena, 1);
  CTRL_Entity *thread = ctrl_entity_from_handle(d_state->ctrl_entity_store, rd_regs()->thread);
  Arch arch = thread->arch;
  U64 reg_count     = regs_reg_code_count_from_arch(arch);
  U64 alias_count   = regs_alias_code_count_from_arch(arch);
  String8 *reg_strings   = regs_reg_code_string_table_from_arch(arch);
  String8 *alias_strings = regs_alias_code_string_table_from_arch(arch);
  String8List exprs_list = {0};
  for(U64 idx = 1; idx < reg_count; idx += 1)
  {
    FuzzyMatchRangeList matches = fuzzy_match_find(scratch.arena, filter, reg_strings[idx]);
    if(matches.count == matches.needle_part_count)
    {
      str8_list_push(scratch.arena, &exprs_list, reg_strings[idx]);
    }
  }
  for(U64 idx = 1; idx < alias_count; idx += 1)
  {
    FuzzyMatchRangeList matches = fuzzy_match_find(scratch.arena, filter, alias_strings[idx]);
    if(matches.count == matches.needle_part_count)
    {
      str8_list_push(scratch.arena, &exprs_list, alias_strings[idx]);
    }
  }
  String8Array *accel = push_array(arena, String8Array, 1);
  *accel = str8_array_from_list(arena, &exprs_list);
  E_LookupInfo info = {accel, 0, accel->count};
  scratch_end(scratch);
  return info;
}

E_LOOKUP_RANGE_FUNCTION_DEF(registers)
{
  String8Array *accel = (String8Array *)user_data;
  Rng1U64 legal_idx_range = r1u64(0, accel->count);
  Rng1U64 read_range = intersect_1u64(legal_idx_range, idx_range);
  U64 read_range_count = dim_1u64(read_range);
  for(U64 idx = 0; idx < read_range_count; idx += 1)
  {
    String8 register_name = accel->v[read_range.min + idx];
    String8 register_expr = push_str8f(arena, "reg:%S", register_name);
    exprs_strings[idx] = register_name;
    exprs[idx] = e_parse_expr_from_text(arena, register_expr);
  }
}

typedef struct RD_TopLevelCfgLookupAccel RD_TopLevelCfgLookupAccel;
struct RD_TopLevelCfgLookupAccel
{
  String8Array cmds;
  RD_CfgArray cfgs;
};

E_LOOKUP_INFO_FUNCTION_DEF(top_level_cfg)
{
  E_LookupInfo result = {0};
  Temp scratch = scratch_begin(&arena, 1);
  {
    E_TypeKey lhs_type_key = lhs->type_key;
    E_Type *lhs_type = e_type_from_key__cached(lhs_type_key);
    String8 cfg_name = rd_singular_from_code_name_plural(lhs_type->name);
    RD_CfgList cfgs_list = rd_cfg_top_level_list_from_string(scratch.arena, cfg_name);\
    String8List cmds_list = {0};
    // TODO(rjf): @cfg hack - probably want to table-drive this
    if(str8_match(cfg_name, str8_lit("target"), 0))
    {
      str8_list_push(arena, &cmds_list, rd_cmd_kind_info_table[RD_CmdKind_AddTarget].string);
    }
    else if(str8_match(cfg_name, str8_lit("breakpoint"), 0))
    {
      str8_list_push(arena, &cmds_list, rd_cmd_kind_info_table[RD_CmdKind_AddBreakpoint].string);
      str8_list_push(arena, &cmds_list, rd_cmd_kind_info_table[RD_CmdKind_AddAddressBreakpoint].string);
      str8_list_push(arena, &cmds_list, rd_cmd_kind_info_table[RD_CmdKind_AddFunctionBreakpoint].string);
    }
    else if(str8_match(cfg_name, str8_lit("watch_pin"), 0))
    {
      str8_list_push(arena, &cmds_list, rd_cmd_kind_info_table[RD_CmdKind_AddWatchPin].string);
    }
    RD_TopLevelCfgLookupAccel *accel = push_array(arena, RD_TopLevelCfgLookupAccel, 1);
    accel->cfgs = rd_cfg_array_from_list(arena, &cfgs_list);
    accel->cmds = str8_array_from_list(arena, &cmds_list);
    result.user_data = accel;
    result.idxed_expr_count = accel->cfgs.count + accel->cmds.count;
  }
  scratch_end(scratch);
  return result;
}

E_LOOKUP_ACCESS_FUNCTION_DEF(top_level_cfg)
{
  E_LookupAccess result = {{&e_irnode_nil}};
  if(kind == E_ExprKind_ArrayIndex)
  {
    Temp scratch = scratch_begin(&arena, 1);
    RD_TopLevelCfgLookupAccel *accel = (RD_TopLevelCfgLookupAccel *)user_data;
    Rng1U64 cmds_idx_range = r1u64(0, accel->cmds.count);
    Rng1U64 cfgs_idx_range = r1u64(accel->cmds.count, accel->cmds.count + accel->cfgs.count);
    E_IRTreeAndType rhs_irtree = e_irtree_and_type_from_expr(scratch.arena, rhs);
    E_OpList rhs_oplist = e_oplist_from_irtree(scratch.arena, rhs_irtree.root);
    String8 rhs_bytecode = e_bytecode_from_oplist(scratch.arena, &rhs_oplist);
    E_Interpretation rhs_interp = e_interpret(rhs_bytecode);
    E_Value rhs_value = rhs_interp.value;
    if(contains_1u64(cmds_idx_range, rhs_value.u64))
    {
      String8 cmd_name = accel->cmds.v[rhs_value.u64 - cmds_idx_range.min];
      RD_CmdKind cmd_kind = rd_cmd_kind_from_string(cmd_name);
      E_TypeKey type_key = e_type_key_basic(E_TypeKind_U64);
      E_Space cmd_space = e_space_make(RD_EvalSpaceKind_MetaCmd);
      result.irtree_and_type.root      = e_irtree_set_space(arena, cmd_space, e_irtree_const_u(arena, cmd_kind));
      result.irtree_and_type.type_key  = type_key;
      result.irtree_and_type.mode      = E_Mode_Value;
    }
    else if(contains_1u64(cfgs_idx_range, rhs_value.u64))
    {
      RD_Cfg *cfg = accel->cfgs.v[rhs_value.u64 - cfgs_idx_range.min];
      E_Space cfg_space = rd_eval_space_from_cfg(cfg);
      String8 cfg_name = cfg->string;
      E_TypeKey cfg_type_key = e_string2typekey_map_lookup(rd_state->meta_name2type_map, cfg_name);
      result.irtree_and_type.root      = e_irtree_set_space(arena, cfg_space, e_irtree_const_u(arena, 0));
      result.irtree_and_type.type_key  = cfg_type_key;
      result.irtree_and_type.mode      = E_Mode_Offset;
    }
    scratch_end(scratch);
  }
  return result;
}

E_LOOKUP_INFO_FUNCTION_DEF(ctrl_entities)
{
  E_LookupInfo result = {0};
  Temp scratch = scratch_begin(&arena, 1);
  {
    E_TypeKey lhs_type_key = lhs->type_key;
    E_Type *lhs_type = e_type_from_key__cached(lhs_type_key);
    String8 name = rd_singular_from_code_name_plural(lhs_type->name);
    CTRL_EntityKind entity_kind = CTRL_EntityKind_Null;
    for EachNonZeroEnumVal(CTRL_EntityKind, k)
    {
      if(str8_match(name, ctrl_entity_kind_code_name_table[k], 0))
      {
        entity_kind = k;
        break;
      }
    }
    CTRL_EntityList list = ctrl_entity_list_from_kind(d_state->ctrl_entity_store, entity_kind);
    CTRL_EntityList list__filtered = list;
    if(filter.size != 0)
    {
      MemoryZeroStruct(&list__filtered);
      for(CTRL_EntityNode *n = list.first; n != 0; n = n->next)
      {
        CTRL_Entity *entity = n->v;
        FuzzyMatchRangeList matches = fuzzy_match_find(scratch.arena, filter, entity->string);
        if(matches.count == matches.needle_part_count)
        {
          ctrl_entity_list_push(scratch.arena, &list__filtered, entity);
        }
      }
    }
    CTRL_EntityArray *array = push_array(arena, CTRL_EntityArray, 1);
    *array = ctrl_entity_array_from_list(arena, &list__filtered);
    result.user_data = array;
    result.idxed_expr_count = list.count;
  }
  scratch_end(scratch);
  return result;
}

E_LOOKUP_ACCESS_FUNCTION_DEF(ctrl_entities)
{
  E_LookupAccess result = {{&e_irnode_nil}};
  if(kind == E_ExprKind_ArrayIndex)
  {
    Temp scratch = scratch_begin(&arena, 1);
    CTRL_EntityArray *entities = (CTRL_EntityArray *)user_data;
    E_IRTreeAndType rhs_irtree = e_irtree_and_type_from_expr(scratch.arena, rhs);
    E_OpList rhs_oplist = e_oplist_from_irtree(scratch.arena, rhs_irtree.root);
    String8 rhs_bytecode = e_bytecode_from_oplist(scratch.arena, &rhs_oplist);
    E_Interpretation rhs_interp = e_interpret(rhs_bytecode);
    E_Value rhs_value = rhs_interp.value;
    if(0 <= rhs_value.u64 && rhs_value.u64 < entities->count)
    {
      CTRL_Entity *entity = entities->v[rhs_value.u64];
      E_Space entity_space = rd_eval_space_from_ctrl_entity(entity, RD_EvalSpaceKind_MetaCtrlEntity);
      String8 entity_name = ctrl_entity_kind_code_name_table[entity->kind];
      E_TypeKey entity_type_key = e_string2typekey_map_lookup(rd_state->meta_name2type_map, entity_name);
      result.irtree_and_type.root      = e_irtree_set_space(arena, entity_space, e_irtree_const_u(arena, 0));
      result.irtree_and_type.type_key  = entity_type_key;
      result.irtree_and_type.mode      = E_Mode_Offset;
    }
    scratch_end(scratch);
  }
  return result;
}

typedef struct RD_DebugInfoTableLookupAccel RD_DebugInfoTableLookupAccel;
struct RD_DebugInfoTableLookupAccel
{
  RDI_SectionKind section;
  U64 rdis_count;
  RDI_Parsed **rdis;
  DI_SearchItemArray items;
};

E_LOOKUP_INFO_FUNCTION_DEF(debug_info_table)
{
  Temp scratch = scratch_begin(&arena, 1);
  
  // rjf: determine which debug info section we're dealing with
  RDI_SectionKind section = RDI_SectionKind_NULL;
  {
    E_TypeKey lhs_type_key = lhs->type_key;
    E_Type *lhs_type = e_type_from_key__cached(lhs_type_key);
    if(0){}
    else if(str8_match(lhs_type->name, str8_lit("procedures"), 0))       {section = RDI_SectionKind_Procedures;}
    else if(str8_match(lhs_type->name, str8_lit("globals"), 0))          {section = RDI_SectionKind_GlobalVariables;}
    else if(str8_match(lhs_type->name, str8_lit("thread_locals"), 0))    {section = RDI_SectionKind_ThreadVariables;}
    else if(str8_match(lhs_type->name, str8_lit("types"), 0))            {section = RDI_SectionKind_UDTs;}
  }
  
  // rjf: gather debug info table items
  RD_DebugInfoTableLookupAccel *accel = push_array(arena, RD_DebugInfoTableLookupAccel, 1);
  if(section != RDI_SectionKind_NULL)
  {
    U64 endt_us = d_state->frame_eval_memread_endt_us;
    
    //- rjf: unpack context
    DI_KeyList dbgi_keys_list = d_push_active_dbgi_key_list(scratch.arena);
    DI_KeyArray dbgi_keys = di_key_array_from_list(scratch.arena, &dbgi_keys_list);
    U64 rdis_count = dbgi_keys.count;
    RDI_Parsed **rdis = push_array(arena, RDI_Parsed *, rdis_count);
    for(U64 idx = 0; idx < rdis_count; idx += 1)
    {
      rdis[idx] = di_rdi_from_key(rd_state->frame_di_scope, &dbgi_keys.v[idx], endt_us);
    }
    
    //- rjf: query all filtered items from dbgi searching system
    U128 fuzzy_search_key = {d_hash_from_string(str8_struct(&rd_regs()->view)), (U64)section};
    B32 items_stale = 0;
    DI_SearchParams params = {section, dbgi_keys};
    accel->section = section;
    accel->rdis_count = rdis_count;
    accel->rdis = rdis;
    accel->items = di_search_items_from_key_params_query(rd_state->frame_di_scope, fuzzy_search_key, &params, filter, endt_us, &items_stale);
    if(items_stale)
    {
      rd_request_frame();
    }
  }
  E_LookupInfo info = {accel, 0, accel->items.count};
  scratch_end(scratch);
  return info;
}

E_LOOKUP_RANGE_FUNCTION_DEF(debug_info_table)
{
  RD_DebugInfoTableLookupAccel *accel = (RD_DebugInfoTableLookupAccel *)user_data;
  U64 needed_row_count = dim_1u64(idx_range);
  for EachIndex(idx, needed_row_count)
  {
    // rjf: unpack row
    DI_SearchItem *item = &accel->items.v[idx_range.min + idx];
    
    // rjf: skip bad elements
    if(item->dbgi_idx >= accel->rdis_count)
    {
      continue;
    }
    
    // rjf: unpack row info
    RDI_Parsed *rdi = accel->rdis[item->dbgi_idx];
    E_Module *module = &e_parse_state->ctx->modules[item->dbgi_idx];
    
    // rjf: build expr
    E_Expr *item_expr = &e_expr_nil;
    {
      U64 element_idx = item->idx;
      switch(accel->section)
      {
        default:{}break;
        case RDI_SectionKind_Procedures:
        {
          RDI_Procedure *procedure = rdi_element_from_name_idx(module->rdi, Procedures, element_idx);
          RDI_Scope *scope = rdi_element_from_name_idx(module->rdi, Scopes, procedure->root_scope_idx);
          U64 voff = *rdi_element_from_name_idx(module->rdi, ScopeVOffData, scope->voff_range_first);
          E_OpList oplist = {0};
          e_oplist_push_op(arena, &oplist, RDI_EvalOp_ConstU64, e_value_u64(module->vaddr_range.min + voff));
          String8 bytecode = e_bytecode_from_oplist(arena, &oplist);
          U32 type_idx = procedure->type_idx;
          RDI_TypeNode *type_node = rdi_element_from_name_idx(module->rdi, TypeNodes, type_idx);
          E_TypeKey type_key = e_type_key_ext(e_type_kind_from_rdi(type_node->kind), type_idx, (U32)(module - e_parse_state->ctx->modules));
          item_expr = e_push_expr(arena, E_ExprKind_LeafBytecode, 0);
          item_expr->mode     = E_Mode_Value;
          item_expr->space    = module->space;
          item_expr->type_key = type_key;
          item_expr->bytecode = bytecode;
          item_expr->string.str = rdi_string_from_idx(module->rdi, procedure->name_string_idx, &item_expr->string.size);
        }break;
        case RDI_SectionKind_GlobalVariables:
        {
          RDI_GlobalVariable *gvar = rdi_element_from_name_idx(module->rdi, GlobalVariables, element_idx);
          U64 voff = gvar->voff;
          E_OpList oplist = {0};
          e_oplist_push_op(arena, &oplist, RDI_EvalOp_ConstU64, e_value_u64(module->vaddr_range.min + voff));
          String8 bytecode = e_bytecode_from_oplist(arena, &oplist);
          U32 type_idx = gvar->type_idx;
          RDI_TypeNode *type_node = rdi_element_from_name_idx(module->rdi, TypeNodes, type_idx);
          E_TypeKey type_key = e_type_key_ext(e_type_kind_from_rdi(type_node->kind), type_idx, (U32)(module - e_parse_state->ctx->modules));
          item_expr = e_push_expr(arena, E_ExprKind_LeafBytecode, 0);
          item_expr->mode     = E_Mode_Offset;
          item_expr->space    = module->space;
          item_expr->type_key = type_key;
          item_expr->bytecode = bytecode;
          item_expr->string.str = rdi_string_from_idx(module->rdi, gvar->name_string_idx, &item_expr->string.size);
        }break;
        case RDI_SectionKind_ThreadVariables:
        {
          RDI_ThreadVariable *tvar = rdi_element_from_name_idx(module->rdi, ThreadVariables, element_idx);
          E_OpList oplist = {0};
          e_oplist_push_op(arena, &oplist, RDI_EvalOp_TLSOff, e_value_u64(tvar->tls_off));
          String8 bytecode = e_bytecode_from_oplist(arena, &oplist);
          U32 type_idx = tvar->type_idx;
          RDI_TypeNode *type_node = rdi_element_from_name_idx(module->rdi, TypeNodes, type_idx);
          E_TypeKey type_key = e_type_key_ext(e_type_kind_from_rdi(type_node->kind), type_idx, (U32)(module - e_parse_state->ctx->modules));
          item_expr = e_push_expr(arena, E_ExprKind_LeafBytecode, 0);
          item_expr->mode     = E_Mode_Offset;
          item_expr->space    = module->space;
          item_expr->type_key = type_key;
          item_expr->bytecode = bytecode;
          item_expr->string.str = rdi_string_from_idx(module->rdi, tvar->name_string_idx, &item_expr->string.size);
        }break;
        case RDI_SectionKind_UDTs:
        {
          RDI_UDT *udt = rdi_element_from_name_idx(module->rdi, UDTs, element_idx);
          RDI_TypeNode *type_node = rdi_element_from_name_idx(module->rdi, TypeNodes, udt->self_type_idx);
          E_TypeKey type_key = e_type_key_ext(e_type_kind_from_rdi(type_node->kind), udt->self_type_idx, (U32)(module - e_parse_state->ctx->modules));
          item_expr = e_push_expr(arena, E_ExprKind_TypeIdent, 0);
          item_expr->type_key = type_key;
        }break;
      }
    }
    
    // rjf: fill
    exprs[idx] = item_expr;
  }
}

E_LOOKUP_ID_FROM_NUM_FUNCTION_DEF(debug_info_table)
{
  RD_DebugInfoTableLookupAccel *accel = (RD_DebugInfoTableLookupAccel *)user_data;
  U64 id = 0;
  if(0 < num && num <= accel->items.count)
  {
    id = accel->items.v[num-1].idx+1;
  }
  return id;
}

E_LOOKUP_NUM_FROM_ID_FUNCTION_DEF(debug_info_table)
{
  RD_DebugInfoTableLookupAccel *accel = (RD_DebugInfoTableLookupAccel *)user_data;
  U64 num = di_search_item_num_from_array_element_idx__linear_search(&accel->items, id-1);
  return num;
}

internal F32
rd_append_value_strings_from_eval(Arena *arena, EV_StringFlags flags, U32 default_radix, FNT_Tag font, F32 font_size, F32 max_size, S32 depth, E_Eval eval, String8List *out)
{
  ProfBeginFunction();
  Temp scratch = scratch_begin(&arena, 1);
  F32 space_taken = 0;
  
  //- rjf: unpack view rules
  U32 radix = default_radix;
  U32 min_digits = 0;
  B32 no_addr = 0;
  B32 no_string = 0;
  B32 has_array = 0;
  for(E_Expr *tag = eval.expr->first_tag; tag != &e_expr_nil; tag = tag->next)
  {
    if(0){}
    else if(str8_match(tag->string, str8_lit("dec"), 0)) {radix = 10;}
    else if(str8_match(tag->string, str8_lit("hex"), 0)) {radix = 16;}
    else if(str8_match(tag->string, str8_lit("bin"), 0)) {radix = 2; }
    else if(str8_match(tag->string, str8_lit("oct"), 0)) {radix = 8; }
    else if(str8_match(tag->string, str8_lit("no_addr"), 0)) {no_addr = 1;}
    else if(str8_match(tag->string, str8_lit("no_string"), 0)) {no_string = 1;}
    else if(str8_match(tag->string, str8_lit("array"), 0)) {has_array = 1;}
    else if(str8_match(tag->string, str8_lit("digits"), 0))
    {
      E_Expr *num_expr = tag->first->next;
      E_Eval num_eval = e_eval_from_expr(scratch.arena, num_expr);
      E_Eval num_value_eval = e_value_eval_from_eval(num_eval);
      min_digits = (U32)num_value_eval.value.u64;
    }
  }
  if(eval.space.kind == RD_EvalSpaceKind_MetaCtrlEntity ||
     eval.space.kind == RD_EvalSpaceKind_MetaCfg)
  {
    E_TypeKind kind = e_type_kind_from_key(eval.type_key);
    if(kind != E_TypeKind_Ptr)
    {
      no_addr = 1;
    }
    else
    {
      E_Type *type = e_type_from_key__cached(eval.type_key);
      if(!(type->flags & E_TypeFlag_External))
      {
        no_addr = 1;
      }
    }
  }
  
  //- rjf: type evaluations -> display type string
  if(eval.mode == E_Mode_Null && !e_type_key_match(e_type_key_zero(), eval.type_key))
  {
    String8 string = e_type_string_from_key(arena, eval.type_key);
    str8_list_push(arena, out, string);
  }
  
  //- rjf: value/offset evaluations
  else if(max_size > 0)
  {
    E_TypeKey type_key = e_type_unwrap(eval.type_key);
    E_TypeKind kind = e_type_kind_from_key(type_key);
    switch(kind)
    {
      //- rjf: default - leaf cases
      default:
      {
        E_Eval value_eval = e_value_eval_from_eval(eval);
        String8 string = ev_string_from_simple_typed_eval(arena, flags, radix, min_digits, value_eval);
        space_taken += fnt_dim_from_tag_size_string(font, font_size, 0, 0, string).x;
        str8_list_push(arena, out, string);
      }break;
      
      //- rjf: pointers
      case E_TypeKind_Function:
      case E_TypeKind_Ptr:
      case E_TypeKind_LRef:
      case E_TypeKind_RRef:
      {
        // rjf: unpack type info
        E_TypeKind type_kind = e_type_kind_from_key(e_type_unwrap(eval.type_key));
        E_TypeKey direct_type_key = e_type_unwrap(e_type_ptee_from_key(e_type_unwrap(eval.type_key)));
        E_TypeKind direct_type_kind = e_type_kind_from_key(direct_type_key);
        
        // rjf: unpack info about pointer destination
        E_Eval value_eval = e_value_eval_from_eval(eval);
        B32 ptee_has_content = (direct_type_kind != E_TypeKind_Null && direct_type_kind != E_TypeKind_Void);
        B32 ptee_has_string  = ((E_TypeKind_Char8 <= direct_type_kind && direct_type_kind <= E_TypeKind_UChar32) ||
                                direct_type_kind == E_TypeKind_S8 ||
                                direct_type_kind == E_TypeKind_U8);
        E_Space space = value_eval.space;
        CTRL_Entity *entity = rd_ctrl_entity_from_eval_space(space);
        CTRL_Entity *process = ctrl_process_from_entity(entity);
        if(process == &ctrl_entity_nil)
        {
          process = ctrl_entity_from_handle(d_state->ctrl_entity_store, rd_regs()->process);
        }
        String8 symbol_name = d_symbol_name_from_process_vaddr(arena, process, value_eval.value.u64, 1);
        
        // rjf: special case: push strings for textual string content
        B32 did_content = 0;
        B32 did_string = 0;
        if(!did_content && ptee_has_string && !no_string)
        {
          did_content = 1;
          did_string = 1;
          U64 string_memory_addr = value_eval.value.u64;
          U64 element_size = e_type_byte_size_from_key(direct_type_key);
          U64 string_buffer_size = 1024;
          U8 *string_buffer = push_array(arena, U8, string_buffer_size);
          for(U64 try_size = string_buffer_size; try_size >= 16; try_size /= 2)
          {
            B32 read_good = e_space_read(eval.space, string_buffer, r1u64(string_memory_addr, string_memory_addr+try_size));
            if(read_good)
            {
              break;
            }
          }
          string_buffer[string_buffer_size-1] = 0;
          String8 string = {0};
          switch(element_size)
          {
            default:{string = str8_cstring((char *)string_buffer);}break;
            case 2: {string = str8_from_16(arena, str16_cstring((U16 *)string_buffer));}break;
            case 4: {string = str8_from_32(arena, str32_cstring((U32 *)string_buffer));}break;
          }
          String8 string_escaped = ev_escaped_from_raw_string(arena, string);
          space_taken += fnt_dim_from_tag_size_string(font, font_size, 0, 0, string_escaped).x;
          space_taken += 2*fnt_dim_from_tag_size_string(font, font_size, 0, 0, str8_lit("\"")).x;
          if(!no_addr || depth > 0)
          {
            str8_list_push(arena, out, str8_lit("\""));
          }
          str8_list_push(arena, out, string_escaped);
          if(!no_addr || depth > 0)
          {
            str8_list_push(arena, out, str8_lit("\""));
          }
        }
        
        // rjf: special case: push strings for symbols
        if(value_eval.value.u64 != 0 &&
           !did_content && symbol_name.size != 0 &&
           flags & EV_StringFlag_ReadOnlyDisplayRules &&
           ((type_kind == E_TypeKind_Ptr && direct_type_kind == E_TypeKind_Void) ||
            (type_kind == E_TypeKind_Ptr && direct_type_kind == E_TypeKind_Function) ||
            (type_kind == E_TypeKind_Function)))
        {
          did_content = 1;
          str8_list_push(arena, out, symbol_name);
          space_taken += fnt_dim_from_tag_size_string(font, font_size, 0, 0, symbol_name).x;
        }
        
        // rjf: special case: need symbol name, don't have one
        if(value_eval.value.u64 != 0 &&
           !did_content && symbol_name.size == 0 &&
           flags & EV_StringFlag_ReadOnlyDisplayRules &&
           ((type_kind == E_TypeKind_Ptr && direct_type_kind == E_TypeKind_Function) ||
            (type_kind == E_TypeKind_Function)) &&
           (flags & EV_StringFlag_ReadOnlyDisplayRules))
        {
          did_content = 1;
          String8 string = str8_lit("???");
          str8_list_push(arena, out, string);
          space_taken += fnt_dim_from_tag_size_string(font, font_size, 0, 0, string).x;
        }
        
        // rjf: push pointer value
        B32 did_ptr_value = 0;
        if(!no_addr || value_eval.value.u64 == 0)
        {
          did_ptr_value = 1;
          if(did_content)
          {
            String8 left_paren = str8_lit(" (");
            space_taken += fnt_dim_from_tag_size_string(font, font_size, 0, 0, left_paren).x;
            str8_list_push(arena, out, left_paren);
          }
          String8 string = ev_string_from_simple_typed_eval(arena, flags, radix, min_digits, value_eval);
          space_taken += fnt_dim_from_tag_size_string(font, font_size, 0, 0, string).x;
          str8_list_push(arena, out, string);
          if(did_content)
          {
            String8 right_paren = str8_lit(")");
            space_taken += fnt_dim_from_tag_size_string(font, font_size, 0, 0, right_paren).x;
            str8_list_push(arena, out, right_paren);
          }
        }
        
        // rjf: descend for all other cases
        B32 did_arrow = 0;
        if(value_eval.value.u64 != 0 && !did_content && ptee_has_content && (flags & EV_StringFlag_ReadOnlyDisplayRules))
        {
          if(did_ptr_value && !did_arrow)
          {
            did_arrow = 1;
            String8 arrow = str8_lit(" -> ");
            space_taken += fnt_dim_from_tag_size_string(font, font_size, 0, 0, arrow).x;
            str8_list_push(arena, out, arrow);
          }
          did_content = 1;
          if(depth < 4)
          {
            E_Expr *deref_expr = e_expr_ref_deref(scratch.arena, eval.expr);
            E_Eval deref_eval = e_eval_from_expr(scratch.arena, deref_expr);
            space_taken += rd_append_value_strings_from_eval(arena, flags, radix, font, font_size, max_size-space_taken, depth+1, deref_eval, out);
          }
          else
          {
            String8 ellipses = str8_lit("...");
            str8_list_push(arena, out, ellipses);
            space_taken += fnt_dim_from_tag_size_string(font, font_size, 0, 0, ellipses).x;
          }
        }
      }break;
      
      //- rjf: arrays
      case E_TypeKind_Array:
      {
        // rjf: unpack type info
        E_Type *eval_type = e_type_from_key__cached(e_type_unwrap(eval.type_key));
        E_TypeKey direct_type_key = e_type_unwrap(eval_type->direct_type_key);
        E_TypeKind direct_type_kind = e_type_kind_from_key(direct_type_key);
        U64 array_count = eval_type->count;
        
        // rjf: get pointed-at type
        B32 array_is_string = ((E_TypeKind_Char8 <= direct_type_kind && direct_type_kind <= E_TypeKind_UChar32) ||
                               direct_type_kind == E_TypeKind_S8 ||
                               direct_type_kind == E_TypeKind_U8);
        
        // rjf: special case: push strings for textual string content
        B32 did_content = 0;
        if(!did_content && array_is_string && !no_string)
        {
          U64 element_size = e_type_byte_size_from_key(direct_type_key);
          did_content = 1;
          U64 string_buffer_size = Clamp(1, array_count, 1024);
          U8 *string_buffer = push_array(arena, U8, string_buffer_size);
          switch(eval.mode)
          {
            default:{}break;
            case E_Mode_Offset:
            {
              U64 string_memory_addr = eval.value.u64;
              B32 read_good = e_space_read(eval.space, string_buffer, r1u64(string_memory_addr, string_memory_addr+string_buffer_size));
            }break;
            case E_Mode_Value:
            {
              MemoryCopy(string_buffer, &eval.value.u512[0], Min(string_buffer_size, sizeof(eval.value)));
            }break;
          }
          String8 string = {0};
          switch(element_size)
          {
            default:{string = str8_cstring_capped(string_buffer, string_buffer + string_buffer_size);}break;
            case 2: {string = str8_from_16(arena, str16_cstring_capped(string_buffer, string_buffer + string_buffer_size));}break;
            case 4: {string = str8_from_32(arena, str32_cstring((U32 *)string_buffer));}break;
          }
          String8 string_escaped = ev_escaped_from_raw_string(arena, string);
          space_taken += fnt_dim_from_tag_size_string(font, font_size, 0, 0, string_escaped).x;
          space_taken += 2*fnt_dim_from_tag_size_string(font, font_size, 0, 0, str8_lit("\"")).x;
          if(!no_addr || depth > 0)
          {
            str8_list_push(arena, out, str8_lit("\""));
          }
          str8_list_push(arena, out, string_escaped);
          if(!no_addr || depth > 0)
          {
            str8_list_push(arena, out, str8_lit("\""));
          }
        }
        
        // rjf: if we did not do any special content, then go to the regular arrays/structs/sets case
        if(!did_content)
        {
          goto arrays_and_sets_and_structs;
        }
      }break;
      
      //- rjf: non-string-arrays/structs/sets
      case E_TypeKind_Struct:
      case E_TypeKind_Union:
      case E_TypeKind_Class:
      case E_TypeKind_IncompleteStruct:
      case E_TypeKind_IncompleteUnion:
      case E_TypeKind_IncompleteClass:
      case E_TypeKind_Set:
      arrays_and_sets_and_structs:
      {
        String8 opener_string = str8_lit("{");
        String8 closer_string = str8_lit("}");
        if(kind == E_TypeKind_Array)
        {
          opener_string = str8_lit("[");
          closer_string = str8_lit("]");
        }
        
        // rjf: opener ({, [)
        {
          str8_list_push(arena, out, opener_string);
          space_taken += fnt_dim_from_tag_size_string(font, font_size, 0, 0, opener_string).x;
        }
        
        // rjf: build contents
        if(depth < 4)
        {
          E_IRTreeAndType irtree = e_irtree_and_type_from_expr(scratch.arena, eval.expr);
          E_LookupRuleTagPair lookup_rule_and_tag = e_lookup_rule_tag_pair_from_expr_irtree(eval.expr, &irtree);
          E_LookupRule *lookup_rule = lookup_rule_and_tag.rule;
          E_Expr *lookup_rule_tag = lookup_rule_and_tag.tag;
          E_LookupInfo lookup_info = lookup_rule->info(arena, &irtree, str8_zero());
          U64 total_possible_child_count = Max(lookup_info.idxed_expr_count, lookup_info.named_expr_count);
          B32 is_first = 1;
          for(U64 idx = 0; idx < total_possible_child_count && max_size > space_taken; idx += 1)
          {
            E_Expr *expr = &e_expr_nil;
            String8 expr_string = {0};
            lookup_rule->range(scratch.arena, eval.expr, r1u64(idx, idx+1), &expr, &expr_string, lookup_info.user_data);
            if(expr != &e_expr_nil)
            {
              if(!is_first)
              {
                String8 comma = str8_lit(", ");
                space_taken += fnt_dim_from_tag_size_string(font, font_size, 0, 0, comma).x;
                str8_list_push(arena, out, comma);
              }
              is_first = 0;
              E_Eval child_eval = e_eval_from_expr(scratch.arena, expr);
              space_taken += rd_append_value_strings_from_eval(arena, flags, radix, font, font_size, max_size-space_taken, depth+1, child_eval, out);
              if(space_taken > max_size && idx+1 < total_possible_child_count)
              {
                String8 ellipses = str8_lit(", ...");
                space_taken += fnt_dim_from_tag_size_string(font, font_size, 0, 0, ellipses).x;
                str8_list_push(arena, out, ellipses);
              }
            }
          }
        }
        else
        {
          String8 ellipses = str8_lit("...");
          str8_list_push(arena, out, ellipses);
          space_taken += fnt_dim_from_tag_size_string(font, font_size, 0, 0, ellipses).x;
        }
        
        // rjf: closer (}, ])
        {
          str8_list_push(arena, out, closer_string);
          space_taken += fnt_dim_from_tag_size_string(font, font_size, 0, 0, closer_string).x;
        }
      }break;
    }
  }
  
  scratch_end(scratch);
  ProfEnd();
  return space_taken;
}

internal String8
rd_value_string_from_eval(Arena *arena, EV_StringFlags flags, U32 default_radix, FNT_Tag font, F32 font_size, F32 max_size, E_Eval eval)
{
  Temp scratch = scratch_begin(&arena, 1);
  String8List strs = {0};
  rd_append_value_strings_from_eval(scratch.arena, flags, default_radix, font, font_size, max_size, 0, eval, &strs);
  String8 result = str8_list_join(arena, &strs, 0);
  scratch_end(scratch);
  return result;
}

////////////////////////////////
//~ rjf: Hover Eval

internal void
rd_set_hover_eval(Vec2F32 pos, String8 file_path, TxtPt pt, U64 vaddr, String8 string)
{
  RD_Cfg *window_cfg = rd_cfg_from_handle(rd_regs()->window);
  RD_WindowState *ws = rd_window_state_from_cfg(window_cfg);
  if(ws->hover_eval_last_frame_idx+1 < rd_state->frame_index &&
     ui_key_match(ui_active_key(UI_MouseButtonKind_Left), ui_key_zero()) &&
     ui_key_match(ui_active_key(UI_MouseButtonKind_Middle), ui_key_zero()) &&
     ui_key_match(ui_active_key(UI_MouseButtonKind_Right), ui_key_zero()))
  {
    B32 is_new_string = !str8_match(ws->hover_eval_string, string, 0);
    if(is_new_string)
    {
      ws->hover_eval_first_frame_idx = ws->hover_eval_last_frame_idx = rd_state->frame_index;
      arena_clear(ws->hover_eval_arena);
      ws->hover_eval_string = push_str8_copy(ws->hover_eval_arena, string);
      ws->hover_eval_file_path = push_str8_copy(ws->hover_eval_arena, file_path);
      ws->hover_eval_file_pt = pt;
      ws->hover_eval_vaddr = vaddr;
      ws->hover_eval_focused = 0;
    }
    ws->hover_eval_spawn_pos = pos;
    ws->hover_eval_last_frame_idx = rd_state->frame_index;
  }
}

////////////////////////////////
//~ rjf: Lister Functions

internal void
rd_lister_item_chunk_list_push(Arena *arena, RD_ListerItemChunkList *list, U64 cap, RD_ListerItem *item)
{
  RD_ListerItemChunkNode *n = list->last;
  if(n == 0 || n->count >= n->cap)
  {
    n = push_array(arena, RD_ListerItemChunkNode, 1);
    SLLQueuePush(list->first, list->last, n);
    n->cap = cap;
    n->v = push_array_no_zero(arena, RD_ListerItem, n->cap);
    list->chunk_count += 1;
  }
  MemoryCopyStruct(&n->v[n->count], item);
  n->count += 1;
  list->total_count += 1;
}

internal RD_ListerItemArray
rd_lister_item_array_from_chunk_list(Arena *arena, RD_ListerItemChunkList *list)
{
  RD_ListerItemArray array = {0};
  array.count = list->total_count;
  array.v = push_array_no_zero(arena, RD_ListerItem, array.count);
  U64 idx = 0;
  for(RD_ListerItemChunkNode *n = list->first; n != 0; n = n->next)
  {
    MemoryCopy(array.v+idx, n->v, sizeof(RD_ListerItem)*n->count);
    idx += n->count;
  }
  return array;
}

internal int
rd_lister_item_qsort_compare(RD_ListerItem *a, RD_ListerItem *b)
{
  int result = 0;
  if(a->group < b->group)
  {
    result = -1;
  }
  else if(a->group > b->group)
  {
    result = +1;
  }
  else if(a->display_name__matches.count > b->display_name__matches.count)
  {
    result = -1;
  }
  else if(a->display_name__matches.count < b->display_name__matches.count)
  {
    result = +1;
  }
  else if(a->description__matches.count > b->description__matches.count)
  {
    result = -1;
  }
  else if(a->description__matches.count < b->description__matches.count)
  {
    result = +1;
  }
  else if(a->kind_name__matches.count > b->kind_name__matches.count)
  {
    result = -1;
  }
  else if(a->kind_name__matches.count < b->kind_name__matches.count)
  {
    result = +1;
  }
  else if(a->string.size < b->string.size)
  {
    result = -1;
  }
  else if(a->string.size > b->string.size)
  {
    result = +1;
  }
  else
  {
    result = strncmp((char *)a->string.str, (char *)b->string.str, Min(a->string.size, b->string.size));
  }
  return result;
}

internal void
rd_lister_item_array_sort__in_place(RD_ListerItemArray *array)
{
  quick_sort(array->v, array->count, sizeof(array->v[0]), rd_lister_item_qsort_compare);
}

internal RD_ListerItemArray
rd_lister_item_array_from_regs_needle_cursor_off(Arena *arena, RD_Regs *regs, String8 needle, U64 cursor_off)
{
  Temp scratch = scratch_begin(&arena, 1);
  DI_Scope *di_scope = di_scope_open();
  RD_ListerFlags flags = regs->lister_flags;
  String8 needle_path = rd_lister_query_path_from_input_string_off(needle, cursor_off);
  RD_ListerItemChunkList item_list = {0};
  DI_KeyList dbgi_keys_list = d_push_active_dbgi_key_list(scratch.arena);
  DI_KeyArray dbgi_keys = di_key_array_from_list(scratch.arena, &dbgi_keys_list);
  
  //////////////////////////
  //- rjf: determine all ctx filters
  //
  String8List ctx_filter_strings = {0};
  {
    switch(regs->reg_slot)
    {
      default:{}break;
      case RD_RegSlot_Cursor:
      {
        if(!txt_pt_match(regs->cursor, regs->mark))
        {
          str8_list_pushf(scratch.arena, &ctx_filter_strings, "$text_rng,");
        }
        else
        {
          str8_list_pushf(scratch.arena, &ctx_filter_strings, "$text_pt,");
        }
      }break;
      case RD_RegSlot_Cfg:
      {
        RD_Cfg *cfg = rd_cfg_from_handle(regs->cfg);
        str8_list_pushf(scratch.arena, &ctx_filter_strings, "$%S,", cfg->string);
      }break;
      case RD_RegSlot_View:
      {
        RD_Cfg *view = rd_cfg_from_handle(regs->view);
        str8_list_pushf(scratch.arena, &ctx_filter_strings, "$tab,");
        str8_list_pushf(scratch.arena, &ctx_filter_strings, "$%S,", view->string);
        String8 view_expr = rd_expr_from_cfg(view);
        String8 view_file_path = rd_file_path_from_eval_string(scratch.arena, view_expr);
        if(view_file_path.size != 0)
        {
          str8_list_pushf(scratch.arena, &ctx_filter_strings, "$file,");
        }
      } // fallthrough;
      case RD_RegSlot_Panel:
      {
        str8_list_pushf(scratch.arena, &ctx_filter_strings, "$panel,");
      }break;
    }
  }
  
  //////////////////////////
  //- rjf: grab rdis
  //
  U64 rdis_count = dbgi_keys.count;
  RDI_Parsed **rdis = push_array(scratch.arena, RDI_Parsed *, rdis_count);
  {
    for(U64 idx = 0; idx < rdis_count; idx += 1)
    {
      RDI_Parsed *rdi = di_rdi_from_key(di_scope, &dbgi_keys.v[idx], 0);
      RDI_TopLevelInfo *tli = rdi_element_from_name_idx(rdi, TopLevelInfo, 0);
      rdis[idx] = rdi;
    }
  }
  
  //- rjf: gather locals
  if(flags & RD_ListerFlag_Locals)
  {
    CTRL_Entity *thread = ctrl_entity_from_handle(d_state->ctrl_entity_store, rd_regs()->thread);
    U64 thread_rip_vaddr = d_query_cached_rip_from_thread_unwind(thread, rd_regs()->unwind_count);
    CTRL_Entity *process = ctrl_entity_ancestor_from_kind(thread, CTRL_EntityKind_Process);
    CTRL_Entity *module = ctrl_module_from_process_vaddr(process, thread_rip_vaddr);
    U64 thread_rip_voff = ctrl_voff_from_vaddr(module, thread_rip_vaddr);
    DI_Key dbgi_key = ctrl_dbgi_key_from_module(module);
    E_String2NumMap *locals_map = d_query_cached_locals_map_from_dbgi_key_voff(&dbgi_key, thread_rip_voff);
    E_String2NumMap *member_map = d_query_cached_member_map_from_dbgi_key_voff(&dbgi_key, thread_rip_voff);
    for(E_String2NumMapNode *n = locals_map->first; n != 0; n = n->order_next)
    {
      FuzzyMatchRangeList display_name__matches = fuzzy_match_find(arena, needle, n->string);
      if(display_name__matches.count == display_name__matches.needle_part_count)
      {
        rd_lister_item_chunk_list_push_new(scratch.arena, &item_list, 256,
                                           .string                = n->string,
                                           .kind_name             = str8_lit("Local"),
                                           .display_name          = n->string,
                                           .display_name__matches = display_name__matches);
      }
    }
    for(E_String2NumMapNode *n = member_map->first; n != 0; n = n->order_next)
    {
      FuzzyMatchRangeList display_name__matches = fuzzy_match_find(arena, needle, n->string);
      if(display_name__matches.count == display_name__matches.needle_part_count)
      {
        rd_lister_item_chunk_list_push_new(scratch.arena, &item_list, 256,
                                           .string                = n->string,
                                           .kind_name             = str8_lit("Local (Member)"),
                                           .display_name          = n->string,
                                           .display_name__matches = display_name__matches);
      }
    }
  }
  
  //- rjf: gather registers
  if(flags & RD_ListerFlag_Registers)
  {
    CTRL_Entity *thread = ctrl_entity_from_handle(d_state->ctrl_entity_store, rd_regs()->thread);
    Arch arch = thread->arch;
    U64 reg_names_count = regs_reg_code_count_from_arch(arch);
    U64 alias_names_count = regs_alias_code_count_from_arch(arch);
    String8 *reg_names = regs_reg_code_string_table_from_arch(arch);
    String8 *alias_names = regs_alias_code_string_table_from_arch(arch);
    for(U64 idx = 0; idx < reg_names_count; idx += 1)
    {
      if(reg_names[idx].size != 0)
      {
        String8 display_name = reg_names[idx];
        FuzzyMatchRangeList display_name__matches = fuzzy_match_find(arena, needle, display_name);
        if(display_name__matches.count == display_name__matches.needle_part_count)
        {
          rd_lister_item_chunk_list_push_new(scratch.arena, &item_list, 256,
                                             .string                = display_name,
                                             .kind_name             = str8_lit("Register"),
                                             .display_name          = display_name,
                                             .display_name__matches = display_name__matches);
        }
      }
    }
    for(U64 idx = 0; idx < alias_names_count; idx += 1)
    {
      if(alias_names[idx].size != 0)
      {
        String8 display_name = alias_names[idx];
        FuzzyMatchRangeList display_name__matches = fuzzy_match_find(arena, needle, display_name);
        if(display_name__matches.count == display_name__matches.needle_part_count)
        {
          rd_lister_item_chunk_list_push_new(scratch.arena, &item_list, 256,
                                             .string                = display_name,
                                             .kind_name             = str8_lit("Reg. Alias"),
                                             .display_name          = display_name,
                                             .display_name__matches = display_name__matches);
        }
      }
    }
  }
  
  //- rjf: gather view rules
  if(flags & RD_ListerFlag_ViewRules)
  {
    for(U64 slot_idx = 0; slot_idx < d_state->view_rule_spec_table_size; slot_idx += 1)
    {
      for(D_ViewRuleSpec *spec = d_state->view_rule_spec_table[slot_idx]; spec != 0 && spec != &d_nil_core_view_rule_spec; spec = spec->hash_next)
      {
        String8 display_name = spec->info.string;
        FuzzyMatchRangeList display_name__matches = fuzzy_match_find(arena, needle, display_name);
        String8 description = spec->info.description;
        FuzzyMatchRangeList description__matches = fuzzy_match_find(arena, needle, description);
        if(display_name__matches.count == display_name__matches.needle_part_count ||
           description__matches.count == description__matches.needle_part_count)
        {
          rd_lister_item_chunk_list_push_new(scratch.arena, &item_list, 256,
                                             .string                = display_name,
                                             .kind_name             = str8_lit("View Rule"),
                                             .display_name          = display_name,
                                             .display_name__matches = display_name__matches,
                                             .description           = description,
                                             .description__matches  = description__matches);
        }
      }
    }
  }
  
  //- rjf: gather members
  if(flags & RD_ListerFlag_Members)
  {
    // TODO(rjf)
  }
  
  //- rjf: gather globals
  if(flags & RD_ListerFlag_Globals && needle.size != 0)
  {
    U128 search_key = {d_hash_from_string(str8_lit("autocomp_globals_search_key")), d_hash_from_string(str8_lit("autocomp_globals_search_key"))};
    DI_SearchParams search_params =
    {
      RDI_SectionKind_GlobalVariables,
      dbgi_keys,
    };
    B32 is_stale = 0;
    DI_SearchItemArray items = di_search_items_from_key_params_query(di_scope, search_key, &search_params, needle, 0, &is_stale);
    for(U64 idx = 0; idx < 20 && idx < items.count; idx += 1)
    {
      // rjf: skip bad elements
      if(items.v[idx].dbgi_idx >= rdis_count)
      {
        continue;
      }
      
      // rjf: unpack info
      RDI_Parsed *rdi = rdis[items.v[idx].dbgi_idx];
      String8 display_name = di_search_item_string_from_rdi_target_element_idx(rdi, search_params.target, items.v[idx].idx);
      FuzzyMatchRangeList display_name__matches = items.v[idx].match_ranges;
      
      // rjf: push item
      rd_lister_item_chunk_list_push_new(scratch.arena, &item_list, 256,
                                         .string                = display_name,
                                         .kind_name             = str8_lit("Global"),
                                         .display_name          = display_name,
                                         .display_name__matches = display_name__matches,
                                         .group                 = 1);
    }
  }
  
  //- rjf: gather thread locals
  if(flags & RD_ListerFlag_ThreadLocals && needle.size != 0)
  {
    U128 search_key = {d_hash_from_string(str8_lit("autocomp_tvars_dis_key")), d_hash_from_string(str8_lit("autocomp_tvars_dis_key"))};
    DI_SearchParams search_params =
    {
      RDI_SectionKind_ThreadVariables,
      dbgi_keys,
    };
    B32 is_stale = 0;
    DI_SearchItemArray items = di_search_items_from_key_params_query(di_scope, search_key, &search_params, needle, 0, &is_stale);
    for(U64 idx = 0; idx < 20 && idx < items.count; idx += 1)
    {
      // rjf: skip bad elements
      if(items.v[idx].dbgi_idx >= rdis_count)
      {
        continue;
      }
      
      // rjf: unpack info
      RDI_Parsed *rdi = rdis[items.v[idx].dbgi_idx];
      String8 display_name = di_search_item_string_from_rdi_target_element_idx(rdi, search_params.target, items.v[idx].idx);
      FuzzyMatchRangeList display_name__matches = items.v[idx].match_ranges;
      
      // rjf: push item
      rd_lister_item_chunk_list_push_new(scratch.arena, &item_list, 256,
                                         .string                = display_name,
                                         .kind_name             = str8_lit("Thread Local"),
                                         .display_name          = display_name,
                                         .display_name__matches = display_name__matches,
                                         .group                 = 1);
    }
  }
  
  //- rjf: gather procedures
  if(flags & RD_ListerFlag_Procedures && needle.size != 0)
  {
    U128 search_key = {d_hash_from_string(str8_lit("autocomp_procedures_search_key")), d_hash_from_string(str8_lit("autocomp_procedures_search_key"))};
    DI_SearchParams search_params =
    {
      RDI_SectionKind_Procedures,
      dbgi_keys,
    };
    B32 is_stale = 0;
    DI_SearchItemArray items = di_search_items_from_key_params_query(di_scope, search_key, &search_params, needle, 0, &is_stale);
    for(U64 idx = 0; idx < 20 && idx < items.count; idx += 1)
    {
      // rjf: skip bad elements
      if(items.v[idx].dbgi_idx >= rdis_count)
      {
        continue;
      }
      
      // rjf: unpack info
      RDI_Parsed *rdi = rdis[items.v[idx].dbgi_idx];
      String8 display_name = di_search_item_string_from_rdi_target_element_idx(rdi, search_params.target, items.v[idx].idx);
      FuzzyMatchRangeList display_name__matches = items.v[idx].match_ranges;
      
      // rjf: push item
      rd_lister_item_chunk_list_push_new(scratch.arena, &item_list, 256,
                                         .string                = display_name,
                                         .kind_name             = str8_lit("Procedure"),
                                         .display_name          = display_name,
                                         .display_name__matches = display_name__matches,
                                         .group                 = 1);
    }
  }
  
  //- rjf: gather types
  if(flags & RD_ListerFlag_Types && needle.size != 0)
  {
    U128 search_key = {d_hash_from_string(str8_lit("autocomp_types_search_key")), d_hash_from_string(str8_lit("autocomp_types_search_key"))};
    DI_SearchParams search_params =
    {
      RDI_SectionKind_UDTs,
      dbgi_keys,
    };
    B32 is_stale = 0;
    DI_SearchItemArray items = di_search_items_from_key_params_query(di_scope, search_key, &search_params, needle, 0, &is_stale);
    for(U64 idx = 0; idx < 20 && idx < items.count; idx += 1)
    {
      // rjf: skip bad elements
      if(items.v[idx].dbgi_idx >= rdis_count)
      {
        continue;
      }
      
      // rjf: unpack info
      RDI_Parsed *rdi = rdis[items.v[idx].dbgi_idx];
      String8 display_name = di_search_item_string_from_rdi_target_element_idx(rdi, search_params.target, items.v[idx].idx);
      FuzzyMatchRangeList display_name__matches = items.v[idx].match_ranges;
      
      // rjf: push item
      rd_lister_item_chunk_list_push_new(scratch.arena, &item_list, 256,
                                         .string                = display_name,
                                         .kind_name             = str8_lit("Type"),
                                         .display_name          = display_name,
                                         .display_name__matches = display_name__matches,
                                         .group                 = 1);
    }
  }
  
  //- rjf: gather languages
  if(flags & RD_ListerFlag_Languages)
  {
    for EachNonZeroEnumVal(TXT_LangKind, lang)
    {
      String8 display_name = txt_extension_from_lang_kind(lang);
      if(display_name.size != 0)
      {
        FuzzyMatchRangeList display_name__matches = fuzzy_match_find(arena, needle, display_name);
        if(display_name__matches.count == display_name__matches.needle_part_count)
        {
          rd_lister_item_chunk_list_push_new(scratch.arena, &item_list, 256,
                                             .string                = display_name,
                                             .kind_name             = str8_lit("Language"),
                                             .display_name          = display_name,
                                             .display_name__matches = display_name__matches);
        }
      }
    }
  }
  
  //- rjf: gather architectures
  if(flags & RD_ListerFlag_Architectures)
  {
    for EachNonZeroEnumVal(Arch, arch)
    {
      String8 display_name = string_from_arch(arch);
      FuzzyMatchRangeList display_name__matches = fuzzy_match_find(arena, needle, display_name);
      if(display_name__matches.count == display_name__matches.needle_part_count)
      {
        rd_lister_item_chunk_list_push_new(scratch.arena, &item_list, 256,
                                           .string                = display_name,
                                           .kind_name             = str8_lit("Architecture"),
                                           .display_name          = display_name,
                                           .display_name__matches = display_name__matches);
      }
    }
  }
  
  //- rjf: gather tex2dformats
  if(flags & RD_ListerFlag_Tex2DFormats)
  {
    for EachEnumVal(R_Tex2DFormat, fmt)
    {
      String8 display_name = lower_from_str8(scratch.arena, r_tex2d_format_display_string_table[fmt]);
      FuzzyMatchRangeList display_name__matches = fuzzy_match_find(arena, needle, display_name);
      if(display_name__matches.count == display_name__matches.needle_part_count)
      {
        rd_lister_item_chunk_list_push_new(scratch.arena, &item_list, 256,
                                           .string                = display_name,
                                           .kind_name             = str8_lit("2D Texture Format"),
                                           .display_name          = display_name,
                                           .display_name__matches = display_name__matches);
      }
    }
  }
  
  //- rjf: gather view rule params
#if 0 // TODO(rjf): @cfg
  if(flags & RD_ListerFlag_ViewRuleParams)
  {
    for(String8Node *n = strings.first; n != 0; n = n->next)
    {
      String8 display_name = n->string;
      FuzzyMatchRangeList display_name__matches = fuzzy_match_find(arena, needle, display_name);
      if(display_name__matches.count == display_name__matches.needle_part_count)
      {
        rd_lister_item_chunk_list_push_new(scratch.arena, &item_list, 256,
                                           .string                = display_name,
                                           .kind_name             = str8_lit("View Rule Parameter"),
                                           .display_name          = display_name,
                                           .display_name__matches = display_name__matches);
      }
    }
  }
#endif
  
  //- rjf: gather files
  if(flags & RD_ListerFlag_Files)
  {
    // rjf: find containing directory in needle_path
    String8 dir_str_in_input = {0};
    for(U64 i = 0; i < needle_path.size; i += 1)
    {
      String8 substr1 = str8_substr(needle_path, r1u64(i, i+1));
      String8 substr2 = str8_substr(needle_path, r1u64(i, i+2));
      String8 substr3 = str8_substr(needle_path, r1u64(i, i+3));
      if(str8_match(substr1, str8_lit("/"), StringMatchFlag_SlashInsensitive))
      {
        dir_str_in_input = str8_substr(needle_path, r1u64(i, needle_path.size));
      }
      else if(i != 0 && str8_match(substr2, str8_lit(":/"), StringMatchFlag_SlashInsensitive))
      {
        dir_str_in_input = str8_substr(needle_path, r1u64(i-1, needle_path.size));
      }
      else if(str8_match(substr2, str8_lit("./"), StringMatchFlag_SlashInsensitive))
      {
        dir_str_in_input = str8_substr(needle_path, r1u64(i, needle_path.size));
      }
      else if(str8_match(substr3, str8_lit("../"), StringMatchFlag_SlashInsensitive))
      {
        dir_str_in_input = str8_substr(needle_path, r1u64(i, needle_path.size));
      }
      if(dir_str_in_input.size != 0)
      {
        break;
      }
    }
    
    // rjf: use needle_path string to form various parts of search space
    String8 prefix = {0};
    String8 path = {0};
    String8 search = {0};
    if(dir_str_in_input.size != 0)
    {
      String8 dir = dir_str_in_input;
      U64 one_past_last_slash = dir.size;
      for(U64 i = 0; i < dir_str_in_input.size; i += 1)
      {
        if(dir_str_in_input.str[i] == '/' || dir_str_in_input.str[i] == '\\')
        {
          one_past_last_slash = i+1;
        }
      }
      dir.size = one_past_last_slash;
      path = dir;
      search = str8_substr(dir_str_in_input, r1u64(one_past_last_slash, dir_str_in_input.size));
      prefix = str8_substr(needle_path, r1u64(0, path.str - needle_path.str));
    }
    
    // rjf: get current files, filtered
    if(dir_str_in_input.size != 0)
    {
      B32 allow_dirs = 1;
      OS_FileIter *it = os_file_iter_begin(scratch.arena, path, 0);
      for(OS_FileInfo info = {0}; os_file_iter_next(scratch.arena, it, &info);)
      {
        FuzzyMatchRangeList match_ranges = fuzzy_match_find(arena, search, info.name);
        B32 fits_search = (match_ranges.count == match_ranges.needle_part_count);
        B32 fits_dir_only = (allow_dirs || !(info.props.flags & FilePropertyFlag_IsFolder));
        if(fits_search && fits_dir_only)
        {
          rd_lister_item_chunk_list_push_new(scratch.arena, &item_list, 256,
                                             .string                = info.name,
                                             .kind_name             = info.props.flags & FilePropertyFlag_IsFolder ? str8_lit("Folder") : str8_lit("File"),
                                             .display_name          = info.name,
                                             .icon_kind             = info.props.flags & FilePropertyFlag_IsFolder ? RD_IconKind_FolderClosedFilled : RD_IconKind_FileOutline,
                                             .display_name__matches = match_ranges,
                                             .flags                 = RD_ListerItemFlag_IsNonCode);
        }
      }
      os_file_iter_end(it);
    }
  }
  
  //- rjf: gather commands
  if(flags & RD_ListerFlag_Commands)
  {
    for EachNonZeroEnumVal(RD_CmdKind, k)
    {
      RD_CmdKindInfo *info = &rd_cmd_kind_info_table[k];
      B32 included_by_filter_string = 0;
      for(String8Node *n = ctx_filter_strings.first; n != 0; n = n->next)
      {
        B32 n_match = (str8_find_needle(info->ctx_filter, 0, n->string, 0) < info->ctx_filter.size);
        if(n_match)
        {
          included_by_filter_string = 1;
          break;
        }
      }
      if(ctx_filter_strings.node_count == 0 || included_by_filter_string)
      {
        String8 cmd_display_name = info->display_name;
        String8 cmd_desc = info->description;
        String8 cmd_tags = info->search_tags;
        FuzzyMatchRangeList name_matches = fuzzy_match_find(arena, needle, cmd_display_name);
        FuzzyMatchRangeList desc_matches = fuzzy_match_find(arena, needle, cmd_desc);
        FuzzyMatchRangeList tags_matches = fuzzy_match_find(arena, needle, cmd_tags);
        if(name_matches.count == name_matches.needle_part_count ||
           desc_matches.count == name_matches.needle_part_count ||
           tags_matches.count > 0 ||
           name_matches.needle_part_count == 0)
        {
          rd_lister_item_chunk_list_push_new(scratch.arena, &item_list, 256,
                                             .icon_kind             = info->icon_kind,
                                             .string                = info->string,
                                             .kind_name             = str8_lit("Command"),
                                             .display_name          = cmd_display_name,
                                             .display_name__matches = name_matches,
                                             .description           = cmd_desc,
                                             .description__matches  = desc_matches,
                                             .flags                 = RD_ListerItemFlag_IsNonCode|RD_ListerItemFlag_Bindings);
        }
      }
    }
  }
  
  //- rjf: gather settings
  if(flags & RD_ListerFlag_Settings)
  {
    String8List schema_strings = {0};
    
    // rjf: push schema for view
    {
      RD_Cfg *view = rd_cfg_from_handle(regs->view);
      String8 view_name = view->string;
      RD_ViewRuleInfo *view_rule_info = rd_view_rule_info_from_string(view_name);
      str8_list_push(scratch.arena, &schema_strings, view_rule_info->params_schema);
    }
    
    // rjf: for each schema, gather parameters
    for(String8Node *n = schema_strings.first; n != 0; n = n->next)
    {
      MD_Node *schema = md_tree_from_string(scratch.arena, n->string)->first;
      for MD_EachNode(param, schema->first)
      {
        RD_VocabularyInfo *param_vocab_info = rd_vocabulary_info_from_code_name(param->string);
        String8 name = param_vocab_info->display_name;
        RD_IconKind icon_kind = param_vocab_info->icon_kind;
        FuzzyMatchRangeList name_matches = fuzzy_match_find(arena, needle, name);
        if(name_matches.count == name_matches.needle_part_count)
        {
          rd_lister_item_chunk_list_push_new(scratch.arena, &item_list, 256,
                                             .icon_kind             = icon_kind,
                                             .string                = param->string,
                                             .kind_name             = str8_lit("Setting"),
                                             .display_name          = name,
                                             .display_name__matches = name_matches,
                                             .flags                 = RD_ListerItemFlag_IsNonCode);
        }
      }
    }
  }
  
  //- rjf: gather system processes
  if(flags & RD_ListerFlag_SystemProcesses)
  {
    U32 this_process_pid = os_get_process_info()->pid;
    DMN_ProcessIter iter = {0};
    dmn_process_iter_begin(&iter);
    for(DMN_ProcessInfo info = {0}; dmn_process_iter_next(scratch.arena, &iter, &info);)
    {
      if(info.pid == this_process_pid)
      {
        continue;
      }
      String8 name = push_str8f(scratch.arena, "%S (PID: %i)", info.name, info.pid);
      FuzzyMatchRangeList name_matches = fuzzy_match_find(arena, needle, name);
      if(name_matches.count == name_matches.needle_part_count)
      {
        rd_lister_item_chunk_list_push_new(scratch.arena, &item_list, 256,
                                           .icon_kind             = RD_IconKind_Threads,
                                           .string                = name,
                                           .kind_name             = str8_lit("System Process"),
                                           .display_name          = name,
                                           .display_name__matches = name_matches,
                                           .flags                 = RD_ListerItemFlag_IsNonCode);
      }
    }
    dmn_process_iter_end(&iter);
  }
  
  //- rjf: lister item list -> sorted array
  RD_ListerItemArray item_array = rd_lister_item_array_from_chunk_list(arena, &item_list);
  rd_lister_item_array_sort__in_place(&item_array);
  
  di_scope_close(di_scope);
  scratch_end(scratch);
  return item_array;
}

internal U64
rd_hash_from_lister_item(RD_ListerItem *item)
{
  U64 item_hash = 5381;
  item_hash = d_hash_from_seed_string(item_hash, item->string);
  item_hash = d_hash_from_seed_string(item_hash, item->kind_name);
  item_hash = d_hash_from_seed_string(item_hash, str8_struct(&item->group));
  return item_hash;
}

internal String8
rd_lister_query_word_from_input_string_off(String8 input, U64 cursor_off)
{
  U64 word_start_off = 0;
  for(U64 off = 0; off < input.size && off < cursor_off; off += 1)
  {
    if(!char_is_alpha(input.str[off]) && !char_is_digit(input.str[off], 10) && input.str[off] != '_')
    {
      word_start_off = off+1;
    }
  }
  String8 query = str8_skip(str8_prefix(input, cursor_off), word_start_off);
  return query;
}

internal String8
rd_lister_query_path_from_input_string_off(String8 input, U64 cursor_off)
{
  // rjf: find start of path
  U64 path_start_off = 0;
  {
    B32 single_quoted = 0;
    B32 double_quoted = 0;
    for(U64 off = 0; off < input.size && off < cursor_off; off += 1)
    {
      if(input.str[off] == '\'')
      {
        single_quoted ^= 1;
      }
      if(input.str[off] == '\"')
      {
        double_quoted ^= 1;
      }
      if(char_is_space(input.str[off]) && !single_quoted && !double_quoted)
      {
        path_start_off = off+1;
      }
    }
  }
  
  // rjf: form path
  String8 path = str8_skip(str8_prefix(input, cursor_off), path_start_off);
  if(path.size >= 1 && path.str[0] == '"')  { path = str8_skip(path, 1); }
  if(path.size >= 1 && path.str[0] == '\'') { path = str8_skip(path, 1); }
  if(path.size >= 1 && path.str[path.size-1] == '"')  { path = str8_chop(path, 1); }
  if(path.size >= 1 && path.str[path.size-1] == '\'') { path = str8_chop(path, 1); }
  return path;
}

#if 0 // TODO(rjf): @cfg_lister

internal RD_ListerParams
rd_view_rule_lister_params_from_input_cursor(Arena *arena, String8 string, U64 cursor_off)
{
  RD_ListerParams params = {0};
  {
    Temp scratch = scratch_begin(&arena, 1);
    
    //- rjf: do partial parse of input
    MD_TokenizeResult input_tokenize = md_tokenize_from_text(scratch.arena, string);
    
    //- rjf: find descension steps to cursor
    typedef struct DescendStep DescendStep;
    struct DescendStep
    {
      DescendStep *next;
      DescendStep *prev;
      String8 string;
    };
    DescendStep *first_step = 0;
    DescendStep *last_step = 0;
    DescendStep *free_step = 0;
    S32 paren_nest = 0;
    S32 colon_nest = 0;
    String8 last_step_string = {0};
    for(U64 idx = 0; idx < input_tokenize.tokens.count; idx += 1)
    {
      MD_Token *token = &input_tokenize.tokens.v[idx];
      if(token->range.min >= cursor_off)
      {
        break;
      }
      String8 token_string = str8_substr(string, token->range);
      if(token->flags & (MD_TokenFlag_Identifier|MD_TokenFlag_StringLiteral))
      {
        last_step_string = token_string;
      }
      if(str8_match(token_string, str8_lit("("), 0) || str8_match(token_string, str8_lit("["), 0) || str8_match(token_string, str8_lit("{"), 0))
      {
        paren_nest += 1;
      }
      if(str8_match(token_string, str8_lit(")"), 0) || str8_match(token_string, str8_lit("]"), 0) || str8_match(token_string, str8_lit("}"), 0))
      {
        paren_nest -= 1;
        for(;colon_nest > paren_nest; colon_nest -= 1)
        {
          if(last_step != 0)
          {
            DescendStep *step = last_step;
            DLLRemove(first_step, last_step, step);
            SLLStackPush(free_step, step);
          }
        }
        if(paren_nest == 0 && last_step != 0)
        {
          DescendStep *step = last_step;
          DLLRemove(first_step, last_step, step);
          SLLStackPush(free_step, step);
        }
      }
      if(str8_match(token_string, str8_lit(":"), 0))
      {
        colon_nest += 1;
        if(last_step_string.size != 0)
        {
          DescendStep *step = free_step;
          if(step != 0)
          {
            SLLStackPop(free_step);
            MemoryZeroStruct(step);
          }
          else
          {
            step = push_array(scratch.arena, DescendStep, 1);
          }
          step->string = last_step_string;
          DLLPushBack(first_step, last_step, step);
        }
      }
      if(str8_match(token_string, str8_lit(";"), 0) || str8_match(token_string, str8_lit(","), 0))
      {
        for(;colon_nest > paren_nest; colon_nest -= 1)
        {
          if(last_step != 0)
          {
            DescendStep *step = last_step;
            DLLRemove(first_step, last_step, step);
            SLLStackPush(free_step, step);
          }
        }
      }
    }
    
    //- rjf: map view rule root to spec
    D_ViewRuleSpec *spec = d_view_rule_spec_from_string(first_step ? first_step->string : str8_zero());
    
    //- rjf: do parse of schema
    MD_TokenizeResult schema_tokenize = md_tokenize_from_text(scratch.arena, spec->info.schema);
    MD_ParseResult schema_parse = md_parse_from_text_tokens(scratch.arena, str8_zero(), spec->info.schema, schema_tokenize.tokens);
    MD_Node *schema_rule_root = md_child_from_string(schema_parse.root, str8_lit("x"), 0);
    
    //- rjf: follow schema according to descend steps, gather flags from schema node matching cursor descension steps
    if(first_step != 0)
    {
      MD_Node *schema_node = schema_rule_root;
      for(DescendStep *step = first_step->next;;)
      {
        if(step == 0)
        {
          for MD_EachNode(child, schema_node->first)
          {
            if(0){}
            else if(str8_match(child->string, str8_lit("expr"),           StringMatchFlag_CaseInsensitive)) {params.flags |= RD_ListerFlag_Locals;}
            else if(str8_match(child->string, str8_lit("member"),         StringMatchFlag_CaseInsensitive)) {params.flags |= RD_ListerFlag_Members;}
            else if(str8_match(child->string, str8_lit("lang"),           StringMatchFlag_CaseInsensitive)) {params.flags |= RD_ListerFlag_Languages;}
            else if(str8_match(child->string, str8_lit("arch"),           StringMatchFlag_CaseInsensitive)) {params.flags |= RD_ListerFlag_Architectures;}
            else if(str8_match(child->string, str8_lit("tex2dformat"),    StringMatchFlag_CaseInsensitive)) {params.flags |= RD_ListerFlag_Tex2DFormats;}
            else if(child->flags & (MD_NodeFlag_StringSingleQuote|MD_NodeFlag_StringDoubleQuote|MD_NodeFlag_StringTick))
            {
              str8_list_push(arena, &params.strings, child->string);
              params.flags |= RD_ListerFlag_ViewRuleParams;
            }
          }
          break;
        }
        if(step != 0)
        {
          MD_Node *next_node = md_child_from_string(schema_node, step->string, StringMatchFlag_CaseInsensitive);
          schema_node = next_node;
          step = step->next;
        }
        else
        {
          schema_node = schema_node->first;
        }
      }
    }
    
    scratch_end(scratch);
  }
  return params;
}

internal void
rd_set_autocomp_lister_query_(RD_ListerParams *params)
{
  RD_Cfg *window_cfg = rd_cfg_from_handle(rd_regs()->window);
  RD_WindowState *ws = rd_window_state_from_cfg(window_cfg);
  arena_clear(ws->lister_arena);
  ws->lister_regs = rd_regs_copy(ws->lister_arena, rd_regs());
  MemoryCopyStruct(&ws->lister_params, params);
  ws->lister_params.strings = str8_list_copy(ws->lister_arena, &ws->lister_params.strings);
  if(!(params->flags & RD_ListerFlag_LineEdit))
  {
    ws->lister_input_size = Min(params->input.size, sizeof(ws->lister_input_buffer));
    MemoryCopy(ws->lister_input_buffer, params->input.str, ws->lister_input_size);
  }
  ws->lister_last_frame_idx = rd_state->frame_index;
}

#endif

internal void
rd_set_autocomp_lister_query_(RD_Regs *regs)
{
  RD_Cfg *window_cfg = rd_cfg_from_handle(regs->window);
  RD_WindowState *ws = rd_window_state_from_cfg(window_cfg);
  if(ws->autocomp_lister == 0)
  {
    Arena *arena = arena_alloc();
    ws->autocomp_lister = push_array(arena, RD_Lister, 1);
    ws->autocomp_lister->arena = arena;
  }
  Arena *arena = ws->autocomp_lister->arena;
  arena_clear(arena);
  ws->autocomp_lister = push_array(arena, RD_Lister, 1);
  ws->autocomp_lister->arena = arena;
  ws->autocomp_lister->regs = rd_regs_copy(arena, regs);
  ws->autocomp_lister->input_string_size = Min(regs->string.size, sizeof(ws->autocomp_lister->input_buffer));
  MemoryCopy(ws->autocomp_lister->input_buffer, regs->string.str, ws->autocomp_lister->input_string_size);
  ws->autocomp_lister->input_cursor = regs->cursor;
  ws->autocomp_lister->input_mark = regs->mark;
  ws->autocomp_lister_last_frame_idx = rd_state->frame_index;
}

////////////////////////////////
//~ rjf: Search Strings

internal void
rd_set_search_string(String8 string)
{
  arena_clear(rd_state->string_search_arena);
  rd_state->string_search_string = push_str8_copy(rd_state->string_search_arena, string);
}

internal String8
rd_push_search_string(Arena *arena)
{
  String8 result = push_str8_copy(arena, rd_state->string_search_string);
  return result;
}

////////////////////////////////
//~ rjf: Colors, Fonts, Config

//- rjf: colors

internal Vec4F32
rd_rgba_from_theme_color(RD_ThemeColor color)
{
  return rd_state->theme->colors[color];
}

internal RD_ThemeColor
rd_theme_color_from_txt_token_kind(TXT_TokenKind kind)
{
  RD_ThemeColor color = RD_ThemeColor_CodeDefault;
  switch(kind)
  {
    default:break;
    case TXT_TokenKind_Keyword:{color = RD_ThemeColor_CodeKeyword;}break;
    case TXT_TokenKind_Numeric:{color = RD_ThemeColor_CodeNumeric;}break;
    case TXT_TokenKind_String: {color = RD_ThemeColor_CodeString;}break;
    case TXT_TokenKind_Meta:   {color = RD_ThemeColor_CodeMeta;}break;
    case TXT_TokenKind_Comment:{color = RD_ThemeColor_CodeComment;}break;
    case TXT_TokenKind_Symbol: {color = RD_ThemeColor_CodeDelimiterOperator;}break;
  }
  return color;
}

internal RD_ThemeColor
rd_theme_color_from_txt_token_kind_lookup_string(TXT_TokenKind kind, String8 string)
{
  RD_ThemeColor color = RD_ThemeColor_CodeDefault;
  if(kind == TXT_TokenKind_Identifier || kind == TXT_TokenKind_Keyword)
  {
    CTRL_Entity *module = ctrl_entity_from_handle(d_state->ctrl_entity_store, rd_regs()->module);
    DI_Key dbgi_key = ctrl_dbgi_key_from_module(module);
    B32 mapped = 0;
    
    // rjf: try to map as local
    if(!mapped && kind == TXT_TokenKind_Identifier)
    {
      U64 local_num = e_num_from_string(e_parse_state->ctx->locals_map, string);
      if(local_num != 0)
      {
        mapped = 1;
        color = RD_ThemeColor_CodeLocal;
      }
    }
    
    // rjf: try to map as member
    if(!mapped && kind == TXT_TokenKind_Identifier)
    {
      U64 member_num = e_num_from_string(e_parse_state->ctx->member_map, string);
      if(member_num != 0)
      {
        mapped = 1;
        color = RD_ThemeColor_CodeLocal;
      }
    }
    
    // rjf: try to map as register
    if(!mapped)
    {
      U64 reg_num = e_num_from_string(e_parse_state->ctx->regs_map, string);
      if(reg_num != 0)
      {
        mapped = 1;
        color = RD_ThemeColor_CodeRegister;
      }
    }
    
    // rjf: try to map as register alias
    if(!mapped)
    {
      U64 alias_num = e_num_from_string(e_parse_state->ctx->reg_alias_map, string);
      if(alias_num != 0)
      {
        mapped = 1;
        color = RD_ThemeColor_CodeRegister;
      }
    }
    
    // rjf: try to map using asynchronous matching system
    if(!mapped && kind == TXT_TokenKind_Identifier)
    {
      RDI_SectionKind section_kind = di_match_store_section_kind_from_name(rd_state->match_store, string, 0);
      mapped = 1;
      switch(section_kind)
      {
        default:{mapped = 0;}break;
        case RDI_SectionKind_Procedures:
        case RDI_SectionKind_GlobalVariables:
        case RDI_SectionKind_ThreadVariables:
        {
          color = RD_ThemeColor_CodeSymbol;
        }break;
        case RDI_SectionKind_TypeNodes:
        {
          color = RD_ThemeColor_CodeType;
        }break;
      }
    }
    
#if 0
    // rjf: try to map as symbol
    if(!mapped && kind == TXT_TokenKind_Identifier)
    {
      U64 voff = d_voff_from_dbgi_key_symbol_name(&dbgi_key, string);
      if(voff != 0)
      {
        mapped = 1;
        color = RD_ThemeColor_CodeSymbol;
      }
    }
    
    // rjf: try to map as type
    if(!mapped && kind == TXT_TokenKind_Identifier)
    {
      U64 type_num = d_type_num_from_dbgi_key_name(&dbgi_key, string);
      if(type_num != 0)
      {
        mapped = 1;
        color = RD_ThemeColor_CodeType;
      }
    }
#endif
  }
  return color;
}

//- rjf: code -> palette

internal UI_Palette *
rd_palette_from_code(RD_PaletteCode code)
{
  RD_Cfg *wcfg = rd_cfg_from_handle(rd_regs()->window);
  RD_WindowState *ws = rd_window_state_from_cfg(wcfg);
  UI_Palette *result = &ws->cfg_palettes[code];
  return result;
}

//- rjf: fonts/sizes

internal FNT_Tag
rd_font_from_slot(RD_FontSlot slot)
{
  // rjf: determine key name for this font slot
  String8 key = {0};
  switch(slot)
  {
    default:{}break;
    case RD_FontSlot_Main:{key = str8_lit("main_font");}break;
    case RD_FontSlot_Code:{key = str8_lit("code_font");}break;
  }
  
  // rjf: determine font name
  String8 font_name = {0};
  if(key.size != 0)
  {
    RD_Cfg *seed_cfg = &rd_nil_cfg;
    if(seed_cfg == &rd_nil_cfg) { seed_cfg = rd_cfg_from_handle(rd_regs()->view); }
    if(seed_cfg == &rd_nil_cfg) { seed_cfg = rd_cfg_from_handle(rd_regs()->panel); }
    if(seed_cfg == &rd_nil_cfg) { seed_cfg = rd_cfg_from_handle(rd_regs()->window); }
    for(RD_Cfg *cfg = seed_cfg; cfg != &rd_nil_cfg; cfg = cfg->parent)
    {
      RD_Cfg *font_root = rd_cfg_child_from_string(cfg, key);
      if(font_root != &rd_nil_cfg)
      {
        font_name = font_root->first->string;
        break;
      }
    }
  }
  
  // rjf: map name -> tag
  FNT_Tag result = {0};
  if(font_name.size == 0)
  {
    switch(slot)
    {
      default:
      case RD_FontSlot_Main: {result = fnt_tag_from_static_data_string(&rd_default_main_font_bytes);}break;
      case RD_FontSlot_Code: {result = fnt_tag_from_static_data_string(&rd_default_code_font_bytes);}break;
      case RD_FontSlot_Icons:{result = fnt_tag_from_static_data_string(&rd_icon_font_bytes);}break;
    }
  }
  else
  {
    // TODO(rjf): need to handle "system font names" here.
    result = fnt_tag_from_path(font_name);
  }
  return result;
}

internal F32
rd_font_size_from_slot(RD_FontSlot slot)
{
  F32 result = 11.f;
  
  // rjf: determine config key based on slot
  String8 key = {0};
  switch(slot)
  {
    default:{}break;
    case RD_FontSlot_Icons:
    case RD_FontSlot_Main:{key = str8_lit("main_font_size");}break;
    case RD_FontSlot_Code:{key = str8_lit("code_font_size");}break;
  }
  
  // rjf: given key, find setting string
  String8 setting_string = rd_setting_from_name(key);
  
  // rjf: if found, map setting string -> f64; otherwise use the window's monitor's DPI
  // based on some default size.
  if(setting_string.size)
  {
    result = (F32)f64_from_str8(setting_string);
  }
  else
  {
    RD_Cfg *window_cfg = rd_cfg_from_handle(rd_regs()->window);
    RD_WindowState *ws = rd_window_state_from_cfg(window_cfg);
    F32 dpi = os_dpi_from_window(ws->os);
    result = 11.f * (dpi / 96.f);
  }
  
  return result;
}

internal FNT_RasterFlags
rd_raster_flags_from_slot(RD_FontSlot slot)
{
  FNT_RasterFlags flags = FNT_RasterFlag_Smooth|FNT_RasterFlag_Hinted;
  switch(slot)
  {
    default:{}break;
    case RD_FontSlot_Icons:{flags = FNT_RasterFlag_Smooth;}break;
    case RD_FontSlot_Main: {flags = (rd_setting_b32_from_name(str8_lit("smooth_main_text"))*FNT_RasterFlag_Smooth)|(rd_setting_b32_from_name(str8_lit("hint_main_text"))*FNT_RasterFlag_Hinted);}break;
    case RD_FontSlot_Code: {flags = (rd_setting_b32_from_name(str8_lit("smooth_code_text"))*FNT_RasterFlag_Smooth)|(rd_setting_b32_from_name(str8_lit("hint_code_text"))*FNT_RasterFlag_Hinted);}break;
  }
  return flags;
}

//- rjf: config serialization

internal int
rd_qsort_compare__cfg_string_bindings(RD_StringBindingPair *a, RD_StringBindingPair *b)
{
  return strncmp((char *)a->string.str, (char *)b->string.str, Min(a->string.size, b->string.size));
}

internal String8List
rd_cfg_strings_from_gfx(Arena *arena, String8 root_path, RD_CfgSrc source)
{
  String8List strs = {0};
#if 0 // TODO(rjf): @cfg
  ProfBeginFunction();
  local_persist char *spaces = "                                                                                ";
  local_persist char *slashes= "////////////////////////////////////////////////////////////////////////////////";
  
  //- rjf: write all entities
  {
    for EachEnumVal(RD_EntityKind, k)
    {
      RD_EntityKindFlags k_flags = rd_entity_kind_flags_table[k];
      if(!(k_flags & RD_EntityKindFlag_IsSerializedToConfig))
      {
        continue;
      }
      B32 first = 1;
      RD_EntityList entities = rd_query_cached_entity_list_with_kind(k);
      for(RD_EntityNode *n = entities.first; n != 0; n = n->next)
      {
        RD_Entity *entity = n->entity;
        if(entity->cfg_src != source)
        {
          continue;
        }
        if(first)
        {
          first = 0;
          String8 title_name = d_entity_kind_name_lower_plural_table[k];
          str8_list_pushf(arena, &strs, "/// %S %.*s\n\n",
                          title_name,
                          (int)Max(0, 79 - (title_name.size + 5)),
                          slashes);
        }
        RD_EntityRec rec = {0};
        S64 depth = 0;
        for(RD_Entity *e = entity; !rd_entity_is_nil(e); e = rec.next)
        {
          //- rjf: get next iteration
          rec = rd_entity_rec_depth_first_pre(e, entity);
          
          //- rjf: unpack entity info
          typedef U32 EntityInfoFlags;
          enum
          {
            EntityInfoFlag_HasName     = (1<<0),
            EntityInfoFlag_HasDisabled = (1<<1),
            EntityInfoFlag_HasTxtPt    = (1<<2),
            EntityInfoFlag_HasVAddr    = (1<<3),
            EntityInfoFlag_HasColor    = (1<<4),
            EntityInfoFlag_HasChildren = (1<<5),
            EntityInfoFlag_HasDebugSubprocesses = (1<<6),
          };
          String8 entity_name_escaped = e->string;
          // TODO(rjf): @hack - hardcoding in the "EntityKind_Location" here - this is because
          // i am assuming an entity *kind* can 'know' about the 'pathness' of a string. this is
          // not the case. post-0.9.12 i need to fix this.
          if(rd_entity_kind_flags_table[e->kind] & RD_EntityKindFlag_NameIsPath &&
             (e->kind != RD_EntityKind_Location || e->flags & RD_EntityFlag_HasTextPoint))
          {
            Temp scratch = scratch_begin(&arena, 1);
            String8 path_normalized = path_normalized_from_string(scratch.arena, e->string);
            entity_name_escaped = path_relative_dst_from_absolute_dst_src(arena, path_normalized, root_path);
            scratch_end(scratch);
          }
          else
          {
            entity_name_escaped = escaped_from_raw_str8(arena, e->string);
          }
          EntityInfoFlags info_flags = 0;
          if(entity_name_escaped.size != 0)         { info_flags |= EntityInfoFlag_HasName; }
          if(!!e->disabled)                         { info_flags |= EntityInfoFlag_HasDisabled; }
          if(e->flags & RD_EntityFlag_HasTextPoint) { info_flags |= EntityInfoFlag_HasTxtPt; }
          if(e->flags & RD_EntityFlag_HasVAddr)     { info_flags |= EntityInfoFlag_HasVAddr; }
          if(e->flags & RD_EntityFlag_HasColor)     { info_flags |= EntityInfoFlag_HasColor; }
          if(!rd_entity_is_nil(e->first))           { info_flags |= EntityInfoFlag_HasChildren; }
          if(e->debug_subprocesses)                 { info_flags |= EntityInfoFlag_HasDebugSubprocesses; }
          
          //- rjf: write entity info
          B32 opened_brace = 0;
          switch(info_flags)
          {
            //- rjf: default path -> entity has lots of stuff, so write all info generically
            default:
            {
              opened_brace = 1;
              
              // rjf: write entity title
              str8_list_pushf(arena, &strs, "%S:\n{\n", d_entity_kind_name_lower_table[e->kind]);
              
              // rjf: write this entity's info
              if(entity_name_escaped.size != 0)
              {
                str8_list_pushf(arena, &strs, "name: \"%S\"\n", entity_name_escaped);
              }
              if(e->disabled)
              {
                str8_list_pushf(arena, &strs, "disabled: 1\n");
              }
              if(e->debug_subprocesses)
              {
                str8_list_pushf(arena, &strs, "debug_subprocesses: 1\n");
              }
              if(e->flags & RD_EntityFlag_HasColor)
              {
                Vec4F32 hsva = rd_hsva_from_entity(e);
                Vec4F32 rgba = rgba_from_hsva(hsva);
                U32 rgba_hex = u32_from_rgba(rgba);
                str8_list_pushf(arena, &strs, "color: 0x%x\n", rgba_hex);
              }
              if(e->flags & RD_EntityFlag_HasTextPoint)
              {
                str8_list_pushf(arena, &strs, "line: %I64d\n", e->text_point.line);
              }
              if(e->flags & RD_EntityFlag_HasVAddr)
              {
                str8_list_pushf(arena, &strs, "vaddr: (0x%I64x)\n", e->vaddr);
              }
            }break;
            
            //- rjf: single-line fast-paths
            case EntityInfoFlag_HasName:
            {str8_list_pushf(arena, &strs, "%S: \"%S\"\n", d_entity_kind_name_lower_table[e->kind], entity_name_escaped);}break;
            case EntityInfoFlag_HasName|EntityInfoFlag_HasTxtPt:
            {str8_list_pushf(arena, &strs, "%S: (\"%S\":%I64d)\n", d_entity_kind_name_lower_table[e->kind], entity_name_escaped, e->text_point.line);}break;
            case EntityInfoFlag_HasVAddr:
            {str8_list_pushf(arena, &strs, "%S: (0x%I64x)\n", d_entity_kind_name_lower_table[e->kind], e->vaddr);}break;
            
            //- rjf: empty
            case 0:
            {}break;
          }
          
          // rjf: push
          depth += rec.push_count;
          
          // rjf: pop
          if(rec.push_count == 0)
          {
            for(S64 pop_idx = 0; pop_idx < rec.pop_count + opened_brace; pop_idx += 1)
            {
              if(depth > 0)
              {
                depth -= 1;
              }
              str8_list_pushf(arena, &strs, "}\n");
            }
          }
          
          // rjf: separate top-level entities with extra newline
          if(rd_entity_is_nil(rec.next) && (rec.pop_count != 0 || n->next == 0))
          {
            str8_list_pushf(arena, &strs, "\n");
          }
        }
      }
    }
  }
  
  //- rjf: write exception code filters
  if(source == RD_CfgSrc_Project)
  {
    str8_list_push(arena, &strs, str8_lit("/// exception code filters ////////////////////////////////////////////////////\n"));
    str8_list_push(arena, &strs, str8_lit("\n"));
    str8_list_push(arena, &strs, str8_lit("exception_code_filters:\n"));
    str8_list_push(arena, &strs, str8_lit("{\n"));
    for(CTRL_ExceptionCodeKind k = (CTRL_ExceptionCodeKind)(CTRL_ExceptionCodeKind_Null+1);
        k < CTRL_ExceptionCodeKind_COUNT;
        k = (CTRL_ExceptionCodeKind)(k+1))
    {
      String8 name = ctrl_exception_code_kind_lowercase_code_string_table[k];
      B32 value = !!(rd_state->ctrl_exception_code_filters[k/64] & (1ull<<(k%64)));
      str8_list_pushf(arena, &strs, "  %S: %i\n", name, value);
    }
    str8_list_push(arena, &strs, str8_lit("}\n\n"));
  }
  
  //- rjf: serialize windows
  if(source == RD_CfgSrc_User)
  {
    B32 first = 1;
    for(RD_WindowState *window = rd_state->first_window_state; window != &rd_nil_window_state; window = window->order_next)
    {
      if(first)
      {
        first = 0;
        str8_list_push(arena, &strs, str8_lit("/// windows ///////////////////////////////////////////////////////////////////\n"));
        str8_list_push(arena, &strs, str8_lit("\n"));
      }
      OS_Handle monitor = os_monitor_from_window(window->os);
      String8 monitor_name = os_name_from_monitor(arena, monitor);
      RD_Panel *root_panel = window->root_panel;
      Rng2F32 rect = os_rect_from_window(window->os);
      Vec2F32 size = dim_2f32(rect);
      str8_list_push (arena, &strs,  str8_lit("window:\n"));
      str8_list_push (arena, &strs,  str8_lit("{\n"));
      str8_list_pushf(arena, &strs,           "  %s%s%s\n",
                      root_panel->split_axis == Axis2_X ? "split_x" : "split_y",
                      os_window_is_fullscreen(window->os) ? " fullscreen" : "",
                      os_window_is_maximized(window->os) ? " maximized" : "");
      str8_list_pushf(arena, &strs, "  monitor: \"%S\"\n", monitor_name);
      str8_list_pushf(arena, &strs, "  size: (%i %i)\n", (int)size.x, (int)size.y);
      str8_list_pushf(arena, &strs, "  dpi: %f\n", os_dpi_from_window(window->os));
      for EachEnumVal(RD_SettingCode, code)
      {
        RD_SettingVal current = window->setting_vals[code];
        if(current.set)
        {
          str8_list_pushf(arena, &strs, "  %S: %i\n", rd_setting_code_lower_string_table[code], current.s32);
        }
      }
      {
        RD_PanelRec rec = {0};
        S32 indentation = 2;
        String8 indent_str = str8_lit("                                                                                                   ");
        str8_list_pushf(arena, &strs, "  panels:\n");
        str8_list_pushf(arena, &strs, "  {\n");
        for(RD_Panel *p = root_panel; !rd_panel_is_nil(p); p = rec.next)
        {
          // rjf: get recursion
          rec = rd_panel_rec_depth_first_pre(p);
          
          // rjf: non-root needs pct node
          if(p != root_panel)
          {
            str8_list_pushf(arena, &strs, "%.*s%g:\n", indentation*2, indent_str.str, p->pct_of_parent);
            str8_list_pushf(arena, &strs, "%.*s{\n", indentation*2, indent_str.str);
            indentation += 1;
          }
          
          // rjf: per-panel options
          struct { String8 key; B32 value; } options[] =
          {
            {str8_lit_comp("tabs_on_bottom"),   p->tab_side == Side_Max},
          };
          B32 has_options = 0;
          for(U64 op_idx = 0; op_idx < ArrayCount(options); op_idx += 1)
          {
            if(options[op_idx].value)
            {
              if(has_options == 0)
              {
                str8_list_pushf(arena, &strs, "%.*s", indentation*2, indent_str.str);
              }
              else
              {
                str8_list_pushf(arena, &strs, " ");
              }
              has_options = 1;
              str8_list_push(arena, &strs, options[op_idx].key);
            }
          }
          if(has_options)
          {
            str8_list_pushf(arena, &strs, "\n");
          }
          
          // rjf: views
          for(RD_View *view = p->first_tab_view; !rd_view_is_nil(view); view = view->order_next)
          {
            String8 view_string = view->spec->string;
            
            // rjf: serialize views
            {
              str8_list_pushf(arena, &strs, "%.*s", indentation*2, indent_str.str);
              
              // rjf: serialize view string
              str8_list_push(arena, &strs, view_string);
              
              // rjf: serialize view parameterizations
              str8_list_push(arena, &strs, str8_lit(": {"));
              if(view == rd_selected_tab_from_panel(p))
              {
                str8_list_push(arena, &strs, str8_lit("selected "));
              }
              {
                if(view->project_path.size != 0)
                {
                  Temp scratch = scratch_begin(&arena, 1);
                  String8 project_path_absolute = path_normalized_from_string(scratch.arena, view->project_path);
                  String8 project_path_relative = path_relative_dst_from_absolute_dst_src(scratch.arena, project_path_absolute, root_path);
                  str8_list_pushf(arena, &strs, "project:{\"%S\"} ", project_path_relative);
                  scratch_end(scratch);
                }
              }
              if(view->query_string_size != 0)
              {
                Temp scratch = scratch_begin(&arena, 1);
                String8 query_raw = str8(view->query_buffer, view->query_string_size);
                {
                  String8 query_file_path = rd_file_path_from_eval_string(scratch.arena, query_raw);
                  if(query_file_path.size != 0)
                  {
                    query_file_path = path_relative_dst_from_absolute_dst_src(scratch.arena, query_file_path, root_path);
                    query_raw = push_str8f(scratch.arena, "file:\"%S\"", query_file_path);
                  }
                }
                String8 query_sanitized = escaped_from_raw_str8(scratch.arena, query_raw);
                str8_list_pushf(arena, &strs, "query:{\"%S\"} ", query_sanitized);
                scratch_end(scratch);
              }
              {
                String8 reserved_keys[] =
                {
                  str8_lit("project"),
                  str8_lit("query"),
                  str8_lit("selected"),
                };
                MD_NodeRec rec = {0};
                MD_Node *params_root = view->params_roots[view->params_read_gen%ArrayCount(view->params_roots)];
                for(MD_Node *n = params_root;
                    !md_node_is_nil(n);
                    n = rec.next)
                {
                  rec = md_node_rec_depth_first_pre(n, params_root);
                  B32 is_reserved_key = 0;
                  for(U64 idx = 0; idx < ArrayCount(reserved_keys); idx += 1)
                  {
                    if(str8_match(n->string, reserved_keys[idx], 0))
                    {
                      is_reserved_key = 1;
                      break;
                    }
                  }
                  if(is_reserved_key)
                  {
                    rec = md_node_rec_depth_first(n, params_root, OffsetOf(MD_Node, next), OffsetOf(MD_Node, next));
                  }
                  if(!is_reserved_key && n != params_root)
                  {
                    str8_list_pushf(arena, &strs, "%S", n->string);
                    if(n->first != &md_nil_node)
                    {
                      str8_list_pushf(arena, &strs, ":{");
                    }
                    for(S32 pop_idx = 0; pop_idx < rec.pop_count; pop_idx += 1)
                    {
                      if(pop_idx == rec.pop_count-1 && rec.next == &md_nil_node)
                      {
                        break;
                      }
                      str8_list_pushf(arena, &strs, "}");
                    }
                    if(rec.pop_count != 0 || n->next != &md_nil_node)
                    {
                      str8_list_pushf(arena, &strs, " ");
                    }
                  }
                }
              }
              str8_list_push(arena, &strs, str8_lit("}\n"));
            }
          }
          
          // rjf: non-roots need closer
          if(p != root_panel && rec.push_count == 0)
          {
            indentation -= 1;
            str8_list_pushf(arena, &strs, "%.*s}\n", indentation*2, indent_str.str);
          }
          
          // rjf: pop
          for(S32 pop_idx = 0; pop_idx < rec.pop_count; pop_idx += 1)
          {
            indentation -= 1;
            if(pop_idx == rec.pop_count-1 && rec.next == &rd_nil_panel)
            {
              break;
            }
            str8_list_pushf(arena, &strs, "%.*s}\n", indentation*2, indent_str.str);
          }
        }
        str8_list_pushf(arena, &strs, "  }\n");
      }
      str8_list_push (arena, &strs,  str8_lit("}\n"));
      str8_list_push (arena, &strs,  str8_lit("\n"));
    }
  }
  
  //- rjf: serialize keybindings
  if(source == RD_CfgSrc_User)
  {
    Temp scratch = scratch_begin(&arena, 1);
    String8 indent_str = str8_lit("                                                                                                             ");
    U64 string_binding_pair_count = 0;
    RD_StringBindingPair *string_binding_pairs = push_array(scratch.arena, RD_StringBindingPair, rd_state->key_map_total_count);
    for(U64 idx = 0;
        idx < rd_state->key_map_table_size && string_binding_pair_count < rd_state->key_map_total_count;
        idx += 1)
    {
      for(RD_KeyMapNode *n = rd_state->key_map_table[idx].first;
          n != 0 && string_binding_pair_count < rd_state->key_map_total_count;
          n = n->hash_next)
      {
        RD_StringBindingPair *pair = string_binding_pairs + string_binding_pair_count;
        pair->string = n->name;
        pair->binding = n->binding;
        string_binding_pair_count += 1;
      }
    }
    quick_sort(string_binding_pairs, string_binding_pair_count, sizeof(RD_StringBindingPair), rd_qsort_compare__cfg_string_bindings);
    if(string_binding_pair_count != 0)
    {
      str8_list_push(arena, &strs, str8_lit("/// keybindings ///////////////////////////////////////////////////////////////\n"));
      str8_list_push(arena, &strs, str8_lit("\n"));
      str8_list_push(arena, &strs, str8_lit("keybindings:\n"));
      str8_list_push(arena, &strs, str8_lit("{\n"));
      for(U64 idx = 0; idx < string_binding_pair_count; idx += 1)
      {
        RD_StringBindingPair *pair = string_binding_pairs + idx;
        String8List modifiers_strings = os_string_list_from_modifiers(scratch.arena, pair->binding.modifiers);
        StringJoin join = {str8_lit(""), str8_lit(" "), str8_lit("")};
        String8 event_flags_string = str8_list_join(scratch.arena, &modifiers_strings, &join);
        String8 key_string = push_str8_copy(scratch.arena, os_g_key_cfg_string_table[pair->binding.key]);
        for(U64 i = 0; i < event_flags_string.size; i += 1)
        {
          event_flags_string.str[i] = char_to_lower(event_flags_string.str[i]);
        }
        String8 binding_string = push_str8f(scratch.arena, "%S%s%S",
                                            event_flags_string,
                                            event_flags_string.size > 0 ? " " : "",
                                            key_string);
        str8_list_pushf(arena, &strs, "  {\"%S\"%.*s%S%.*s}\n",
                        pair->string,
                        40 > pair->string.size ? ((int)(40 - pair->string.size)) : 0, indent_str.str,
                        binding_string,
                        20 > binding_string.size ? ((int)(20 - binding_string.size)) : 0, indent_str.str);
      }
      str8_list_push(arena, &strs, str8_lit("}\n\n"));
    }
    scratch_end(scratch);
  }
  
  //- rjf: serialize theme colors
  if(source == RD_CfgSrc_User)
  {
    // rjf: determine if this theme matches an existing preset
    B32 is_preset = 0;
    RD_ThemePreset matching_preset = RD_ThemePreset_DefaultDark;
    {
      for(RD_ThemePreset p = (RD_ThemePreset)0; p < RD_ThemePreset_COUNT; p = (RD_ThemePreset)(p+1))
      {
        B32 matches_this_preset = 1;
        for(RD_ThemeColor c = (RD_ThemeColor)(RD_ThemeColor_Null+1); c < RD_ThemeColor_COUNT; c = (RD_ThemeColor)(c+1))
        {
          if(!MemoryMatchStruct(&rd_state->cfg_theme_target.colors[c], &rd_theme_preset_colors_table[p][c]))
          {
            matches_this_preset = 0;
            break;
          }
        }
        if(matches_this_preset)
        {
          is_preset = 1;
          matching_preset = p;
          break;
        }
      }
    }
    
    // rjf: serialize header
    String8 indent_str = str8_lit("                                                                                                             ");
    str8_list_push(arena, &strs, str8_lit("/// colors ////////////////////////////////////////////////////////////////////\n"));
    str8_list_push(arena, &strs, str8_lit("\n"));
    
    // rjf: serialize preset theme
    if(is_preset)
    {
      str8_list_pushf(arena, &strs, "color_preset: \"%S\"\n\n", rd_theme_preset_code_string_table[matching_preset]);
    }
    
    // rjf: serialize non-preset theme
    if(!is_preset)
    {
      str8_list_push(arena, &strs, str8_lit("colors:\n"));
      str8_list_push(arena, &strs, str8_lit("{\n"));
      for(RD_ThemeColor color = (RD_ThemeColor)(RD_ThemeColor_Null+1);
          color < RD_ThemeColor_COUNT;
          color = (RD_ThemeColor)(color+1))
      {
        String8 color_name = rd_theme_color_cfg_string_table[color];
        Vec4F32 color_rgba = rd_state->cfg_theme_target.colors[color];
        String8 color_hex  = hex_string_from_rgba_4f32(arena, color_rgba);
        str8_list_pushf(arena, &strs, "  %S:%.*s0x%S\n",
                        color_name,
                        30 > color_name.size ? ((int)(30 - color_name.size)) : 0, indent_str.str,
                        color_hex);
      }
      str8_list_push(arena, &strs, str8_lit("}\n\n"));
    }
  }
  
  //- rjf: serialize fonts
  if(source == RD_CfgSrc_User)
  {
    String8 code_font_path_escaped = escaped_from_raw_str8(arena, rd_state->cfg_code_font_path);
    String8 main_font_path_escaped = escaped_from_raw_str8(arena, rd_state->cfg_main_font_path);
    str8_list_push(arena, &strs, str8_lit("/// fonts /////////////////////////////////////////////////////////////////////\n"));
    str8_list_push(arena, &strs, str8_lit("\n"));
    str8_list_pushf(arena, &strs, "code_font: \"%S\"\n", code_font_path_escaped);
    str8_list_pushf(arena, &strs, "main_font: \"%S\"\n", main_font_path_escaped);
    str8_list_push(arena, &strs, str8_lit("\n"));
  }
  
  //- rjf: serialize global settings
  {
    B32 first = 1;
    for EachEnumVal(RD_SettingCode, code)
    {
      if(rd_setting_code_default_is_per_window_table[code])
      {
        continue;
      }
      RD_SettingVal current = rd_state->cfg_setting_vals[source][code];
      if(current.set)
      {
        if(first)
        {
          first = 0;
          str8_list_push(arena, &strs, str8_lit("/// global settings ///////////////////////////////////////////////////////////\n"));
          str8_list_push(arena, &strs, str8_lit("\n"));
        }
        str8_list_pushf(arena, &strs, "%S: %i\n", rd_setting_code_lower_string_table[code], current.s32);
      }
    }
    if(!first)
    {
      str8_list_push(arena, &strs, str8_lit("\n"));
    }
  }
  
  ProfEnd();
#endif
  return strs;
}

////////////////////////////////
//~ rjf: Process Control Info Stringification

internal String8
rd_string_from_exception_code(U32 code)
{
  String8 string = {0};
  for EachNonZeroEnumVal(CTRL_ExceptionCodeKind, k)
  {
    if(code == ctrl_exception_code_kind_code_table[k])
    {
      string = ctrl_exception_code_kind_display_string_table[k];
      break;
    }
  }
  return string;
}

internal DR_FancyStringList
rd_stop_explanation_fstrs_from_ctrl_event(Arena *arena, CTRL_Event *event)
{
  CTRL_Entity *thread = ctrl_entity_from_handle(d_state->ctrl_entity_store, event->entity);
  DR_FancyStringList thread_fstrs = rd_title_fstrs_from_ctrl_entity(arena, thread, ui_top_palette()->text, ui_top_font_size(), 0);
  DR_FancyStringList fstrs = {0};
  switch(event->cause)
  {
    default:{}break;
    
    //- rjf: finished operation; if active thread, completed thread, otherwise we're just stopped
    case CTRL_EventCause_Finished:
    {
      if(thread != &ctrl_entity_nil)
      {
        dr_fancy_string_list_concat_in_place(&fstrs, &thread_fstrs);
        dr_fancy_string_list_push_new(arena, &fstrs, ui_top_font(), ui_top_font_size(), ui_top_palette()->text, str8_lit(" completed step"));
      }
      else
      {
        dr_fancy_string_list_push_new(arena, &fstrs, ui_top_font(), ui_top_font_size(), ui_top_palette()->text, str8_lit("Stopped"));
      }
    }break;
    
    //- rjf: stopped at entry point
    case CTRL_EventCause_EntryPoint:
    {
      if(thread != &ctrl_entity_nil)
      {
        dr_fancy_string_list_concat_in_place(&fstrs, &thread_fstrs);
        dr_fancy_string_list_push_new(arena, &fstrs, ui_top_font(), ui_top_font_size(), ui_top_palette()->text, str8_lit(" stopped at entry point"));
      }
      else
      {
        dr_fancy_string_list_push_new(arena, &fstrs, ui_top_font(), ui_top_font_size(), ui_top_palette()->text, str8_lit("Stopped at entry point"));
      }
    }break;
    
    //- rjf: user breakpoint
    case CTRL_EventCause_UserBreakpoint:
    {
      if(thread != &ctrl_entity_nil)
      {
        dr_fancy_string_list_push_new(arena, &fstrs, rd_font_from_slot(RD_FontSlot_Icons), ui_top_font_size(), ui_top_palette()->text, rd_icon_kind_text_table[RD_IconKind_CircleFilled]);
        dr_fancy_string_list_push_new(arena, &fstrs, ui_top_font(), ui_top_font_size(), ui_top_palette()->text, str8_lit("  "));
        dr_fancy_string_list_concat_in_place(&fstrs, &thread_fstrs);
        dr_fancy_string_list_push_new(arena, &fstrs, ui_top_font(), ui_top_font_size(), ui_top_palette()->text, str8_lit(" hit a breakpoint"));
      }
    }break;
    
    //- rjf: exception
    case CTRL_EventCause_InterruptedByException:
    {
      if(thread != &ctrl_entity_nil)
      {
        dr_fancy_string_list_push_new(arena, &fstrs, rd_font_from_slot(RD_FontSlot_Icons), ui_top_font_size(), ui_top_palette()->text, rd_icon_kind_text_table[RD_IconKind_WarningBig]);
        switch(event->exception_kind)
        {
          default:
          {
            dr_fancy_string_list_push_new(arena, &fstrs, ui_top_font(), ui_top_font_size(), ui_top_palette()->text, str8_lit("  "));
            dr_fancy_string_list_concat_in_place(&fstrs, &thread_fstrs);
            dr_fancy_string_list_push_new(arena, &fstrs, ui_top_font(), ui_top_font_size(), ui_top_palette()->text, str8_lit(" hit an exception - "));
            String8 exception_code_string = str8_from_u64(arena, event->exception_code, 16, 0, 0);
            String8 exception_explanation_string = rd_string_from_exception_code(event->exception_code);
            String8 exception_info_string = push_str8f(arena, "%S%s%S%s",
                                                       exception_code_string,
                                                       exception_explanation_string.size != 0 ? " (" : "",
                                                       exception_explanation_string,
                                                       exception_explanation_string.size != 0 ? ")" : "");
            dr_fancy_string_list_push_new(arena, &fstrs, ui_top_font(), ui_top_font_size(), ui_top_palette()->text, exception_info_string);
          }break;
          case CTRL_ExceptionKind_CppThrow:
          {
            dr_fancy_string_list_push_new(arena, &fstrs, ui_top_font(), ui_top_font_size(), ui_top_palette()->text, str8_lit("  "));
            dr_fancy_string_list_concat_in_place(&fstrs, &thread_fstrs);
            dr_fancy_string_list_push_new(arena, &fstrs, ui_top_font(), ui_top_font_size(), ui_top_palette()->text, str8_lit(" hit a C++ exception - "));
            String8 exception_code_string = str8_from_u64(arena, event->exception_code, 16, 0, 0);
            dr_fancy_string_list_push_new(arena, &fstrs, ui_top_font(), ui_top_font_size(), ui_top_palette()->text, exception_code_string);
          }break;
          case CTRL_ExceptionKind_MemoryRead:
          {
            dr_fancy_string_list_push_new(arena, &fstrs, ui_top_font(), ui_top_font_size(), ui_top_palette()->text, str8_lit("  "));
            dr_fancy_string_list_concat_in_place(&fstrs, &thread_fstrs);
            dr_fancy_string_list_push_new(arena, &fstrs, ui_top_font(), ui_top_font_size(), ui_top_palette()->text, str8_lit(" hit an exception - "));
            String8 exception_code_string = str8_from_u64(arena, event->exception_code, 16, 0, 0);
            String8 exception_info_string = push_str8f(arena, "%S (Access violation reading 0x%I64x)", exception_code_string, event->vaddr_rng.min);
            dr_fancy_string_list_push_new(arena, &fstrs, ui_top_font(), ui_top_font_size(), ui_top_palette()->text, exception_info_string);
          }break;
          case CTRL_ExceptionKind_MemoryWrite:
          {
            dr_fancy_string_list_push_new(arena, &fstrs, ui_top_font(), ui_top_font_size(), ui_top_palette()->text, str8_lit("  "));
            dr_fancy_string_list_concat_in_place(&fstrs, &thread_fstrs);
            dr_fancy_string_list_push_new(arena, &fstrs, ui_top_font(), ui_top_font_size(), ui_top_palette()->text, str8_lit(" hit an exception - "));
            String8 exception_code_string = str8_from_u64(arena, event->exception_code, 16, 0, 0);
            String8 exception_info_string = push_str8f(arena, "%S (Access violation writing 0x%I64x)", exception_code_string, event->vaddr_rng.min);
            dr_fancy_string_list_push_new(arena, &fstrs, ui_top_font(), ui_top_font_size(), ui_top_palette()->text, exception_info_string);
          }break;
          case CTRL_ExceptionKind_MemoryExecute:
          {
            dr_fancy_string_list_push_new(arena, &fstrs, ui_top_font(), ui_top_font_size(), ui_top_palette()->text, str8_lit("  "));
            dr_fancy_string_list_concat_in_place(&fstrs, &thread_fstrs);
            dr_fancy_string_list_push_new(arena, &fstrs, ui_top_font(), ui_top_font_size(), ui_top_palette()->text, str8_lit(" hit an exception - "));
            String8 exception_code_string = str8_from_u64(arena, event->exception_code, 16, 0, 0);
            String8 exception_info_string = push_str8f(arena, "%S (Access violation executing 0x%I64x)", exception_code_string, event->vaddr_rng.min);
            dr_fancy_string_list_push_new(arena, &fstrs, ui_top_font(), ui_top_font_size(), ui_top_palette()->text, exception_info_string);
          }break;
        }
      }
      else
      {
        dr_fancy_string_list_push_new(arena, &fstrs, rd_font_from_slot(RD_FontSlot_Icons), ui_top_font_size(), ui_top_palette()->text, rd_icon_kind_text_table[RD_IconKind_WarningBig]);
        dr_fancy_string_list_push_new(arena, &fstrs, ui_top_font(), ui_top_font_size(), ui_top_palette()->text, str8_lit("Hit an exception - "));
        String8 exception_code_string = str8_from_u64(arena, event->exception_code, 16, 0, 0);
        String8 exception_explanation_string = rd_string_from_exception_code(event->exception_code);
        String8 exception_info_string = push_str8f(arena, "%S%s%S%s",
                                                   exception_code_string,
                                                   exception_explanation_string.size != 0 ? " (" : "",
                                                   exception_explanation_string,
                                                   exception_explanation_string.size != 0 ? ")" : "");
        dr_fancy_string_list_push_new(arena, &fstrs, ui_top_font(), ui_top_font_size(), ui_top_palette()->text, exception_info_string);
      }
    }break;
    
    //- rjf: trap
    case CTRL_EventCause_InterruptedByTrap:
    {
      dr_fancy_string_list_push_new(arena, &fstrs, rd_font_from_slot(RD_FontSlot_Icons), ui_top_font_size(), ui_top_palette()->text, rd_icon_kind_text_table[RD_IconKind_WarningBig]);
      dr_fancy_string_list_push_new(arena, &fstrs, ui_top_font(), ui_top_font_size(), ui_top_palette()->text, str8_lit("  "));
      dr_fancy_string_list_concat_in_place(&fstrs, &thread_fstrs);
      dr_fancy_string_list_push_new(arena, &fstrs, ui_top_font(), ui_top_font_size(), ui_top_palette()->text, str8_lit(" hit a trap"));
    }break;
    
    //- rjf: halt
    case CTRL_EventCause_InterruptedByHalt:
    {
      dr_fancy_string_list_push_new(arena, &fstrs, rd_font_from_slot(RD_FontSlot_Icons), ui_top_font_size(), ui_top_palette()->text, rd_icon_kind_text_table[RD_IconKind_Pause]);
      dr_fancy_string_list_push_new(arena, &fstrs, ui_top_font(), ui_top_font_size(), ui_top_palette()->text, str8_lit("Halted"));
    }break;
  }
  return fstrs;
}

////////////////////////////////
//~ rjf: Vocabulary Info Lookups

internal RD_VocabularyInfo *
rd_vocabulary_info_from_code_name(String8 code_name)
{
  RD_VocabularyInfo *info = &rd_nil_vocabulary_info;
  for EachElement(idx, rd_vocabulary_info_table)
  {
    if(str8_match(rd_vocabulary_info_table[idx].code_name, code_name, 0))
    {
      info = &rd_vocabulary_info_table[idx];
      break;
    }
  }
  return info;
}

internal RD_VocabularyInfo *
rd_vocabulary_info_from_code_name_plural(String8 code_name_plural)
{
  RD_VocabularyInfo *info = &rd_nil_vocabulary_info;
  for EachElement(idx, rd_vocabulary_info_table)
  {
    if(str8_match(rd_vocabulary_info_table[idx].code_name_plural, code_name_plural, 0))
    {
      info = &rd_vocabulary_info_table[idx];
      break;
    }
  }
  return info;
}

////////////////////////////////
//~ rjf: Continuous Frame Requests

internal void
rd_request_frame(void)
{
  rd_state->num_frames_requested = 4;
}

////////////////////////////////
//~ rjf: Main State Accessors

//- rjf: per-frame arena

internal Arena *
rd_frame_arena(void)
{
  return rd_state->frame_arenas[rd_state->frame_index%ArrayCount(rd_state->frame_arenas)];
}

//- rjf: entity cache queries

internal RD_EntityList
rd_query_cached_entity_list_with_kind(RD_EntityKind kind)
{
  ProfBeginFunction();
  RD_EntityListCache *cache = &rd_state->kind_caches[kind];
  
  // rjf: build cached list if we're out-of-date
  if(cache->alloc_gen != rd_state->kind_alloc_gens[kind])
  {
    cache->alloc_gen = rd_state->kind_alloc_gens[kind];
    if(cache->arena == 0)
    {
      cache->arena = arena_alloc();
    }
    arena_clear(cache->arena);
    cache->list = rd_push_entity_list_with_kind(cache->arena, kind);
  }
  
  // rjf: grab & return cached list
  RD_EntityList result = cache->list;
  ProfEnd();
  return result;
}

////////////////////////////////
//~ rjf: Registers

internal RD_Regs *
rd_regs(void)
{
  RD_Regs *regs = &rd_state->top_regs->v;
  return regs;
}

internal RD_Regs *
rd_base_regs(void)
{
  RD_Regs *regs = &rd_state->base_regs.v;
  return regs;
}

internal RD_Regs *
rd_push_regs_(RD_Regs *regs)
{
  RD_RegsNode *n = push_array(rd_frame_arena(), RD_RegsNode, 1);
  rd_regs_copy_contents(rd_frame_arena(), &n->v, regs);
  SLLStackPush(rd_state->top_regs, n);
  return &n->v;
}

internal RD_Regs *
rd_pop_regs(void)
{
  RD_Regs *regs = &rd_state->top_regs->v;
  SLLStackPop(rd_state->top_regs);
  if(rd_state->top_regs == 0)
  {
    rd_state->top_regs = &rd_state->base_regs;
  }
  return regs;
}

internal void
rd_regs_fill_slot_from_string(RD_RegSlot slot, String8 string)
{
  String8 error = {0};
  switch(slot)
  {
    default:
    case RD_RegSlot_String:
    case RD_RegSlot_FilePath:
    {
      String8TxtPtPair pair = str8_txt_pt_pair_from_string(string);
      if(pair.pt.line == 0 || slot == RD_RegSlot_String)
      {
        rd_regs()->string = push_str8_copy(rd_frame_arena(), string);
      }
      else
      {
        rd_regs()->file_path = push_str8_copy(rd_frame_arena(), pair.string);
        rd_regs()->cursor = pair.pt;
      }
    }break;
    case RD_RegSlot_Cursor:
    {
      U64 v = 0;
      if(try_u64_from_str8_c_rules(string, &v))
      {
        rd_regs()->cursor.column = 1;
        rd_regs()->cursor.line = v;
      }
      else
      {
        log_user_error(str8_lit("Couldn't interpret as a line number."));
      }
    }break;
    case RD_RegSlot_Vaddr: goto use_numeric_eval;
    case RD_RegSlot_Voff: goto use_numeric_eval;
    case RD_RegSlot_UnwindCount: goto use_numeric_eval;
    case RD_RegSlot_InlineDepth: goto use_numeric_eval;
    case RD_RegSlot_PID: goto use_numeric_eval;
    use_numeric_eval:
    {
      Temp scratch = scratch_begin(0, 0);
      E_Eval eval = e_eval_from_string(scratch.arena, string);
      if(eval.msgs.max_kind == E_MsgKind_Null)
      {
        E_TypeKind eval_type_kind = e_type_kind_from_key(e_type_unwrap(eval.type_key));
        if(eval_type_kind == E_TypeKind_Ptr ||
           eval_type_kind == E_TypeKind_LRef ||
           eval_type_kind == E_TypeKind_RRef)
        {
          eval = e_value_eval_from_eval(eval);
        }
        U64 u64 = eval.value.u64;
        switch(slot)
        {
          default:{}break;
          case RD_RegSlot_Vaddr:
          {
            rd_regs()->vaddr = u64;
          }break;
          case RD_RegSlot_Voff:
          {
            rd_regs()->voff = u64;
          }break;
          case RD_RegSlot_UnwindCount:
          {
            rd_regs()->unwind_count = u64;
          }break;
          case RD_RegSlot_InlineDepth:
          {
            rd_regs()->inline_depth = u64;
          }break;
          case RD_RegSlot_PID:
          {
            rd_regs()->pid = u64;
          }break;
        }
      }
      else
      {
        log_user_errorf("Couldn't evaluate \"%S\" as an address.", string);
      }
      scratch_end(scratch);
    }break;
  }
}

////////////////////////////////
//~ rjf: Commands

//- rjf: name -> info

internal RD_CmdKind
rd_cmd_kind_from_string(String8 string)
{
  RD_CmdKind result = RD_CmdKind_Null;
  for(U64 idx = 0; idx < ArrayCount(rd_cmd_kind_info_table); idx += 1)
  {
    if(str8_match(string, rd_cmd_kind_info_table[idx].string, 0))
    {
      result = (RD_CmdKind)idx;
      break;
    }
  }
  return result;
}

internal RD_CmdKindInfo *
rd_cmd_kind_info_from_string(String8 string)
{
  RD_CmdKindInfo *info = &rd_nil_cmd_kind_info;
  {
    // TODO(rjf): @dynamic_cmds extend this by looking up into dynamically-registered commands by views
    RD_CmdKind kind = rd_cmd_kind_from_string(string);
    if(kind != RD_CmdKind_Null)
    {
      info = &rd_cmd_kind_info_table[kind];
    }
  }
  return info;
}

//- rjf: pushing

internal void
rd_push_cmd(String8 name, RD_Regs *regs)
{
  rd_cmd_list_push_new(rd_state->cmds_arenas[0], &rd_state->cmds[0], name, regs);
}

//- rjf: iterating

internal B32
rd_next_cmd(RD_Cmd **cmd)
{
  U64 slot = rd_state->cmds_gen%ArrayCount(rd_state->cmds);
  RD_CmdNode *start_node = rd_state->cmds[slot].first;
  if(cmd[0] != 0)
  {
    start_node = CastFromMember(RD_CmdNode, cmd, cmd[0]);
    start_node = start_node->next;
  }
  cmd[0] = 0;
  if(start_node != 0)
  {
    cmd[0] = &start_node->cmd;
  }
  return !!cmd[0];
}

////////////////////////////////
//~ rjf: Main Layer Top-Level Calls

#if !defined(STBI_INCLUDE_STB_IMAGE_H)
# define STB_IMAGE_IMPLEMENTATION
# define STBI_ONLY_PNG
# define STBI_ONLY_BMP
# include "third_party/stb/stb_image.h"
#endif

internal void
rd_init(CmdLine *cmdln)
{
  ProfBeginFunction();
  Arena *arena = arena_alloc();
  rd_state = push_array(arena, RD_State, 1);
  rd_state->arena = arena;
  rd_state->quit_after_success = (cmd_line_has_flag(cmdln, str8_lit("quit_after_success")) ||
                                  cmd_line_has_flag(cmdln, str8_lit("q")));
  rd_state->user_path_arena = arena_alloc();
  rd_state->project_path_arena = arena_alloc();
  for(U64 idx = 0; idx < ArrayCount(rd_state->frame_arenas); idx += 1)
  {
    rd_state->frame_arenas[idx] = arena_alloc();
  }
  rd_state->log = log_alloc();
  log_select(rd_state->log);
  {
    Temp scratch = scratch_begin(0, 0);
    String8 user_program_data_path = os_get_process_info()->user_program_data_path;
    String8 user_data_folder = push_str8f(scratch.arena, "%S/raddbg/logs", user_program_data_path);
    rd_state->log_path = push_str8f(rd_state->arena, "%S/ui_thread.raddbg_log", user_data_folder);
    os_make_directory(user_data_folder);
    os_write_data_to_file_path(rd_state->log_path, str8_zero());
    scratch_end(scratch);
  }
  rd_state->num_frames_requested = 2;
  rd_state->seconds_until_autosave = 0.5f;
  rd_state->match_store = di_match_store_alloc();
  for(U64 idx = 0; idx < ArrayCount(rd_state->cmds_arenas); idx += 1)
  {
    rd_state->cmds_arenas[idx] = arena_alloc();
  }
  rd_state->entities_arena = arena_alloc(.reserve_size = GB(64), .commit_size = KB(64));
  rd_state->entities_root = &rd_nil_entity;
  rd_state->entities_base = push_array(rd_state->entities_arena, RD_Entity, 0);
  rd_state->entities_count = 0;
  rd_state->entities_root = rd_entity_alloc(&rd_nil_entity, RD_EntityKind_Root);
  rd_state->popup_arena = arena_alloc();
  rd_state->ctx_menu_key = ui_key_from_string(ui_key_zero(), str8_lit("top_level_ctx_menu"));
  rd_state->drop_completion_key = ui_key_from_string(ui_key_zero(), str8_lit("drop_completion_ctx_menu"));
  rd_state->string_search_arena = arena_alloc();
  rd_state->bind_change_arena = arena_alloc();
  rd_state->drag_drop_arena = arena_alloc();
  rd_state->drag_drop_regs = push_array(rd_state->drag_drop_arena, RD_Regs, 1);
  rd_state->top_regs = &rd_state->base_regs;
  
  // rjf: set up schemas
  {
    U64 schemas_count = ArrayCount(rd_name_schema_info_table);
    rd_state->schemas = push_array(rd_state->arena, MD_Node *, schemas_count);
    for EachIndex(idx, schemas_count)
    {
      rd_state->schemas[idx] = md_tree_from_string(rd_state->arena, rd_name_schema_info_table[idx].schema)->first;
    }
  }
  
  // rjf: set up top-level config entity trees
  {
    rd_state->root_cfg = rd_cfg_alloc();
    RD_Cfg *user_tree         = rd_cfg_new(rd_state->root_cfg, str8_lit("user"));
    RD_Cfg *project_tree      = rd_cfg_new(rd_state->root_cfg, str8_lit("project"));
    RD_Cfg *command_line_tree = rd_cfg_new(rd_state->root_cfg, str8_lit("command_line"));
    RD_Cfg *transient         = rd_cfg_new(rd_state->root_cfg, str8_lit("transient"));
  }
  
  // rjf: set up window cache
  {
    rd_state->window_state_slots_count = 64;
    rd_state->window_state_slots = push_array(arena, RD_WindowStateSlot, rd_state->window_state_slots_count);
    rd_state->first_window_state = rd_state->last_window_state = &rd_nil_window_state;
  }
  
  // rjf: set up view cache
  {
    rd_state->view_state_slots_count = 4096;
    rd_state->view_state_slots = push_array(arena, RD_ViewStateSlot, rd_state->view_state_slots_count);
  }
  
  // rjf: set up user / project paths
  {
    Temp scratch = scratch_begin(0, 0);
    
    // rjf: unpack command line arguments
    String8 user_path = cmd_line_string(cmdln, str8_lit("user"));
    String8 project_path = cmd_line_string(cmdln, str8_lit("project"));
    {
      String8 user_program_data_path = os_get_process_info()->user_program_data_path;
      String8 user_data_folder = push_str8f(scratch.arena, "%S/%S", user_program_data_path, str8_lit("raddbg"));
      os_make_directory(user_data_folder);
      if(user_path.size == 0)
      {
        user_path = push_str8f(scratch.arena, "%S/default.raddbg_user", user_data_folder);
      }
      if(project_path.size == 0)
      {
        project_path = push_str8f(scratch.arena, "%S/default.raddbg_project", user_data_folder);
      }
    }
    
    // rjf: do initial load
    rd_cmd(RD_CmdKind_OpenUser,    .file_path = user_path);
    rd_cmd(RD_CmdKind_OpenProject, .file_path = project_path);
    
    scratch_end(scratch);
  }
  
  // rjf: unpack icon image data
  {
    Temp scratch = scratch_begin(0, 0);
    String8 data = rd_icon_file_bytes;
    U8 *ptr = data.str;
    U8 *opl = ptr+data.size;
    
    // rjf: read header
#pragma pack(push, 1)
    typedef struct ICO_Header ICO_Header;
    struct ICO_Header
    {
      U16 reserved_padding; // must be 0
      U16 image_type; // if 1 -> ICO, if 2 -> CUR
      U16 num_images;
    };
    typedef struct ICO_Entry ICO_Entry;
    struct ICO_Entry
    {
      U8 image_width_px;
      U8 image_height_px;
      U8 num_colors;
      U8 reserved_padding; // should be 0
      union
      {
        U16 ico_color_planes; // in ICO
        U16 cur_hotspot_x_px; // in CUR
      };
      union
      {
        U16 ico_bits_per_pixel; // in ICO
        U16 cur_hotspot_y_px;   // in CUR
      };
      U32 image_data_size;
      U32 image_data_off;
    };
#pragma pack(pop)
    ICO_Header hdr = {0};
    if(ptr+sizeof(hdr) < opl)
    {
      MemoryCopy(&hdr, ptr, sizeof(hdr));
      ptr += sizeof(hdr);
    }
    
    // rjf: read image entries
    U64 entries_count = hdr.num_images;
    ICO_Entry *entries = push_array(scratch.arena, ICO_Entry, hdr.num_images);
    {
      U64 bytes_to_read = sizeof(ICO_Entry)*entries_count;
      bytes_to_read = Min(bytes_to_read, opl-ptr);
      MemoryCopy(entries, ptr, bytes_to_read);
      ptr += bytes_to_read;
    }
    
    // rjf: find largest image
    ICO_Entry *best_entry = 0;
    U64 best_entry_area = 0;
    for(U64 idx = 0; idx < entries_count; idx += 1)
    {
      ICO_Entry *entry = &entries[idx];
      U64 width = entry->image_width_px;
      if(width == 0) { width = 256; }
      U64 height = entry->image_height_px;
      if(height == 0) { height = 256; }
      U64 entry_area = width*height;
      if(entry_area > best_entry_area)
      {
        best_entry = entry;
        best_entry_area = entry_area;
      }
    }
    
    // rjf: deserialize raw image data from best entry's offset
    U8 *image_data = 0;
    Vec2S32 image_dim = {0};
    if(best_entry != 0)
    {
      U8 *file_data_ptr = data.str + best_entry->image_data_off;
      U64 file_data_size = best_entry->image_data_size;
      int width = 0;
      int height = 0;
      int components = 0;
      image_data = stbi_load_from_memory(file_data_ptr, file_data_size, &width, &height, &components, 4);
      image_dim.x = width;
      image_dim.y = height;
    }
    
    // rjf: upload to gpu texture
    rd_state->icon_texture = r_tex2d_alloc(R_ResourceKind_Static, image_dim, R_Tex2DFormat_RGBA8, image_data);
    
    // rjf: release
    stbi_image_free(image_data);
    scratch_end(scratch);
  }
  
  // rjf: set up initial browse path
  {
    Temp scratch = scratch_begin(0, 0);
    String8 current_path = os_get_current_path(scratch.arena);
    String8 current_path_with_slash = push_str8f(scratch.arena, "%S/", current_path);
    rd_state->current_path_arena = arena_alloc();
    rd_state->current_path = push_str8_copy(rd_state->current_path_arena, current_path_with_slash);
    scratch_end(scratch);
  }
  
  ProfEnd();
}

internal void
rd_frame(void)
{
  ProfBeginFunction();
  Temp scratch = scratch_begin(0, 0);
  local_persist S32 depth = 0;
  log_scope_begin();
  
  //- TODO(rjf): @cfg debugging: stringify the current cfg tree
  {
    String8 string = rd_string_from_cfg_tree(scratch.arena, rd_state->root_cfg);
    int x = 0;
  }
  
  //////////////////////////////
  //- rjf: do per-frame resets
  //
  arena_clear(rd_frame_arena());
  rd_state->top_regs = &rd_state->base_regs;
  rd_regs_copy_contents(rd_frame_arena(), &rd_state->top_regs->v, &rd_state->top_regs->v);
  if(rd_state->next_hover_regs != 0)
  {
    rd_state->hover_regs = rd_regs_copy(rd_frame_arena(), rd_state->next_hover_regs);
    rd_state->hover_regs_slot = rd_state->next_hover_regs_slot;
    rd_state->next_hover_regs = 0;
  }
  else
  {
    rd_state->hover_regs = push_array(rd_frame_arena(), RD_Regs, 1);
    rd_state->hover_regs_slot = RD_RegSlot_Null;
  }
  if(depth == 0)
  {
    rd_state->frame_di_scope = di_scope_open();
  }
  B32 allow_text_hotkeys = !rd_state->text_edit_mode;
  rd_state->text_edit_mode = 0;
  rd_state->ctrl_entity_meval_cache_slots_count = 1024;
  rd_state->ctrl_entity_meval_cache_slots = push_array(rd_frame_arena(), RD_CtrlEntityMetaEvalCacheSlot, rd_state->ctrl_entity_meval_cache_slots_count);
  rd_state->cfg2evalblob_map = push_array(rd_frame_arena(), RD_Cfg2EvalBlobMap, 1);
  rd_state->cfg2evalblob_map->slots_count = 256;
  rd_state->cfg2evalblob_map->slots = push_array(rd_frame_arena(), RD_Cfg2EvalBlobSlot, rd_state->cfg2evalblob_map->slots_count);
  rd_state->entity2evalblob_map = push_array(rd_frame_arena(), RD_Entity2EvalBlobMap, 1);
  rd_state->entity2evalblob_map->slots_count = 256;
  rd_state->entity2evalblob_map->slots = push_array(rd_frame_arena(), RD_Entity2EvalBlobSlot, rd_state->entity2evalblob_map->slots_count);
  
  //////////////////////////////
  //- rjf: iterate all tabs, touch their view-states
  //
  if(depth == 0)
  {
    Temp scratch = scratch_begin(0, 0);
    RD_CfgList windows = rd_cfg_top_level_list_from_string(scratch.arena, str8_lit("window"));
    for(RD_CfgNode *n = windows.first; n != 0; n = n->next)
    {
      RD_Cfg *window = n->v;
      RD_PanelTree panel_tree = rd_panel_tree_from_cfg(scratch.arena, window);
      for(RD_PanelNode *p = panel_tree.root; p != &rd_nil_panel_node; p = rd_panel_node_rec__depth_first_pre(panel_tree.root, p).next)
      {
        for(RD_CfgNode *n = p->tabs.first; n != 0; n = n->next)
        {
          RD_Cfg *tab = n->v;
          if(rd_cfg_is_project_filtered(tab))
          {
            continue;
          }
          rd_view_state_from_cfg(tab);
        }
      }
    }
    scratch_end(scratch);
  }
  
  //////////////////////////////
  //- rjf: garbage collect untouched immediate cfg trees
  //
  if(depth == 0)
  {
    RD_Cfg *transient = rd_cfg_child_from_string(rd_state->root_cfg, str8_lit("transient"));
    for(RD_Cfg *tln = transient->first, *next = &rd_nil_cfg; tln != &rd_nil_cfg; tln = next)
    {
      next = tln->next;
      if(str8_match(tln->string, str8_lit("immediate"), 0))
      {
        if(rd_cfg_child_from_string(tln, str8_lit("hot")) == &rd_nil_cfg)
        {
          rd_cfg_release(tln);
        }
      }
    }
    for(RD_Cfg *tln = transient->first; tln != &rd_nil_cfg; tln = tln->next)
    {
      if(str8_match(tln->string, str8_lit("immediate"), 0))
      {
        rd_cfg_release(rd_cfg_child_from_string(tln, str8_lit("hot")));
      }
    }
  }
  
  //////////////////////////////
  //- rjf: garbage collect untouched window states
  //
  if(depth == 0) DeferLoop(depth += 1, depth -= 1)
  {
    for EachIndex(slot_idx, rd_state->window_state_slots_count)
    {
      for(RD_WindowState *ws = rd_state->window_state_slots[slot_idx].first, *next; ws != 0; ws = next)
      {
        next = ws->hash_next;
        if(ws->last_frame_index_touched+2 < rd_state->frame_index)
        {
          ui_state_release(ws->ui);
          r_window_unequip(ws->os, ws->r);
          os_window_close(ws->os);
          if(ws->autocomp_lister != 0)
          {
            arena_release(ws->autocomp_lister->arena);
          }
          for(RD_Lister *lister = ws->top_query_lister; lister != 0; lister = lister->next)
          {
            arena_release(lister->arena);
          }
          arena_release(ws->ctx_menu_arena);
          arena_release(ws->drop_completion_arena);
          arena_release(ws->hover_eval_arena);
          arena_release(ws->arena);
          DLLRemove_NPZ(&rd_nil_window_state, rd_state->first_window_state, rd_state->last_window_state, ws, order_next, order_prev);
          DLLRemove_NP(rd_state->window_state_slots[slot_idx].first, rd_state->window_state_slots[slot_idx].last, ws, hash_next, hash_prev);
          SLLStackPush_N(rd_state->free_window_state, ws, order_next);
        }
      }
    }
  }
  
  //////////////////////////////
  //- rjf: garbage collect untouched view states
  //
  if(depth == 0)
  {
    for EachIndex(slot_idx, rd_state->view_state_slots_count)
    {
      for(RD_ViewState *vs = rd_state->view_state_slots[slot_idx].first, *next; vs != 0; vs = next)
      {
        next = vs->hash_next;
        if(vs->last_frame_index_touched+2 < rd_state->frame_index)
        {
          ev_view_release(vs->ev_view);
          for(RD_ArenaExt *ext = vs->first_arena_ext; ext != 0; ext = ext->next)
          {
            arena_release(ext->arena);
          }
          arena_release(vs->arena);
          DLLRemove_NP(rd_state->view_state_slots[slot_idx].first, rd_state->view_state_slots[slot_idx].last, vs, hash_next, hash_prev);
          SLLStackPush_N(rd_state->free_view_state, vs, hash_next);
        }
      }
    }
  }
  
  //////////////////////////////
  //- rjf: sync with di parsers
  //
  ProfScope("sync with di parsers")
  {
    DI_EventList events = di_p2u_pop_events(scratch.arena, 0);
    for(DI_EventNode *n = events.first; n != 0; n = n->next)
    {
      DI_Event *event = &n->v;
      switch(event->kind)
      {
        default:{}break;
        case DI_EventKind_ConversionStarted:
        {
          RD_Cfg *root = rd_cfg_child_from_string(rd_state->root_cfg, str8_lit("transient"));
          RD_Cfg *task = rd_cfg_new(root, str8_lit("conversion_task"));
          rd_cfg_new(task, event->string);
        }break;
        case DI_EventKind_ConversionEnded:
        {
          RD_Cfg *root = rd_cfg_child_from_string(rd_state->root_cfg, str8_lit("transient"));
          for(RD_Cfg *tln = root->first; tln != &rd_nil_cfg; tln = tln->next)
          {
            if(str8_match(tln->string, str8_lit("conversion_task"), 0) && str8_match(tln->first->string, event->string, 0))
            {
              rd_cfg_release(tln);
              break;
            }
          }
        }break;
      }
    }
  }
  
  //////////////////////////////
  //- rjf: animate all views
  //
  if(depth == 0)
  {
    F32 slow_rate = 1 - pow_f32(2, (-10.f * rd_state->frame_dt));
    F32 fast_rate = 1 - pow_f32(2, (-40.f * rd_state->frame_dt));
    for EachIndex(slot_idx, rd_state->view_state_slots_count)
    {
      for(RD_ViewState *vs = rd_state->view_state_slots[slot_idx].first;
          vs != 0;
          vs = vs->hash_next)
      {
        F32 scroll_x_diff = (-vs->scroll_pos.x.off);
        F32 scroll_y_diff = (-vs->scroll_pos.y.off);
        F32 loading_t_diff = (vs->loading_t_target - vs->loading_t);
        vs->scroll_pos.x.off += scroll_x_diff*fast_rate;
        vs->scroll_pos.y.off += scroll_y_diff*fast_rate;
        vs->loading_t += loading_t_diff * slow_rate;
        if(abs_f32(loading_t_diff) > 0.01f ||
           abs_f32(scroll_x_diff) > 0.01f ||
           abs_f32(scroll_y_diff) > 0.01f)
        {
          rd_request_frame();
        }
        if(abs_f32(scroll_x_diff) <= 0.01f)
        {
          vs->scroll_pos.x.off = 0;
        }
        if(abs_f32(scroll_y_diff) <= 0.01f)
        {
          vs->scroll_pos.y.off = 0;
        }
        RD_Cfg *vcfg = rd_cfg_from_handle(vs->cfg_handle);
        if(rd_cfg_child_from_string(vcfg, str8_lit("selected")) != &rd_nil_cfg)
        {
          vs->loading_t_target = 0;
        }
      }
    }
  }
  
  //////////////////////////////
  //- rjf: get events from the OS
  //
  OS_EventList events = {0};
  if(depth == 0) DeferLoop(depth += 1, depth -= 1)
  {
    events = os_get_events(scratch.arena, rd_state->num_frames_requested == 0);
  }
  
  //////////////////////////////
  //- rjf: pick target hz
  //
  // TODO(rjf): maximize target, given all windows and their monitors
  F32 target_hz = os_get_gfx_info()->default_refresh_rate;
  if(rd_state->frame_index > 32)
  {
    // rjf: calculate average frame time out of the last N
    U64 num_frames_in_history = Min(ArrayCount(rd_state->frame_time_us_history), rd_state->frame_index);
    U64 frame_time_history_sum_us = 0;
    for(U64 idx = 0; idx < num_frames_in_history; idx += 1)
    {
      frame_time_history_sum_us += rd_state->frame_time_us_history[idx];
    }
    U64 frame_time_history_avg_us = frame_time_history_sum_us/num_frames_in_history;
    
    // rjf: pick among a number of sensible targets to snap to, given how well
    // we've been performing
    F32 possible_alternate_hz_targets[] = {target_hz, 60.f, 120.f, 144.f, 240.f};
    F32 best_target_hz = target_hz;
    S64 best_target_hz_frame_time_us_diff = max_S64;
    for(U64 idx = 0; idx < ArrayCount(possible_alternate_hz_targets); idx += 1)
    {
      F32 candidate = possible_alternate_hz_targets[idx];
      if(candidate <= target_hz)
      {
        U64 candidate_frame_time_us = 1000000/(U64)candidate;
        S64 frame_time_us_diff = (S64)frame_time_history_avg_us - (S64)candidate_frame_time_us;
        if(abs_s64(frame_time_us_diff) < best_target_hz_frame_time_us_diff)
        {
          best_target_hz = candidate;
          best_target_hz_frame_time_us_diff = frame_time_us_diff;
        }
      }
    }
    target_hz = best_target_hz;
  }
  
  //////////////////////////////
  //- rjf: target Hz -> delta time
  //
  rd_state->frame_dt = 1.f/target_hz;
  
  //////////////////////////////
  //- rjf: begin measuring actual per-frame work
  //
  U64 begin_time_us = os_now_microseconds();
  
  //////////////////////////////
  //- rjf: bind change
  //
  if(!rd_state->popup_active && rd_state->bind_change_active)
  {
    if(os_key_press(&events, os_handle_zero(), 0, OS_Key_Esc))
    {
      rd_request_frame();
      rd_state->bind_change_active = 0;
    }
    if(os_key_press(&events, os_handle_zero(), 0, OS_Key_Delete))
    {
      rd_request_frame();
      // TODO(rjf): @cfg_bindchange rd_unbind_name(rd_state->bind_change_cmd_name, rd_state->bind_change_binding);
      rd_state->bind_change_active = 0;
    }
    for(OS_Event *event = events.first, *next = 0; event != 0; event = next)
    {
      if(event->kind == OS_EventKind_Press &&
         event->key != OS_Key_Esc &&
         event->key != OS_Key_Return &&
         event->key != OS_Key_Backspace &&
         event->key != OS_Key_Delete &&
         event->key != OS_Key_LeftMouseButton &&
         event->key != OS_Key_RightMouseButton &&
         event->key != OS_Key_MiddleMouseButton &&
         event->key != OS_Key_Ctrl &&
         event->key != OS_Key_Alt &&
         event->key != OS_Key_Shift)
      {
        rd_state->bind_change_active = 0;
        RD_Binding binding = zero_struct;
        {
          binding.key = event->key;
          binding.modifiers = event->modifiers;
        }
        // TODO(rjf): @cfg_bindchange rd_unbind_name(rd_state->bind_change_cmd_name, rd_state->bind_change_binding);
        // TODO(rjf): @cfg_bindchange rd_bind_name(rd_state->bind_change_cmd_name, binding);
        U32 codepoint = os_codepoint_from_modifiers_and_key(event->modifiers, event->key);
        os_text(&events, event->window, codepoint);
        os_eat_event(&events, event);
        rd_request_frame();
        break;
      }
    }
  }
  
  //////////////////////////////
  //- rjf: build key map from config
  //
  ProfScope("build key map from config")
  {
    //- rjf: set up table
    rd_state->key_map = push_array(rd_frame_arena(), RD_KeyMap, 1);
    RD_KeyMap *key_map = rd_state->key_map;
    key_map->name_slots_count = 4096;
    key_map->name_slots = push_array(rd_frame_arena(), RD_KeyMapSlot, key_map->name_slots_count);
    key_map->binding_slots_count = 4096;
    key_map->binding_slots = push_array(rd_frame_arena(), RD_KeyMapSlot, key_map->binding_slots_count);
    
    //- rjf: gather & parse all explicitly stored keybinding sets
    RD_CfgList keybindings_cfg_list = rd_cfg_top_level_list_from_string(scratch.arena, str8_lit("keybindings"));
    for(RD_CfgNode *n = keybindings_cfg_list.first; n != 0; n = n->next)
    {
      RD_Cfg *keybindings_root = n->v;
      for(RD_Cfg *keybinding = keybindings_root->first; keybinding != &rd_nil_cfg; keybinding = keybinding->next)
      {
        String8 name = {0};
        RD_Binding binding = {0};
        for(RD_Cfg *child = keybinding->first; child != &rd_nil_cfg; child = child->next)
        {
          if(0){}
          else if(str8_match(child->string, str8_lit("ctrl"), 0))   { binding.modifiers |= OS_Modifier_Ctrl; }
          else if(str8_match(child->string, str8_lit("alt"), 0))    { binding.modifiers |= OS_Modifier_Alt; }
          else if(str8_match(child->string, str8_lit("shift"), 0))  { binding.modifiers |= OS_Modifier_Shift; }
          else
          {
            OS_Key key = OS_Key_Null;
            for EachEnumVal(OS_Key, k)
            {
              if(str8_match(child->string, os_g_key_cfg_string_table[k], StringMatchFlag_CaseInsensitive))
              {
                key = k;
                break;
              }
            }
            if(key != OS_Key_Null)
            {
              binding.key = key;
            }
            else
            {
              name = child->string;
              for(U64 idx = 0; idx < ArrayCount(rd_binding_version_remap_old_name_table); idx += 1)
              {
                if(str8_match(rd_binding_version_remap_old_name_table[idx], name, StringMatchFlag_CaseInsensitive))
                {
                  name = rd_binding_version_remap_new_name_table[idx];
                }
              }
            }
          }
        }
        if(name.size != 0)
        {
          U64 name_hash = d_hash_from_string(name);
          U64 binding_hash = d_hash_from_string(str8_struct(&binding));
          U64 name_slot_idx = name_hash%key_map->name_slots_count;
          U64 binding_slot_idx = binding_hash%key_map->binding_slots_count;
          RD_KeyMapNode *n = push_array(rd_frame_arena(), RD_KeyMapNode, 1);
          n->cfg = keybinding;
          n->name = push_str8_copy(rd_frame_arena(), name);
          n->binding = binding;
          SLLQueuePush_N(key_map->name_slots[name_slot_idx].first, key_map->name_slots[name_slot_idx].last, n, name_hash_next);
          SLLQueuePush_N(key_map->binding_slots[binding_slot_idx].first, key_map->binding_slots[binding_slot_idx].last, n, binding_hash_next);
        }
      }
    }
    
    //- rjf: iterate default bindings - if their commands are not found in the
    // map, then use the default binding.
    //
    // TODO(rjf): @dynamic_cmds
    //
    for EachElement(idx, rd_default_binding_table)
    {
      String8 name = rd_default_binding_table[idx].string;
      B32 name_was_mapped = 0;
      U64 name_hash = d_hash_from_string(name);
      U64 name_slot_idx = name_hash%key_map->name_slots_count;
      for(RD_KeyMapNode *n = key_map->name_slots[name_slot_idx].first; n != 0; n = n->name_hash_next)
      {
        if(str8_match(n->name, name, 0))
        {
          name_was_mapped = 1;
          break;
        }
      }
      if(!name_was_mapped)
      {
        RD_Binding binding = rd_default_binding_table[idx].binding;
        U64 binding_hash = d_hash_from_string(str8_struct(&binding));
        U64 binding_slot_idx = binding_hash%key_map->binding_slots_count;
        RD_KeyMapNode *n = push_array(rd_frame_arena(), RD_KeyMapNode, 1);
        n->cfg = &rd_nil_cfg;
        n->name = push_str8_copy(rd_frame_arena(), name);
        n->binding = binding;
        SLLQueuePush_N(key_map->name_slots[name_slot_idx].first, key_map->name_slots[name_slot_idx].last, n, name_hash_next);
        SLLQueuePush_N(key_map->binding_slots[binding_slot_idx].first, key_map->binding_slots[binding_slot_idx].last, n, binding_hash_next);
      }
    }
  }
  
  //////////////////////////////
  //- rjf: build theme from config
  //
  ProfScope("build theme from config")
  {
    rd_state->theme_target = push_array(rd_frame_arena(), RD_Theme, 1);
    RD_Theme *theme = rd_state->theme_target;
    
    //- rjf: gather globally-applying config options
    RD_CfgList preset_roots = rd_cfg_top_level_list_from_string(scratch.arena, str8_lit("color_preset"));
    RD_CfgList colors_roots = rd_cfg_top_level_list_from_string(scratch.arena, str8_lit("colors"));
    
    //- rjf: assume default-dark
    MemoryCopy(theme->colors, rd_theme_preset_colors_table[RD_ThemePreset_DefaultDark], sizeof(rd_theme_preset_colors__default_dark));
    
    //- rjf: apply explicitly-specified presets
    for(RD_CfgNode *n = preset_roots.first; n != 0; n = n->next)
    {
      RD_Cfg *preset = n->v;
      String8 preset_name = preset->first->string;
      RD_ThemePreset preset_kind = (RD_ThemePreset)0;
      B32 found_preset_kind = 0;
      for(RD_ThemePreset p = (RD_ThemePreset)0; p < RD_ThemePreset_COUNT; p = (RD_ThemePreset)(p+1))
      {
        if(str8_match(preset_name, rd_theme_preset_code_string_table[p], StringMatchFlag_CaseInsensitive))
        {
          found_preset_kind = 1;
          preset_kind = p;
          break;
        }
      }
      if(found_preset_kind)
      {
        MemoryCopy(theme->colors, rd_theme_preset_colors_table[preset_kind], sizeof(rd_theme_preset_colors__default_dark));
      }
    }
    
    //- rjf: apply explicitly-specified color codes
    for(RD_CfgNode *n = colors_roots.first; n != 0; n = n->next)
    {
      RD_Cfg *colors = n->v;
      for(RD_Cfg *color = colors->first; color != &rd_nil_cfg; color = color->next)
      {
        String8 name = color->string;
        RD_ThemeColor color_code = RD_ThemeColor_Null;
        for(RD_ThemeColor c = RD_ThemeColor_Null; c < RD_ThemeColor_COUNT; c = (RD_ThemeColor)(c+1))
        {
          if(str8_match(rd_theme_color_cfg_string_table[c], name, StringMatchFlag_CaseInsensitive))
          {
            color_code = c;
            break;
          }
        }
        if(color_code != RD_ThemeColor_Null)
        {
          U64 color_val = 0;
          if(try_u64_from_str8_c_rules(color->first->string, &color_val))
          {
            Vec4F32 color_rgba = rgba_from_u32((U32)color_val);
            theme->colors[color_code] = color_rgba;
          }
        }
      }
    }
  }
  
  //////////////////////////////
  //- rjf: animate theme
  //
  {
    RD_Theme *last = rd_state->theme;
    rd_state->theme = push_array(rd_frame_arena(), RD_Theme, 1);
    RD_Theme *current = rd_state->theme;
    RD_Theme *target = rd_state->theme_target;
    if(last)
    {
      MemoryCopyStruct(current, last);
    }
    if(rd_state->frame_index <= 2)
    {
      MemoryCopyStruct(current, target);
    }
    else
    {
      F32 rate = 1 - pow_f32(2, (-50.f * rd_state->frame_dt));
      for(RD_ThemeColor color = RD_ThemeColor_Null;
          color < RD_ThemeColor_COUNT;
          color = (RD_ThemeColor)(color+1))
      {
        if(abs_f32(target->colors[color].x - current->colors[color].x) > 0.01f ||
           abs_f32(target->colors[color].y - current->colors[color].y) > 0.01f ||
           abs_f32(target->colors[color].z - current->colors[color].z) > 0.01f ||
           abs_f32(target->colors[color].w - current->colors[color].w) > 0.01f)
        {
          rd_request_frame();
        }
        current->colors[color].x += (target->colors[color].x - current->colors[color].x) * rate;
        current->colors[color].y += (target->colors[color].y - current->colors[color].y) * rate;
        current->colors[color].z += (target->colors[color].z - current->colors[color].z) * rate;
        current->colors[color].w += (target->colors[color].w - current->colors[color].w) * rate;
      }
    }
  }
  
  //////////////////////////////
  //- rjf: consume events
  //
  ProfScope("consume events")
  {
    for(OS_Event *event = events.first, *next = 0;
        event != 0;
        event = next)
      RD_RegsScope()
    {
      next = event->next;
      RD_WindowState *ws = rd_window_state_from_os_handle(event->window);
      if(ws != 0 && ws != rd_window_state_from_cfg(rd_cfg_from_handle(rd_regs()->window)))
      {
        Temp scratch = scratch_begin(0, 0);
        RD_PanelTree panel_tree = rd_panel_tree_from_cfg(scratch.arena, rd_cfg_from_handle(ws->cfg_handle));
        rd_regs()->window = ws->cfg_handle;
        rd_regs()->panel  = rd_handle_from_cfg(panel_tree.focused->cfg);
        rd_regs()->view   = rd_handle_from_cfg(panel_tree.focused->selected_tab);
        scratch_end(scratch);
      }
      B32 take = 0;
      
      //- rjf: try drag/drop drop-kickoff
      if(rd_drag_is_active() && event->kind == OS_EventKind_Release && event->key == OS_Key_LeftMouseButton)
      {
        rd_state->drag_drop_state = RD_DragDropState_Dropping;
      }
      
      //- rjf: try window close
      if(!take && event->kind == OS_EventKind_WindowClose && ws != 0)
      {
        take = 1;
        rd_cmd(RD_CmdKind_Exit);
      }
      
      //- rjf: try menu bar operations
      {
        if(!take && event->kind == OS_EventKind_Press && event->key == OS_Key_Alt && event->modifiers == 0 && event->is_repeat == 0)
        {
          take = 1;
          rd_request_frame();
          ws->menu_bar_focused_on_press = ws->menu_bar_focused;
          ws->menu_bar_key_held = 1;
          ws->menu_bar_focus_press_started = 1;
        }
        if(!take && event->kind == OS_EventKind_Release && event->key == OS_Key_Alt && event->modifiers == 0 && event->is_repeat == 0)
        {
          take = 1;
          rd_request_frame();
          ws->menu_bar_key_held = 0;
        }
        if(ws->menu_bar_focused && event->kind == OS_EventKind_Press && event->key == OS_Key_Alt && event->modifiers == 0 && event->is_repeat == 0)
        {
          take = 1;
          rd_request_frame();
          ws->menu_bar_focused = 0;
        }
        else if(ws->menu_bar_focus_press_started && !ws->menu_bar_focused && event->kind == OS_EventKind_Release && event->modifiers == 0 && event->key == OS_Key_Alt && event->is_repeat == 0)
        {
          take = 1;
          rd_request_frame();
          ws->menu_bar_focused = !ws->menu_bar_focused_on_press;
          ws->menu_bar_focus_press_started = 0;
        }
        else if(event->kind == OS_EventKind_Press && event->key == OS_Key_Esc && ws->menu_bar_focused && !ui_any_ctx_menu_is_open())
        {
          take = 1;
          rd_request_frame();
          ws->menu_bar_focused = 0;
        }
      }
      
      //- rjf: try hotkey presses
      if(!take && event->kind == OS_EventKind_Press)
      {
        RD_Binding binding = {event->key, event->modifiers};
        RD_KeyMapNodePtrList key_map_nodes = rd_key_map_node_ptr_list_from_binding(scratch.arena, binding);
        if(key_map_nodes.first != 0)
        {
          U32 hit_char = os_codepoint_from_modifiers_and_key(event->modifiers, event->key);
          if(hit_char == 0 || allow_text_hotkeys)
          {
            rd_cmd(RD_CmdKind_RunCommand, .cmd_name = key_map_nodes.first->v->name);
            if(allow_text_hotkeys)
            {
              os_text(&events, event->window, hit_char);
              next = event->next;
            }
            take = 1;
            if(event->modifiers & OS_Modifier_Alt)
            {
              ws->menu_bar_focus_press_started = 0;
            }
          }
        }
        else if(OS_Key_F1 <= event->key && event->key <= OS_Key_F19)
        {
          ws->menu_bar_focus_press_started = 0;
        }
        rd_request_frame();
      }
      
      //- rjf: try text events
      if(!take && event->kind == OS_EventKind_Text)
      {
        String32 insertion32 = str32(&event->character, 1);
        String8 insertion8 = str8_from_32(scratch.arena, insertion32);
        rd_cmd(RD_CmdKind_InsertText, .string = insertion8);
        rd_request_frame();
        take = 1;
        if(event->modifiers & OS_Modifier_Alt)
        {
          ws->menu_bar_focus_press_started = 0;
        }
      }
      
      //- rjf: do fall-through
      if(!take)
      {
        take = 1;
        rd_cmd(RD_CmdKind_OSEvent, .os_event = event);
      }
      
      //- rjf: take
      if(take)
      {
        os_eat_event(&events, event);
      }
    }
  }
  
  //////////////////////////////
  //- rjf: loop - consume events in core, tick engine, and repeat
  //
  CTRL_Handle find_thread_retry = {0};
  RD_Cmd *cmd = 0;
  ProfScope("loop - consume events in core, tick engine, and repeat") for(U64 cmd_process_loop_idx = 0; cmd_process_loop_idx < 3; cmd_process_loop_idx += 1)
  {
    ////////////////////////////
    //- rjf: unpack eval-dependent info
    //
    ProfBegin("unpack eval-dependent info");
    CTRL_Entity *process = ctrl_entity_from_handle(d_state->ctrl_entity_store, rd_regs()->process);
    CTRL_Entity *thread = ctrl_entity_from_handle(d_state->ctrl_entity_store, rd_regs()->thread);
    Arch arch = thread->arch;
    U64 unwind_count = rd_regs()->unwind_count;
    U64 rip_vaddr = d_query_cached_rip_from_thread_unwind(thread, unwind_count);
    CTRL_Unwind unwind = d_query_cached_unwind_from_thread(thread);
    CTRL_Entity *module = ctrl_module_from_process_vaddr(process, rip_vaddr);
    U64 rip_voff = ctrl_voff_from_vaddr(module, rip_vaddr);
    U64 tls_root_vaddr = ctrl_query_cached_tls_root_vaddr_from_thread(d_state->ctrl_entity_store, thread->handle);
    CTRL_EntityList all_modules = ctrl_entity_list_from_kind(d_state->ctrl_entity_store, CTRL_EntityKind_Module);
    U64 eval_modules_count = Max(1, all_modules.count);
    E_Module *eval_modules = push_array(scratch.arena, E_Module, eval_modules_count);
    E_Module *eval_modules_primary = &eval_modules[0];
    eval_modules_primary->rdi = &di_rdi_parsed_nil;
    eval_modules_primary->vaddr_range = r1u64(0, max_U64);
    DI_Key primary_dbgi_key = {0};
    ProfScope("produce all eval modules")
    {
      U64 eval_module_idx = 0;
      for(CTRL_EntityNode *n = all_modules.first; n != 0; n = n->next, eval_module_idx += 1)
      {
        CTRL_Entity *m = n->v;
        DI_Key dbgi_key = ctrl_dbgi_key_from_module(m);
        eval_modules[eval_module_idx].arch        = m->arch;
        eval_modules[eval_module_idx].rdi         = di_rdi_from_key(rd_state->frame_di_scope, &dbgi_key, 0);
        eval_modules[eval_module_idx].vaddr_range = m->vaddr_range;
        eval_modules[eval_module_idx].space       = rd_eval_space_from_ctrl_entity(ctrl_entity_ancestor_from_kind(m, CTRL_EntityKind_Process), RD_EvalSpaceKind_CtrlEntity);
        if(module == m)
        {
          eval_modules_primary = &eval_modules[eval_module_idx];
          primary_dbgi_key = dbgi_key;
        }
      }
    }
    ProfEnd();
    
    ////////////////////////////
    //- rjf: build eval type context
    //
    E_TypeCtx *type_ctx = push_array(scratch.arena, E_TypeCtx, 1);
    ProfScope("build eval type context")
    {
      E_TypeCtx *ctx = type_ctx;
      ctx->ip_vaddr          = rip_vaddr;
      ctx->ip_voff           = rip_voff;
      ctx->modules           = eval_modules;
      ctx->modules_count     = eval_modules_count;
      ctx->primary_module    = eval_modules_primary;
    }
    e_select_type_ctx(type_ctx);
    
    ////////////////////////////
    //- rjf: build eval parse context
    //
    E_ParseCtx *parse_ctx = push_array(scratch.arena, E_ParseCtx, 1);
    ProfScope("build eval parse context")
    {
      E_ParseCtx *ctx = parse_ctx;
      ctx->ip_vaddr          = rip_vaddr;
      ctx->ip_voff           = rip_voff;
      ctx->ip_thread_space   = rd_eval_space_from_ctrl_entity(thread, RD_EvalSpaceKind_CtrlEntity);
      ctx->modules           = eval_modules;
      ctx->modules_count     = eval_modules_count;
      ctx->primary_module    = eval_modules_primary;
      ctx->regs_map      = ctrl_string2reg_from_arch(ctx->primary_module->arch);
      ctx->reg_alias_map = ctrl_string2alias_from_arch(ctx->primary_module->arch);
      ctx->locals_map    = d_query_cached_locals_map_from_dbgi_key_voff(&primary_dbgi_key, rip_voff);
      ctx->member_map    = d_query_cached_member_map_from_dbgi_key_voff(&primary_dbgi_key, rip_voff);
    }
    e_select_parse_ctx(parse_ctx);
    
    ////////////////////////////
    //- rjf: build eval IR context
    //
    E_TypeKey meta_eval_type_key = e_type_key_cons_base(type(CTRL_MetaEval));
    E_IRCtx *ir_ctx = push_array(scratch.arena, E_IRCtx, 1);
    if(e_ir_state != 0) { e_ir_state->ctx = 0; }
    {
      E_IRCtx *ctx = ir_ctx;
      ctx->macro_map    = push_array(scratch.arena, E_String2ExprMap, 1);
      ctx->macro_map[0] = e_string2expr_map_make(scratch.arena, 512);
      ctx->lookup_rule_map    = push_array(scratch.arena, E_LookupRuleMap, 1);
      ctx->lookup_rule_map[0] = e_lookup_rule_map_make(scratch.arena, 512);
      ctx->auto_hook_map      = push_array(scratch.arena, E_AutoHookMap, 1);
      ctx->auto_hook_map[0]   = e_auto_hook_map_make(scratch.arena, 512);
      
      //- rjf: choose set of evallable meta names
      String8 evallable_meta_names[] =
      {
        str8_lit("breakpoint"),
        str8_lit("watch_pin"),
        str8_lit("target"),
        str8_lit("file_path_map"),
        str8_lit("auto_view_rule"),
        str8_lit("machine"),
        str8_lit("process"),
        str8_lit("thread"),
        str8_lit("module"),
      };
      
      //- rjf: build special member types for evallable meta types
      E_TypeKey bool_type_key = {0};
      E_TypeKey u64_type_key = {0};
      E_TypeKey vaddr_range_type_key = {0};
      E_TypeKey code_string_type_key = {0};
      E_TypeKey path_type_key = {0};
      E_TypeKey string_type_key = {0};
      E_TypeKey location_type_key = {0};
      {
        E_MemberList vaddr_range_members_list = {0};
        e_member_list_push_new(scratch.arena, &vaddr_range_members_list, .type_key = e_type_key_basic(E_TypeKind_U64), .name = str8_lit("min"), .off = 0);
        e_member_list_push_new(scratch.arena, &vaddr_range_members_list, .type_key = e_type_key_basic(E_TypeKind_U64), .name = str8_lit("max"), .off = 8);
        E_MemberArray vaddr_range_members = e_member_array_from_list(scratch.arena, &vaddr_range_members_list);
        bool_type_key        = e_type_key_basic(E_TypeKind_Bool);
        u64_type_key         = e_type_key_basic(E_TypeKind_U64);
        vaddr_range_type_key = e_type_key_cons(.kind = E_TypeKind_Struct, .name = str8_lit("vaddr_range"), .count = vaddr_range_members.count, .members = vaddr_range_members.v);
        code_string_type_key = e_type_key_cons_ptr(arch_from_context(), e_type_key_basic(E_TypeKind_U8), 1, E_TypeFlag_IsCodeText);
        path_type_key        = e_type_key_cons_ptr(arch_from_context(), e_type_key_basic(E_TypeKind_U8), 1, E_TypeFlag_IsPathText);
        string_type_key      = e_type_key_cons_ptr(arch_from_context(), e_type_key_basic(E_TypeKind_U8), 1, E_TypeFlag_IsPlainText);
        location_type_key    = e_type_key_cons_ptr(arch_from_context(), e_type_key_basic(E_TypeKind_U8), 1, E_TypeFlag_IsPlainText|E_TypeFlag_IsCodeText|E_TypeFlag_IsPathText);
      }
      
      //- rjf: build types for each evallable meta name
      struct
      {
        String8 schema_type_name;
        E_TypeKey type_key;
      }
      schema_type_name_key_map[] =
      {
        { str8_lit("bool"), bool_type_key },
        { str8_lit("u64"), u64_type_key },
        { str8_lit("vaddr_range"), vaddr_range_type_key },
        { str8_lit("code_string"), code_string_type_key },
        { str8_lit("path"), path_type_key },
        { str8_lit("string"), string_type_key },
        { str8_lit("location"), location_type_key },
      };
      E_TypeKey evallable_meta_types[ArrayCount(evallable_meta_names)] = {0};
      for EachElement(idx, evallable_meta_names)
      {
        String8 name = evallable_meta_names[idx];
        MD_Node *schema = rd_schema_from_name(scratch.arena, name);
        E_MemberList members_list = {0};
        U64 off = 0;
        for MD_EachNode(child, schema->first)
        {
          String8 member_name        = child->string;
          String8 member_pretty_name = rd_display_from_code_name(member_name);
          E_TypeKey member_type_key  = zero_struct;
          for EachElement(schema_type_name_idx, schema_type_name_key_map)
          {
            if(str8_match(child->first->string, schema_type_name_key_map[schema_type_name_idx].schema_type_name, 0))
            {
              member_type_key = schema_type_name_key_map[schema_type_name_idx].type_key;
              break;
            }
          }
          e_member_list_push_new(scratch.arena, &members_list,
                                 .type_key    = member_type_key,
                                 .name        = member_name,
                                 .pretty_name = member_pretty_name,
                                 .off         = off);
          off += e_type_byte_size_from_key(member_type_key);
        }
        E_MemberArray members = e_member_array_from_list(scratch.arena, &members_list);
        evallable_meta_types[idx] = e_type_key_cons(.name    = name,
                                                    .kind    = E_TypeKind_Struct,
                                                    .members = members.v,
                                                    .count   = members.count);
      }
      
      //- rjf: cache meta name -> type key correllation
      rd_state->meta_name2type_map = push_array(rd_frame_arena(), E_String2TypeKeyMap, 1);
      rd_state->meta_name2type_map[0] = e_string2typekey_map_make(rd_frame_arena(), 256);
      for EachElement(idx, evallable_meta_names)
      {
        String8 name = evallable_meta_names[idx];
        E_TypeKey type_key = evallable_meta_types[idx];
        e_string2typekey_map_insert(rd_frame_arena(), rd_state->meta_name2type_map, name, type_key);
      }
      
      //- rjf: add macros for evallable config trees
      String8 evallable_cfg_names[] =
      {
        str8_lit("breakpoint"),
        str8_lit("watch_pin"),
        str8_lit("target"),
        str8_lit("file_path_map"),
        str8_lit("auto_view_rule"),
      };
      for EachElement(idx, evallable_cfg_names)
      {
        String8 name = evallable_cfg_names[idx];
        RD_CfgList cfgs = rd_cfg_top_level_list_from_string(scratch.arena, name);
        for(RD_CfgNode *n = cfgs.first; n != 0; n = n->next)
        {
          RD_Cfg *cfg = n->v;
          String8 label = rd_cfg_child_from_string(cfg, str8_lit("label"))->first->string;
          String8 exe   = rd_cfg_child_from_string(cfg, str8_lit("executable"))->first->string;
          E_Space space = rd_eval_space_from_cfg(cfg);
          E_Expr *expr = e_push_expr(scratch.arena, E_ExprKind_LeafOffset, 0);
          expr->space    = space;
          expr->mode     = E_Mode_Offset;
          expr->type_key = e_string2typekey_map_lookup(rd_state->meta_name2type_map, name);
          if(exe.size != 0)
          {
            e_string2expr_map_insert(scratch.arena, ctx->macro_map, str8_skip_last_slash(exe), expr);
          }
          if(label.size != 0)
          {
            e_string2expr_map_insert(scratch.arena, ctx->macro_map, label, expr);
          }
        }
      }
      
      //- rjf: add macros for evallable control entities
      String8 evallable_ctrl_names[] =
      {
        str8_lit("machine"),
        str8_lit("process"),
        str8_lit("thread"),
        str8_lit("module"),
      };
      for EachElement(idx, evallable_ctrl_names)
      {
        String8 name = evallable_ctrl_names[idx];
        CTRL_EntityKind kind = ctrl_entity_kind_from_string(name);
        CTRL_EntityList list = ctrl_entity_list_from_kind(d_state->ctrl_entity_store, kind);
        E_TypeKey type_key = e_string2typekey_map_lookup(rd_state->meta_name2type_map, name);
        for(CTRL_EntityNode *n = list.first; n != 0; n = n->next)
        {
          CTRL_Entity *entity = n->v;
          E_Space space = rd_eval_space_from_ctrl_entity(entity, RD_EvalSpaceKind_MetaCtrlEntity);
          E_Expr *expr = e_push_expr(scratch.arena, E_ExprKind_LeafOffset, 0);
          expr->space    = space;
          expr->mode     = E_Mode_Offset;
          expr->type_key = type_key;
          if(entity->string.size != 0)
          {
            e_string2expr_map_insert(scratch.arena, ctx->macro_map, entity->string, expr);
          }
          if(kind == CTRL_EntityKind_Thread && ctrl_handle_match(rd_base_regs()->thread, entity->handle))
          {
            e_string2expr_map_insert(scratch.arena, ctx->macro_map, str8_lit("current_thread"), expr);
          }
          if(kind == CTRL_EntityKind_Process && ctrl_handle_match(rd_base_regs()->process, entity->handle))
          {
            e_string2expr_map_insert(scratch.arena, ctx->macro_map, str8_lit("current_process"), expr);
          }
          if(kind == CTRL_EntityKind_Module && ctrl_handle_match(rd_base_regs()->module, entity->handle))
          {
            e_string2expr_map_insert(scratch.arena, ctx->macro_map, str8_lit("current_module"), expr);
          }
        }
        e_auto_hook_map_insert_new(scratch.arena, ctx->auto_hook_map, .type_key = type_key, .tag_expr_string = name);
      }
      
      //- rjf: add macro for collections with specific lookup rules
      struct
      {
        String8 name;
        E_LookupInfoFunctionType *lookup_info;
        E_LookupRangeFunctionType *lookup_range;
      }
      collection_infos[] =
      {
#define Collection(name) {str8_lit_comp(#name), E_LOOKUP_INFO_FUNCTION_NAME(name), E_LOOKUP_RANGE_FUNCTION_NAME(name)}
        Collection(watches),
        Collection(locals),
        Collection(registers),
#undef Collection
      };
      for EachElement(idx, collection_infos)
      {
        String8 collection_name = collection_infos[idx].name;
        E_Expr *expr = e_push_expr(scratch.arena, E_ExprKind_LeafOffset, 0);
        expr->type_key = e_type_key_cons(.kind = E_TypeKind_Set, .name = collection_name);
        e_string2expr_map_insert(scratch.arena, ctx->macro_map, collection_name, expr);
        e_lookup_rule_map_insert_new(scratch.arena, ctx->lookup_rule_map, collection_name,
                                     .info   = collection_infos[idx].lookup_info,
                                     .range  = collection_infos[idx].lookup_range);
      }
      
      //- rjf: add macros for debug info table collections
      String8 debug_info_table_collection_names[] =
      {
        str8_lit_comp("procedures"),
        str8_lit_comp("thread_locals"),
        str8_lit_comp("globals"),
        str8_lit_comp("types"),
      };
      for EachElement(idx, debug_info_table_collection_names)
      {
        String8 name = debug_info_table_collection_names[idx];
        E_Expr *expr = e_push_expr(scratch.arena, E_ExprKind_LeafOffset, 0);
        expr->type_key = e_type_key_cons(.kind = E_TypeKind_Set, .name = name);
        e_string2expr_map_insert(scratch.arena, ctx->macro_map, name, expr);
        e_lookup_rule_map_insert_new(scratch.arena, ctx->lookup_rule_map, name,
                                     .info        = E_LOOKUP_INFO_FUNCTION_NAME(debug_info_table),
                                     .range       = E_LOOKUP_RANGE_FUNCTION_NAME(debug_info_table),
                                     .id_from_num = E_LOOKUP_ID_FROM_NUM_FUNCTION_NAME(debug_info_table),
                                     .num_from_id = E_LOOKUP_NUM_FROM_ID_FUNCTION_NAME(debug_info_table));
      }
      
      //- rjf: add macros for all config collections
      for EachElement(cfg_name_idx, evallable_cfg_names)
      {
        String8 cfg_name = evallable_cfg_names[cfg_name_idx];
        String8 collection_name = rd_plural_from_code_name(cfg_name);
        E_TypeKey collection_type_key = e_type_key_cons(.kind = E_TypeKind_Set, .name = collection_name);
        E_Expr *expr = e_push_expr(scratch.arena, E_ExprKind_LeafOffset, 0);
        expr->type_key = collection_type_key;
        e_string2expr_map_insert(scratch.arena, ctx->macro_map, collection_name, expr);
        e_lookup_rule_map_insert_new(scratch.arena, ctx->lookup_rule_map, collection_name,
                                     .info   = E_LOOKUP_INFO_FUNCTION_NAME(top_level_cfg),
                                     .access = E_LOOKUP_ACCESS_FUNCTION_NAME(top_level_cfg));
      }
      
      //- rjf: add macros for all ctrl entity collections
      for EachElement(ctrl_name_idx, evallable_ctrl_names)
      {
        String8 kind_name = evallable_ctrl_names[ctrl_name_idx];
        String8 collection_name = rd_plural_from_code_name(kind_name);
        E_TypeKey collection_type_key = e_type_key_cons(.kind = E_TypeKind_Set, .name = collection_name);
        E_Expr *expr = e_push_expr(scratch.arena, E_ExprKind_LeafOffset, 0);
        expr->type_key = collection_type_key;
        e_string2expr_map_insert(scratch.arena, ctx->macro_map, collection_name, expr);
        e_lookup_rule_map_insert_new(scratch.arena, ctx->lookup_rule_map, collection_name,
                                     .info   = E_LOOKUP_INFO_FUNCTION_NAME(ctrl_entities),
                                     .access = E_LOOKUP_ACCESS_FUNCTION_NAME(ctrl_entities));
      }
      
      //- rjf: add macro for output log
      {
        HS_Scope *hs_scope = hs_scope_open();
        U128 key = d_state->output_log_key;
        U128 hash = hs_hash_from_key(key, 0);
        String8 data = hs_data_from_hash(hs_scope, hash);
        E_Space space = e_space_make(E_SpaceKind_HashStoreKey);
        E_Expr *expr = e_push_expr(scratch.arena, E_ExprKind_LeafOffset, 0);
        space.u128 = key;
        expr->space    = space;
        expr->mode     = E_Mode_Offset;
        expr->type_key = e_type_key_cons_array(e_type_key_basic(E_TypeKind_U8), data.size);
        e_string2expr_map_insert(scratch.arena, ctx->macro_map, str8_lit("output_log"), expr);
        hs_scope_close(hs_scope);
      }
      
      //- rjf: add macros for all watches which define identifiers
      RD_CfgList watches = rd_cfg_top_level_list_from_string(scratch.arena, str8_lit("watch"));
      for(RD_CfgNode *n = watches.first; n != 0; n = n->next)
      {
        RD_Cfg *watch = n->v;
        String8 expr = rd_expr_from_cfg(watch);
        E_Parse parse = e_parse_expr_from_text__cached(expr);
        if(parse.msgs.max_kind == E_MsgKind_Null)
        {
          e_push_leaf_ident_exprs_from_expr__in_place(scratch.arena, ctx->macro_map, parse.expr);
        }
      }
    }
    e_select_ir_ctx(ir_ctx);
    
    ////////////////////////////
    //- rjf: build eval interpretation context
    //
    E_InterpretCtx *interpret_ctx = push_array(scratch.arena, E_InterpretCtx, 1);
    {
      E_InterpretCtx *ctx = interpret_ctx;
      ctx->space_read        = rd_eval_space_read;
      ctx->space_write       = rd_eval_space_write;
      ctx->primary_space     = eval_modules_primary->space;
      ctx->reg_arch          = eval_modules_primary->arch;
      ctx->reg_space         = rd_eval_space_from_ctrl_entity(thread, RD_EvalSpaceKind_CtrlEntity);
      ctx->reg_unwind_count  = unwind_count;
      ctx->module_base       = push_array(scratch.arena, U64, 1);
      ctx->module_base[0]    = module->vaddr_range.min;
      ctx->tls_base          = push_array(scratch.arena, U64, 1);
      ctx->tls_base[0]       = d_query_cached_tls_base_vaddr_from_process_root_rip(process, tls_root_vaddr, rip_vaddr);
    }
    e_select_interpret_ctx(interpret_ctx);
    
    ////////////////////////////
    //- rjf: build eval visualization view rule table
    //
    EV_ViewRuleInfoTable *view_rule_info_table = push_array(scratch.arena, EV_ViewRuleInfoTable, 1);
    {
      ev_view_rule_info_table_push_builtins(scratch.arena, view_rule_info_table);
    }
    ev_select_view_rule_info_table(view_rule_info_table);
    
    ////////////////////////////
    //- rjf: build eval visualization auto-view-rule table
    //
    // TODO(rjf): @cfg
    //
    EV_AutoViewRuleTable *auto_view_rule_table = push_array(scratch.arena, EV_AutoViewRuleTable, 1);
    {
      RD_CfgList auto_view_rules = rd_cfg_top_level_list_from_string(scratch.arena, str8_lit("auto_view_rule"));
      for(RD_CfgNode *n = auto_view_rules.first; n != 0; n = n->next)
      {
        RD_Cfg *rule = n->v;
        String8 type_string      = rd_cfg_child_from_string(rule, str8_lit("source"))->first->string;
        String8 view_rule_string = rd_cfg_child_from_string(rule, str8_lit("dest"))->first->string;
        E_TokenArray tokens = e_token_array_from_text(scratch.arena, type_string);
        E_Parse type_parse = e_parse_type_from_text_tokens(scratch.arena, type_string, &tokens);
        E_TypeKey type_key = e_type_from_expr(type_parse.expr);
        if(!e_type_key_match(e_type_key_zero(), type_key))
        {
          // ev_auto_view_rule_table_push_new(scratch.arena, auto_view_rule_table, type_key, view_rule_string, 0);
        }
      }
    }
    // ev_select_auto_view_rule_table(auto_view_rule_table);
    
    ////////////////////////////
    //- rjf: autosave if needed
    //
    {
      rd_state->seconds_until_autosave -= rd_state->frame_dt;
      if(rd_state->seconds_until_autosave <= 0.f)
      {
        rd_cmd(RD_CmdKind_WriteUserData);
        rd_cmd(RD_CmdKind_WriteProjectData);
        rd_state->seconds_until_autosave = 5.f;
      }
    }
    
    ////////////////////////////
    //- rjf: sanitize the window/panel/tab tree structure
    //
    {
      // TODO(rjf): @cfg_panels in the past, we had a spot in the rd_window_frame,
      // which ensured to select tabs, if a panel had tabs but had none
      // selected, and if a panel had a selected tab but it was project-filtered.
      // this is effectively just fixing up unexpected malformations of the
      // panel tree - because we are adopting some number of new possibilities
      // of this via the cfg change, we can just actually do a single fixup
      // point here.
    }
    
    ////////////////////////////
    //- rjf: process top-level graphical commands
    //
    if(depth == 0)
    {
      for(;rd_next_cmd(&cmd);) RD_RegsScope()
      {
        // rjf: unpack command
        RD_CmdKind kind = rd_cmd_kind_from_string(cmd->name);
        rd_regs_copy_contents(rd_frame_arena(), rd_regs(), cmd->regs);
        
        // rjf: request frame
        rd_request_frame();
        
        // rjf: process command
        Dir2 split_dir = Dir2_Invalid;
        RD_Cfg *split_panel = &rd_nil_cfg;
        U64 panel_sib_off = 0;
        U64 panel_child_off = 0;
        Vec2S32 panel_change_dir = {0};
        switch(kind)
        {
          //- rjf: default cases
          case RD_CmdKind_Run:
          case RD_CmdKind_LaunchAndRun:
          case RD_CmdKind_LaunchAndInit:
          case RD_CmdKind_StepInto:
          case RD_CmdKind_StepOver:
          case RD_CmdKind_Restart:
          {
            CTRL_EntityList processes = ctrl_entity_list_from_kind(d_state->ctrl_entity_store, CTRL_EntityKind_Process);
            if(processes.count == 0 || kind == RD_CmdKind_Restart)
            {
              RD_CfgList bps = rd_cfg_top_level_list_from_string(scratch.arena, str8_lit("breakpoint"));
              for(RD_CfgNode *n = bps.first; n != 0; n = n->next)
              {
                RD_Cfg *hit_count = rd_cfg_child_from_string_or_alloc(n->v, str8_lit("hit_count"));
                rd_cfg_release_all_children(hit_count);
                rd_cfg_new(hit_count, str8_lit("0"));
              }
            }
          } // fallthrough
          default:
          {
            // rjf: try to run engine command
            if(D_CmdKind_Null < (D_CmdKind)kind && (D_CmdKind)kind < D_CmdKind_COUNT)
            {
              D_CmdParams params = {0};
              params.machine       = rd_regs()->machine;
              params.process       = rd_regs()->process;
              params.thread        = rd_regs()->thread;
              params.entity        = rd_regs()->ctrl_entity;
              params.string        = rd_regs()->string;
              params.file_path     = rd_regs()->file_path;
              params.cursor        = rd_regs()->cursor;
              params.vaddr         = rd_regs()->vaddr;
              params.prefer_disasm = rd_regs()->prefer_disasm;
              params.pid           = rd_regs()->pid;
              params.targets.count = 1;
              params.targets.v = push_array(scratch.arena, D_Target, params.targets.count);
              params.targets.v[0] = rd_target_from_cfg(scratch.arena, rd_cfg_from_handle(rd_regs()->cfg));
              d_push_cmd((D_CmdKind)kind, &params);
            }
            
            // rjf: try to open tabs for "view driver" commands
            RD_ViewRuleInfo *view_rule_info = rd_view_rule_info_from_string(cmd->name);
            if(view_rule_info != &rd_nil_view_rule_info)
            {
              rd_cmd(RD_CmdKind_OpenTab, .string = str8_zero(), .params_tree = md_tree_from_string(scratch.arena, cmd->name)->first);
            }
          }break;
          
          //- rjf: top-level lister
          case RD_CmdKind_OpenLister:
          {
            RD_ListerFlags lister_flags = (RD_ListerFlag_LineEdit|
                                           RD_ListerFlag_Descriptions|
                                           RD_ListerFlag_KindLabel|
                                           RD_ListerFlag_Procedures|
                                           RD_ListerFlag_Files|
                                           RD_ListerFlag_Commands|
                                           RD_ListerFlag_Settings|
                                           RD_ListerFlag_SystemProcesses);
            rd_cmd(RD_CmdKind_PushQuery, .lister_flags = lister_flags);
          }break;
          
          //- rjf: command fast path
          case RD_CmdKind_RunCommand:
          {
            RD_CmdKindInfo *info = rd_cmd_kind_info_from_string(cmd->regs->cmd_name);
            
            // rjf: command does not have a query - simply execute with the current registers
            if(!(info->query.flags & RD_QueryFlag_Required))
            {
              RD_RegsScope(.cmd_name = str8_zero()) rd_push_cmd(cmd->regs->cmd_name, rd_regs());
            }
            
            // rjf: command has required query -> prep query
            else
            {
              rd_cmd(RD_CmdKind_PushQuery, .lister_flags = rd_regs()->lister_flags|RD_ListerFlag_LineEdit|RD_ListerFlag_Commands|RD_ListerFlag_Descriptions);
            }
          }break;
          
          //- rjf: exiting
          case RD_CmdKind_Exit:
          {
            // rjf: if control processes are live, but this is not force-confirmed, then
            // get confirmation from user
            CTRL_EntityList processes = ctrl_entity_list_from_kind(d_state->ctrl_entity_store, CTRL_EntityKind_Process);
            UI_Key key = ui_key_from_string(ui_key_zero(), str8_lit("lossy_exit_confirmation"));
            if(processes.count != 0 && !rd_regs()->force_confirm && !ui_key_match(rd_state->popup_key, key))
            {
              rd_state->popup_key = key;
              rd_state->popup_active = 1;
              arena_clear(rd_state->popup_arena);
              MemoryZeroStruct(&rd_state->popup_cmds);
              rd_state->popup_title = push_str8f(rd_state->popup_arena, "Are you sure you want to exit?");
              rd_state->popup_desc = push_str8f(rd_state->popup_arena, "The debugger is still attached to %slive process%s.",
                                                processes.count == 1 ? "a " : "",
                                                processes.count == 1 ? ""   : "es");
              RD_Regs *regs = rd_regs_copy(rd_frame_arena(), rd_regs());
              regs->force_confirm = 1;
              rd_cmd_list_push_new(rd_state->popup_arena, &rd_state->popup_cmds, rd_cmd_kind_info_table[RD_CmdKind_Exit].string, regs);
            }
            
            // rjf: otherwise, actually exit
            else
            {
              rd_cmd(RD_CmdKind_WriteUserData);
              rd_cmd(RD_CmdKind_WriteProjectData);
              rd_state->quit = 1;
            }
          }break;
          
          //- rjf: windows
          case RD_CmdKind_OpenWindow:
          {
            RD_Cfg *old_window = rd_cfg_from_handle(rd_regs()->window);
            RD_Cfg *bucket = old_window->parent;
            if(bucket == &rd_nil_cfg)
            {
              bucket = rd_cfg_child_from_string(rd_state->root_cfg, str8_lit("user"));
            }
            RD_Cfg *new_window = rd_cfg_new(bucket, str8_lit("window"));
            RD_Cfg *size = rd_cfg_new(new_window, str8_lit("size"));
            rd_cfg_newf(size, "1280");
            rd_cfg_newf(size, "720");
            for(RD_Cfg *old_child = old_window->first; old_child != &rd_nil_cfg; old_child = old_child->next)
            {
              if(!str8_match(old_child->string, str8_lit("panels"), 0))
              {
                RD_Cfg *new_child = rd_cfg_deep_copy(old_child);
                rd_cfg_insert_child(new_window, new_window->last, new_child);
              }
            }
          }break;
          case RD_CmdKind_CloseWindow:
          {
            RD_Cfg *wcfg = rd_cfg_from_handle(rd_regs()->window);
            rd_cfg_release(wcfg);
          }break;
          case RD_CmdKind_ToggleFullscreen:
          {
            RD_Cfg *wcfg = rd_cfg_from_handle(rd_regs()->window);
            RD_WindowState *ws = rd_window_state_from_cfg(wcfg);
            if(ws != &rd_nil_window_state)
            {
              os_window_set_fullscreen(ws->os, !os_window_is_fullscreen(ws->os));
            }
          }break;
          case RD_CmdKind_BringToFront:
          {
            RD_Cfg *last_focused_wcfg = rd_cfg_from_handle(rd_state->last_focused_window);
            RD_WindowState *last_focused_ws = rd_window_state_from_cfg(last_focused_wcfg);
            if(last_focused_ws == &rd_nil_window_state)
            {
              last_focused_ws = rd_state->first_window_state;
            }
            if(last_focused_ws != &rd_nil_window_state)
            {
              os_window_set_minimized(last_focused_ws->os, 0);
              os_window_focus(last_focused_ws->os);
            }
          }break;
          
          //- rjf: confirmations
          case RD_CmdKind_PopupAccept:
          {
            rd_state->popup_active = 0;
            rd_state->popup_key = ui_key_zero();
            for(RD_CmdNode *n = rd_state->popup_cmds.first; n != 0; n = n->next)
            {
              rd_push_cmd(n->cmd.name, n->cmd.regs);
            }
          }break;
          case RD_CmdKind_PopupCancel:
          {
            rd_state->popup_active = 0;
            rd_state->popup_key = ui_key_zero();
          }break;
          
          //- rjf: config path saving/loading/applying
          case RD_CmdKind_OpenRecentProject:
          {
            RD_Cfg *cfg = rd_cfg_from_handle(rd_regs()->cfg);
            if(str8_match(cfg->string, str8_lit("recent_project"), 0))
            {
              rd_cmd(RD_CmdKind_OpenProject, .file_path = cfg->first->string);
            }
          }break;
          case RD_CmdKind_OpenUser:
          case RD_CmdKind_OpenProject:
          {
            String8 file_root_key = (kind == RD_CmdKind_OpenUser ? str8_lit("user") : str8_lit("project"));
            RD_Cfg *file_root = rd_cfg_child_from_string(rd_state->root_cfg, file_root_key);
            
            //- rjf: load the new file's data
            String8 file_path = rd_regs()->file_path;
            String8 file_data = os_data_from_file_path(scratch.arena, file_path);
            
            //- rjf: determine if the file is good
            B32 file_is_okay = 0;
            {
              FileProperties file_props = os_properties_from_file_path(file_path);
              file_is_okay = ((file_props.size == 0 && file_props.created == 0) ||
                              str8_match(str8_prefix(file_data, 9), str8_lit("// raddbg"), 0));
            }
            
            //- rjf: bad file -> alert user
            if(!file_is_okay)
            {
              log_user_errorf("\"%S\" appears to refer to an existing file which is not a RADDBG config file. This would overwrite the file.", file_path);
            }
            
            //- rjf: eliminate all old state under this file tree
            if(file_is_okay)
            {
              rd_cfg_release_all_children(file_root);
            }
            
            //- rjf: parse the new file, generate cfg entities for it
            RD_CfgList file_cfg_list = {0};
            if(file_is_okay)
            {
              file_cfg_list = rd_cfg_tree_list_from_string(scratch.arena, file_data);
            }
            
            //- rjf: store path
            if(file_is_okay)
            {
              switch(kind)
              {
                default:{}break;
                case RD_CmdKind_OpenUser:
                {
                  arena_clear(rd_state->user_path_arena);
                  rd_state->user_path = push_str8_copy(rd_state->user_path_arena, file_path);
                }break;
                case RD_CmdKind_OpenProject:
                {
                  arena_clear(rd_state->project_path_arena);
                  rd_state->project_path = push_str8_copy(rd_state->project_path_arena, file_path);
                }break;
              }
            }
            
            //- rjf: insert the new cfg entities into this file tree
            if(file_is_okay)
            {
              for(RD_CfgNode *n = file_cfg_list.first; n != 0; n = n->next)
              {
                rd_cfg_insert_child(file_root, file_root->last, n->v);
              }
            }
            
            //- rjf: if config did not open any windows for the user, then we need to open a sensible default
            if(file_is_okay && kind == RD_CmdKind_OpenUser)
            {
              RD_CfgList all_user_windows = rd_cfg_child_list_from_string(scratch.arena, file_root, str8_lit("window"));
              if(all_user_windows.count == 0)
              {
                OS_Handle monitor    = os_primary_monitor();
                String8 monitor_name = os_name_from_monitor(scratch.arena, monitor);
                Vec2F32 monitor_dim  = os_dim_from_monitor(monitor);
                F32 monitor_dpi      = os_dpi_from_monitor(monitor);
                Vec2F32 window_dim   = v2f32(monitor_dim.x*4/5, monitor_dim.y*4/5);
                RD_Cfg *new_window = rd_cfg_new(file_root, str8_lit("window"));
                RD_Cfg *size = rd_cfg_new(new_window, str8_lit("size"));
                rd_cfg_newf(size, "%f", window_dim.x);
                rd_cfg_newf(size, "%f", window_dim.y);
                F32 line_height_guess = 11.f * (monitor_dpi / 96.f);
                F32 num_lines_in_monitor_height = monitor_dim.y / line_height_guess;
                if(num_lines_in_monitor_height < 100)
                {
                  rd_cmd(RD_CmdKind_ResetToCompactPanels, .window = rd_handle_from_cfg(new_window));
                }
                else
                {
                  rd_cmd(RD_CmdKind_ResetToDefaultPanels, .window = rd_handle_from_cfg(new_window));
                }
              }
            }
            
            //- rjf: record recently-opened projects in the user
            if(file_is_okay && kind == RD_CmdKind_OpenProject)
            {
              RD_Cfg *user = rd_cfg_child_from_string(rd_state->root_cfg, str8_lit("user"));
              RD_CfgList recent_projects = rd_cfg_child_list_from_string(scratch.arena, user, str8_lit("recent_project"));
              RD_Cfg *recent_project = &rd_nil_cfg;
              for(RD_CfgNode *n = recent_projects.first; n != 0; n = n->next)
              {
                if(path_match_normalized(n->v->string, file_path))
                {
                  recent_project = n->v;
                  break;
                }
              }
              if(recent_project == &rd_nil_cfg)
              {
                recent_project = rd_cfg_new(user, str8_lit("recent_project"));
                rd_cfg_new(recent_project, path_normalized_from_string(scratch.arena, file_path));
              }
              rd_cfg_unhook(user, recent_project);
              rd_cfg_insert_child(user, &rd_nil_cfg, recent_project);
              recent_projects = rd_cfg_child_list_from_string(scratch.arena, user, str8_lit("recent_project"));
              if(recent_projects.count > 32)
              {
                rd_cfg_release(recent_projects.last->v);
              }
            }
            
            //- TODO(rjf): @cfg set up debugging config state
            if(kind == RD_CmdKind_OpenUser)
            {
              RD_Cfg *user = rd_cfg_child_from_string(rd_state->root_cfg, str8_lit("user"));
              RD_Cfg *watch = rd_cfg_new(user, str8_lit("watch"));
              RD_Cfg *expr = rd_cfg_new(watch, str8_lit("expression"));
              rd_cfg_new(expr, str8_lit("current_thread"));
            }
          }break;
          
          //- rjf: writing config changes
          case RD_CmdKind_WriteUserData:
          case RD_CmdKind_WriteProjectData:
          {
#if 0 // TODO(rjf): @cfg
            RD_CfgSrc src = RD_CfgSrc_User;
            for(RD_CfgSrc s = (RD_CfgSrc)0; s < RD_CfgSrc_COUNT; s = (RD_CfgSrc)(s+1))
            {
              if(kind == rd_cfg_src_write_cmd_kind_table[s])
              {
                src = s;
                break;
              }
            }
            String8 path = rd_cfg_path_from_src(src);
            String8List rd_strs = rd_cfg_strings_from_gfx(scratch.arena, path, src);
            String8 header = push_str8f(scratch.arena, "// raddbg %s file\n\n", rd_cfg_src_string_table[src].str);
            String8List strs = {0};
            str8_list_push(scratch.arena, &strs, header);
            str8_list_concat_in_place(&strs, &rd_strs);
            String8 data = str8_list_join(scratch.arena, &strs, 0);
            String8 data_indented = indented_from_string(scratch.arena, data);
            os_write_data_to_file_path(path, data_indented);
#endif
          }break;
          
          //- rjf: code navigation
          case RD_CmdKind_FindTextForward:
          case RD_CmdKind_FindTextBackward:
          {
            rd_set_search_string(rd_regs()->string);
          }break;
          
          //- rjf: find next and find prev
          case RD_CmdKind_FindNext:
          {
            rd_cmd(RD_CmdKind_FindTextForward, .string = rd_push_search_string(scratch.arena));
          }break;
          case RD_CmdKind_FindPrev:
          {
            rd_cmd(RD_CmdKind_FindTextBackward, .string = rd_push_search_string(scratch.arena));
          }break;
          
          //- rjf: font sizes
          case RD_CmdKind_IncUIFontScale:
          {
            fnt_reset();
            F32 current_font_size = rd_font_size_from_slot(RD_FontSlot_Main);
            F32 new_font_size = clamp_1f32(r1f32(6, 72), current_font_size+1);
            RD_Cfg *window = rd_cfg_from_handle(rd_regs()->window);
            RD_Cfg *main_font_size = rd_cfg_child_from_string_or_alloc(window, str8_lit("main_font_size"));
            rd_cfg_release_all_children(main_font_size);
            rd_cfg_newf(main_font_size, "%f", new_font_size);
          }break;
          case RD_CmdKind_DecUIFontScale:
          {
            fnt_reset();
            F32 current_font_size = rd_font_size_from_slot(RD_FontSlot_Main);
            F32 new_font_size = clamp_1f32(r1f32(6, 72), current_font_size-1);
            RD_Cfg *window = rd_cfg_from_handle(rd_regs()->window);
            RD_Cfg *main_font_size = rd_cfg_child_from_string_or_alloc(window, str8_lit("main_font_size"));
            rd_cfg_release_all_children(main_font_size);
            rd_cfg_newf(main_font_size, "%f", new_font_size);
          }break;
          case RD_CmdKind_IncCodeFontScale:
          {
            fnt_reset();
            F32 current_font_size = rd_font_size_from_slot(RD_FontSlot_Code);
            F32 new_font_size = clamp_1f32(r1f32(6, 72), current_font_size+1);
            RD_Cfg *window = rd_cfg_from_handle(rd_regs()->window);
            RD_Cfg *code_font_size = rd_cfg_child_from_string_or_alloc(window, str8_lit("code_font_size"));
            rd_cfg_release_all_children(code_font_size);
            rd_cfg_newf(code_font_size, "%f", new_font_size);
          }break;
          case RD_CmdKind_DecCodeFontScale:
          {
            fnt_reset();
            F32 current_font_size = rd_font_size_from_slot(RD_FontSlot_Code);
            F32 new_font_size = clamp_1f32(r1f32(6, 72), current_font_size-1);
            RD_Cfg *window = rd_cfg_from_handle(rd_regs()->window);
            RD_Cfg *code_font_size = rd_cfg_child_from_string_or_alloc(window, str8_lit("code_font_size"));
            rd_cfg_release_all_children(code_font_size);
            rd_cfg_newf(code_font_size, "%f", new_font_size);
          }break;
          
          //- rjf: panel creation
          case RD_CmdKind_NewPanelLeft: {split_dir = Dir2_Left;}goto split;
          case RD_CmdKind_NewPanelUp:   {split_dir = Dir2_Up;}goto split;
          case RD_CmdKind_NewPanelRight:{split_dir = Dir2_Right;}goto split;
          case RD_CmdKind_NewPanelDown: {split_dir = Dir2_Down;}goto split;
          case RD_CmdKind_SplitPanel:
          {
            split_dir = rd_regs()->dir2;
            split_panel = rd_cfg_from_handle(rd_regs()->dst_panel);
          }goto split;
          split:;
          if(split_dir != Dir2_Invalid)
          {
            // rjf: unpack
            Axis2 split_axis = axis2_from_dir2(split_dir);
            Side split_side = side_from_dir2(split_dir);
            RD_Cfg *new_panel_cfg = &rd_nil_cfg;
            RD_PanelTree panel_tree = rd_panel_tree_from_cfg(scratch.arena, split_panel);
            RD_PanelNode *panel_root = panel_tree.root;
            RD_PanelNode *panel = rd_panel_node_from_tree_cfg(panel_root, split_panel);
            RD_PanelNode *parent = panel->parent;
            
            // rjf: splitting on same axis as parent -> insert new sibling on same axis, adjust sizes
            if(parent != &rd_nil_panel_node && parent->split_axis == split_axis)
            {
              RD_Cfg *parent_cfg = parent->cfg;
              RD_Cfg *panel_cfg = panel->cfg;
              RD_Cfg *new_cfg = rd_cfg_alloc();
              rd_cfg_insert_child(parent_cfg, split_side == Side_Max ? panel_cfg : panel_cfg->prev, new_cfg);
              rd_cfg_equip_stringf(new_cfg, "%f", 1.f/parent->child_count);
              for(RD_PanelNode *child = parent->first; child != &rd_nil_panel_node; child = child->next)
              {
                F32 old_pct = child->pct_of_parent;
                F32 new_pct = old_pct * ((F32)(parent->child_count) / (parent->child_count+1));
                rd_cfg_equip_stringf(child->cfg, "%f", new_pct);
              }
              new_panel_cfg = new_cfg;
            }
            
            // rjf: splitting on opposite axis as parent - need to create new replacement node, + new sibling
            else
            {
              RD_Cfg *split_panel_prev = panel->prev->cfg;
              RD_Cfg *new_parent = rd_cfg_alloc();
              RD_Cfg *new_sibling = rd_cfg_alloc();
              rd_cfg_equip_string(new_parent, split_panel->string);
              rd_cfg_equip_string(split_panel, str8_lit("0.5"));
              rd_cfg_equip_string(new_sibling, str8_lit("0.5"));
              if(parent->cfg != &rd_nil_cfg)
              {
                rd_cfg_unhook(parent->cfg, split_panel);
                rd_cfg_insert_child(parent->cfg, split_panel_prev, new_parent);
              }
              else
              {
                rd_cfg_equip_string(new_parent, str8_lit("panels"));
                RD_Cfg *window_cfg = rd_window_from_cfg(split_panel);
                rd_cfg_insert_child(window_cfg, window_cfg->last, new_parent);
              }
              RD_Cfg *min = split_panel;
              RD_Cfg *max = new_sibling;
              if(split_side == Side_Min)
              {
                Swap(RD_Cfg *, min, max);
              }
              rd_cfg_insert_child(new_parent, new_parent->last, min);
              rd_cfg_insert_child(new_parent, new_parent->last, max);
              new_panel_cfg = new_sibling;
            }
            
            // rjf: pre-emptively set up the animation rectangle, depending on where
            // the new panel was inserted (?)
#if 0 // TODO(rjf): @cfg
            if(!rd_panel_is_nil(new_panel->prev))
            {
              Rng2F32 prev_rect_pct = new_panel->prev->animated_rect_pct;
              new_panel->animated_rect_pct = prev_rect_pct;
              new_panel->animated_rect_pct.p0.v[split_axis] = new_panel->animated_rect_pct.p1.v[split_axis];
            }
            if(!rd_panel_is_nil(new_panel->next))
            {
              Rng2F32 next_rect_pct = new_panel->next->animated_rect_pct;
              new_panel->animated_rect_pct = next_rect_pct;
              new_panel->animated_rect_pct.p1.v[split_axis] = new_panel->animated_rect_pct.p0.v[split_axis];
            }
#endif
            
            // rjf: if this split was caused by drag/dropping a tab, and the originating panel
            // has no further tabs, then close the originating panel
            RD_Cfg *dragdrop_origin_panel_cfg = rd_cfg_from_handle(rd_regs()->panel);
            RD_Cfg *dragdrop_tab = rd_cfg_from_handle(rd_regs()->view);
            if(kind == RD_CmdKind_SplitPanel &&
               new_panel_cfg != &rd_nil_cfg && dragdrop_tab != &rd_nil_cfg && dragdrop_origin_panel_cfg != &rd_nil_cfg)
            {
              rd_cfg_unhook(dragdrop_origin_panel_cfg, dragdrop_tab);
              rd_cfg_insert_child(new_panel_cfg, new_panel_cfg->last, dragdrop_tab);
              RD_PanelTree origin_panel_tree = rd_panel_tree_from_cfg(scratch.arena, dragdrop_origin_panel_cfg);
              RD_PanelNode *origin_panel = rd_panel_node_from_tree_cfg(origin_panel_tree.root, dragdrop_origin_panel_cfg);
              if(origin_panel->tabs.count == 0)
              {
                rd_cmd(RD_CmdKind_ClosePanel);
              }
            }
            
            // rjf: focus new panel
            if(new_panel_cfg != &rd_nil_cfg)
            {
              rd_cmd(RD_CmdKind_FocusPanel, .panel = rd_handle_from_cfg(new_panel_cfg));
            }
          }break;
          
          //- rjf: panel rotation
          case RD_CmdKind_RotatePanelColumns:
          {
            RD_Cfg *panel_cfg = rd_cfg_from_handle(rd_regs()->panel);
            RD_PanelTree panel_tree = rd_panel_tree_from_cfg(scratch.arena, panel_cfg);
            RD_PanelNode *panel = rd_panel_node_from_tree_cfg(panel_tree.root, panel_cfg);
            RD_PanelNode *parent = &rd_nil_panel_node;
            for(RD_PanelNode *p = panel->parent; p != &rd_nil_panel_node; p = p->parent)
            {
              if(p->split_axis == Axis2_X)
              {
                parent = p;
                break;
              }
            }
            if(parent != &rd_nil_panel_node && parent->child_count > 1)
            {
              RD_Cfg *rotated = parent->first->cfg;
              rd_cfg_unhook(parent->cfg, parent->first->cfg);
              rd_cfg_insert_child(parent->cfg, parent->last->cfg, rotated);
            }
          }break;
          
          //- rjf: panel focusing
          case RD_CmdKind_NextPanel: panel_sib_off = OffsetOf(RD_PanelNode, next); panel_child_off = OffsetOf(RD_PanelNode, first); goto cycle;
          case RD_CmdKind_PrevPanel: panel_sib_off = OffsetOf(RD_PanelNode, prev); panel_child_off = OffsetOf(RD_PanelNode, last); goto cycle;
          cycle:;
          {
            RD_PanelTree panel_tree = rd_panel_tree_from_cfg(scratch.arena, rd_cfg_from_handle(rd_regs()->window));
            RD_PanelNode *next_focused = &rd_nil_panel_node;
            for(RD_PanelNode *p = panel_tree.focused;
                p != &rd_nil_panel_node;
                p = rd_panel_node_rec__depth_first(panel_tree.root, p, panel_sib_off, panel_child_off).next)
            {
              if(p->first == &rd_nil_panel_node)
              {
                next_focused = p;
                break;
              }
            }
            if(next_focused == &rd_nil_panel_node)
            {
              for(RD_PanelNode *p = panel_tree.root;
                  p != &rd_nil_panel_node;
                  p = rd_panel_node_rec__depth_first(panel_tree.root, p, panel_sib_off, panel_child_off).next)
              {
                if(p->first == &rd_nil_panel_node)
                {
                  next_focused = p;
                }
              }
            }
            rd_cmd(RD_CmdKind_FocusPanel, .panel = rd_handle_from_cfg(next_focused->cfg));
          }break;
          case RD_CmdKind_FocusPanel:
          {
            RD_Cfg *panel = rd_cfg_from_handle(rd_regs()->panel);
            RD_PanelTree panel_tree = rd_panel_tree_from_cfg(scratch.arena, panel);
            for(RD_PanelNode *p = panel_tree.root;
                p != &rd_nil_panel_node;
                p = rd_panel_node_rec__depth_first_pre(panel_tree.root, p).next)
            {
              RD_Cfg *p_cfg = p->cfg;
              RD_Cfg *p_selection = rd_cfg_child_from_string(p_cfg, str8_lit("selected"));
              if(p_selection != &rd_nil_cfg)
              {
                rd_cfg_release(p_selection);
              }
            }
            if(panel != &rd_nil_cfg)
            {
              rd_cfg_new(panel, str8_lit("selected"));
              RD_Cfg *window = rd_window_from_cfg(panel);
              RD_WindowState *ws = rd_window_state_from_cfg(window);
              ws->menu_bar_focused = 0;
            }
          }break;
          
          //- rjf: directional panel focus changing
          case RD_CmdKind_FocusPanelRight: panel_change_dir = v2s32(+1, +0); goto focus_panel_dir;
          case RD_CmdKind_FocusPanelLeft:  panel_change_dir = v2s32(-1, +0); goto focus_panel_dir;
          case RD_CmdKind_FocusPanelUp:    panel_change_dir = v2s32(+0, -1); goto focus_panel_dir;
          case RD_CmdKind_FocusPanelDown:  panel_change_dir = v2s32(+0, +1); goto focus_panel_dir;
          focus_panel_dir:;
          {
            RD_Cfg *window = rd_cfg_from_handle(rd_regs()->window);
            RD_PanelTree panel_tree = rd_panel_tree_from_cfg(scratch.arena, window);
            RD_PanelNode *src_panel = panel_tree.focused;
            Rng2F32 src_panel_rect = rd_target_rect_from_panel_node(r2f32(v2f32(0, 0), v2f32(1000, 1000)), panel_tree.root, src_panel);
            Vec2F32 src_panel_center = center_2f32(src_panel_rect);
            Vec2F32 src_panel_half_dim = scale_2f32(dim_2f32(src_panel_rect), 0.5f);
            Vec2F32 travel_dim = add_2f32(src_panel_half_dim, v2f32(10.f, 10.f));
            Vec2F32 travel_dst = add_2f32(src_panel_center, mul_2f32(travel_dim, v2f32((F32)panel_change_dir.x, (F32)panel_change_dir.y)));
            RD_PanelNode *dst_root = &rd_nil_panel_node;
            for(RD_PanelNode *p = panel_tree.root; p != &rd_nil_panel_node; p = rd_panel_node_rec__depth_first_pre(panel_tree.root, p).next)
            {
              if(p == src_panel || p->first != &rd_nil_panel_node)
              {
                continue;
              }
              Rng2F32 p_rect = rd_target_rect_from_panel_node(r2f32(v2f32(0, 0), v2f32(1000, 1000)), panel_tree.root, p);
              if(contains_2f32(p_rect, travel_dst))
              {
                dst_root = p;
                break;
              }
            }
            if(dst_root != &rd_nil_panel_node)
            {
              RD_PanelNode *dst_panel = &rd_nil_panel_node;
              for(RD_PanelNode *p = dst_root; p != &rd_nil_panel_node; p = rd_panel_node_rec__depth_first_pre(dst_root, p).next)
              {
                if(p->first == &rd_nil_panel_node && p != src_panel)
                {
                  dst_panel = p;
                  break;
                }
              }
              rd_cmd(RD_CmdKind_FocusPanel, .panel = rd_handle_from_cfg(dst_panel->cfg));
            }
          }break;
          
          //- rjf: undo/redo
          case RD_CmdKind_Undo:{}break;
          case RD_CmdKind_Redo:{}break;
          
          //- rjf: focus history
          case RD_CmdKind_GoBack:{}break;
          case RD_CmdKind_GoForward:{}break;
          
          //- rjf: files
          case RD_CmdKind_SetCurrentPath:
          {
            arena_clear(rd_state->current_path_arena);
            rd_state->current_path = push_str8_copy(rd_state->current_path_arena, rd_regs()->file_path);
          }break;
          case RD_CmdKind_SetFileReplacementPath:
          {
            // NOTE(rjf):
            //
            // C:/foo/bar/baz.c
            // D:/foo/bar/baz.c
            // -> override C: -> D:
            //
            // C:/1/2/foo/bar.c
            // C:/2/3/foo/bar.c
            // -> override C:/1/2 -> C:2/3
            //
            // C:/foo/bar/baz.c
            // D:/1/2/3.c
            // -> override C:/foo/bar/baz.c -> D:/1/2/3.c
            
            //- rjf: unpack
            String8 src_path = rd_regs()->string;
            String8 dst_path = rd_regs()->file_path;
            String8 src_path__normalized = path_normalized_from_string(scratch.arena, src_path);
            String8 dst_path__normalized = path_normalized_from_string(scratch.arena, dst_path);
            String8List src_path_parts = str8_split_path(scratch.arena, src_path__normalized);
            String8List dst_path_parts = str8_split_path(scratch.arena, dst_path__normalized);
            
            //- rjf: reverse path parts
            String8List src_path_parts__reversed = {0};
            String8List dst_path_parts__reversed = {0};
            for(String8Node *n = src_path_parts.first; n != 0; n = n->next)
            {
              str8_list_push_front(scratch.arena, &src_path_parts__reversed, n->string);
            }
            for(String8Node *n = dst_path_parts.first; n != 0; n = n->next)
            {
              str8_list_push_front(scratch.arena, &dst_path_parts__reversed, n->string);
            }
            
            //- rjf: trace from each path upwards, in lock-step, to find the first difference
            // between the paths
            String8Node *first_diff_src = src_path_parts__reversed.first;
            String8Node *first_diff_dst = dst_path_parts__reversed.first;
            for(;first_diff_src != 0 && first_diff_dst != 0;)
            {
              if(!str8_match(first_diff_src->string, first_diff_dst->string, StringMatchFlag_CaseInsensitive))
              {
                break;
              }
              first_diff_src = first_diff_src->next;
              first_diff_dst = first_diff_dst->next;
            }
            
            //- rjf: form final map paths
            String8List map_src_parts = {0};
            String8List map_dst_parts = {0};
            for(String8Node *n = first_diff_src; n != 0; n = n->next)
            {
              str8_list_push_front(scratch.arena, &map_src_parts, n->string);
            }
            for(String8Node *n = first_diff_dst; n != 0; n = n->next)
            {
              str8_list_push_front(scratch.arena, &map_dst_parts, n->string);
            }
            StringJoin map_join = {.sep = str8_lit("/")};
            String8 map_src = str8_list_join(scratch.arena, &map_src_parts, &map_join);
            String8 map_dst = str8_list_join(scratch.arena, &map_dst_parts, &map_join);
            
            //- rjf: store as file path map cfg
            RD_Cfg *user = rd_cfg_child_from_string(rd_state->root_cfg, str8_lit("user"));
            RD_Cfg *map = rd_cfg_new(user, str8_lit("file_path_map"));
            RD_Cfg *src = rd_cfg_new(map, str8_lit("source"));
            RD_Cfg *dst = rd_cfg_new(map, str8_lit("dest"));
            rd_cfg_new(src, map_src);
            rd_cfg_new(dst, map_dst);
          }break;
          
          //- rjf: panel removal
          case RD_CmdKind_ClosePanel:
          {
#if 0 // TODO(rjf): @cfg
            RD_Window *ws = rd_window_from_handle(rd_regs()->window);
            RD_Panel *panel = rd_panel_from_handle(rd_regs()->panel);
            RD_Panel *parent = panel->parent;
            if(!rd_panel_is_nil(parent))
            {
              Axis2 split_axis = parent->split_axis;
              
              // NOTE(rjf): If we're removing all but the last child of this parent,
              // we should just remove both children.
              if(parent->child_count == 2)
              {
                RD_Panel *discard_child = panel;
                RD_Panel *keep_child = panel == parent->first ? parent->last : parent->first;
                RD_Panel *grandparent = parent->parent;
                RD_Panel *parent_prev = parent->prev;
                F32 pct_of_parent = parent->pct_of_parent;
                
                // rjf: unhook kept child
                rd_panel_remove(parent, keep_child);
                
                // rjf: unhook this subtree
                if(!rd_panel_is_nil(grandparent))
                {
                  rd_panel_remove(grandparent, parent);
                }
                
                // rjf: release the things we should discard
                {
                  rd_panel_release(ws, parent);
                  rd_panel_release(ws, discard_child);
                }
                
                // rjf: re-hook our kept child into the overall tree
                if(rd_panel_is_nil(grandparent))
                {
                  ws->root_panel = keep_child;
                }
                else
                {
                  rd_panel_insert(grandparent, parent_prev, keep_child);
                }
                keep_child->pct_of_parent = pct_of_parent;
                
                // rjf: reset focus, if needed
                if(ws->focused_panel == discard_child)
                {
                  ws->focused_panel = keep_child;
                  for(RD_Panel *grandchild = ws->focused_panel; !rd_panel_is_nil(grandchild); grandchild = grandchild->first)
                  {
                    ws->focused_panel = grandchild;
                  }
                }
                
                // rjf: keep-child split-axis == grandparent split-axis? bubble keep-child up into grandparent's children
                if(!rd_panel_is_nil(grandparent) && grandparent->split_axis == keep_child->split_axis && !rd_panel_is_nil(keep_child->first))
                {
                  rd_panel_remove(grandparent, keep_child);
                  RD_Panel *prev = parent_prev;
                  for(RD_Panel *child = keep_child->first, *next = 0; !rd_panel_is_nil(child); child = next)
                  {
                    next = child->next;
                    rd_panel_remove(keep_child, child);
                    rd_panel_insert(grandparent, prev, child);
                    prev = child;
                    child->pct_of_parent *= keep_child->pct_of_parent;
                  }
                  rd_panel_release(ws, keep_child);
                }
              }
              // NOTE(rjf): Otherwise we can just remove this child.
              else
              {
                RD_Panel *next = &rd_nil_panel;
                F32 removed_size_pct = panel->pct_of_parent;
                if(rd_panel_is_nil(next)) { next = panel->prev; }
                if(rd_panel_is_nil(next)) { next = panel->next; }
                rd_panel_remove(parent, panel);
                rd_panel_release(ws, panel);
                if(ws->focused_panel == panel)
                {
                  ws->focused_panel = next;
                  for(RD_Panel *grandchild = ws->focused_panel; !rd_panel_is_nil(grandchild); grandchild = grandchild->first)
                  {
                    ws->focused_panel = grandchild;
                  }
                }
                for(RD_Panel *child = parent->first; !rd_panel_is_nil(child); child = child->next)
                {
                  child->pct_of_parent /= 1.f-removed_size_pct;
                }
              }
            }
#endif
          }break;
          
          //- rjf: panel tab controls
          case RD_CmdKind_FocusTab:
          {
            RD_Cfg *tab = rd_cfg_from_handle(rd_regs()->view);
            RD_Cfg *panel = tab->parent;
            RD_PanelTree panel_tree = rd_panel_tree_from_cfg(scratch.arena, panel);
            RD_PanelNode *panel_node = rd_panel_node_from_tree_cfg(panel_tree.root, panel);
            for(RD_CfgNode *n = panel_node->tabs.first; n != 0; n = n->next)
            {
              rd_cfg_release(rd_cfg_child_from_string(n->v, str8_lit("selected")));
            }
            rd_cfg_new(tab, str8_lit("selected"));
          }break;
          case RD_CmdKind_NextTab:
          {
            RD_Cfg *window = rd_cfg_from_handle(rd_regs()->window);
            RD_PanelTree panel_tree = rd_panel_tree_from_cfg(scratch.arena, window);
            RD_PanelNode *focused = panel_tree.focused;
            RD_CfgNode *selected_tab_n = 0;
            for(RD_CfgNode *n = focused->tabs.first; n != 0; n = n->next)
            {
              if(n->v == focused->selected_tab)
              {
                selected_tab_n = n;
                break;
              }
            }
            RD_Cfg *next_selected_tab = &rd_nil_cfg;
            U64 idx = 0;
            for(RD_CfgNode *tab_n = selected_tab_n;
                tab_n != 0 && (tab_n != selected_tab_n || idx == 0);
                ((tab_n->next == 0) ? (tab_n = focused->tabs.first) : (tab_n = tab_n->next)), idx += 1)
            {
              if(!rd_cfg_is_project_filtered(tab_n->v) && tab_n != selected_tab_n)
              {
                next_selected_tab = tab_n->v;
                break;
              }
            }
            if(next_selected_tab != &rd_nil_cfg)
            {
              rd_cmd(RD_CmdKind_FocusTab, .view = rd_handle_from_cfg(next_selected_tab));
            }
          }break;
          case RD_CmdKind_PrevTab:
          {
            RD_Cfg *window = rd_cfg_from_handle(rd_regs()->window);
            RD_PanelTree panel_tree = rd_panel_tree_from_cfg(scratch.arena, window);
            RD_PanelNode *focused = panel_tree.focused;
            RD_CfgNode *selected_tab_n = 0;
            for(RD_CfgNode *n = focused->tabs.last; n != 0; n = n->prev)
            {
              if(n->v == focused->selected_tab)
              {
                selected_tab_n = n;
                break;
              }
            }
            RD_Cfg *next_selected_tab = &rd_nil_cfg;
            U64 idx = 0;
            for(RD_CfgNode *tab_n = selected_tab_n;
                tab_n != 0 && (tab_n != selected_tab_n || idx == 0);
                ((tab_n->prev == 0) ? (tab_n = focused->tabs.last) : (tab_n = tab_n->prev)), idx += 1)
            {
              if(!rd_cfg_is_project_filtered(tab_n->v) && tab_n != selected_tab_n)
              {
                next_selected_tab = tab_n->v;
                break;
              }
            }
            if(next_selected_tab != &rd_nil_cfg)
            {
              rd_cmd(RD_CmdKind_FocusTab, .view = rd_handle_from_cfg(next_selected_tab));
            }
          }break;
          case RD_CmdKind_MoveTabRight:
          case RD_CmdKind_MoveTabLeft:
          {
#if 0 // TODO(rjf): @cfg
            RD_Window *ws = rd_window_from_handle(rd_regs()->window);
            RD_Panel *panel = ws->focused_panel;
            RD_View *view = rd_selected_tab_from_panel(panel);
            RD_View *prev_view = (kind == RD_CmdKind_MoveTabRight ? view->order_next : view->order_prev->order_prev);
            if(!rd_view_is_nil(prev_view) || kind == RD_CmdKind_MoveTabLeft)
            {
              rd_cmd(RD_CmdKind_MoveTab,
                     .panel = rd_handle_from_panel(panel),
                     .dst_panel = rd_handle_from_panel(panel),
                     .view = rd_handle_from_view(view),
                     .prev_view = rd_handle_from_view(prev_view));
            }
#endif
          }break;
          case RD_CmdKind_OpenTab:
          {
#if 0 // TODO(rjf): @cfg
            RD_Panel *panel = rd_panel_from_handle(rd_regs()->panel);
            RD_View *view = rd_view_alloc();
            String8 query = rd_regs()->string;
            RD_ViewRuleInfo *spec = rd_view_rule_info_from_string(rd_regs()->params_tree->string);
            rd_view_equip_spec(view, spec, query, rd_regs()->params_tree);
            rd_panel_insert_tab_view(panel, panel->last_tab_view, view);
#endif
          }break;
          case RD_CmdKind_CloseTab:
          {
            RD_Cfg *tab = rd_cfg_from_handle(rd_regs()->view);
            RD_PanelTree panel_tree = rd_panel_tree_from_cfg(scratch.arena, tab);
            RD_PanelNode *panel = rd_panel_node_from_tree_cfg(panel_tree.root, tab->parent);
            B32 found_selected = 0;
            RD_Cfg *next_selected_tab = &rd_nil_cfg;
            for(RD_CfgNode *n = panel->tabs.first; n != 0; n = n->next)
            {
              if(n->v == panel->selected_tab)
              {
                found_selected = 1;
              }
              else if(!rd_cfg_is_project_filtered(n->v))
              {
                next_selected_tab = n->v;
                if(found_selected)
                {
                  break;
                }
              }
            }
            rd_cmd(RD_CmdKind_FocusTab, .view = rd_handle_from_cfg(next_selected_tab));
            rd_cfg_release(tab);
          }break;
          case RD_CmdKind_MoveTab:
          {
#if 0 // TODO(rjf): @cfg
            RD_Window *ws = rd_window_from_handle(rd_regs()->window);
            RD_Panel *src_panel = rd_panel_from_handle(rd_regs()->panel);
            RD_View *view = rd_view_from_handle(rd_regs()->view);
            RD_Panel *dst_panel = rd_panel_from_handle(rd_regs()->dst_panel);
            RD_View *prev_view = rd_view_from_handle(rd_regs()->prev_view);
            if(!rd_panel_is_nil(src_panel) &&
               !rd_panel_is_nil(dst_panel) &&
               prev_view != view)
            {
              rd_panel_remove_tab_view(src_panel, view);
              rd_panel_insert_tab_view(dst_panel, prev_view, view);
              ws->focused_panel = dst_panel;
              B32 src_panel_is_empty = 1;
              for(RD_View *v = src_panel->first_tab_view; !rd_view_is_nil(v); v = v->order_next)
              {
                if(!rd_view_is_project_filtered(v))
                {
                  src_panel_is_empty = 0;
                  break;
                }
              }
              if(src_panel_is_empty && src_panel != ws->root_panel)
              {
                rd_cmd(RD_CmdKind_ClosePanel, .panel = rd_handle_from_panel(src_panel));
              }
            }
#endif
          }break;
          case RD_CmdKind_TabBarTop:
          {
            RD_Cfg *panel = rd_cfg_from_handle(rd_regs()->panel);
            rd_cfg_release(rd_cfg_child_from_string(panel, str8_lit("tabs_on_bottom")));
          }break;
          case RD_CmdKind_TabBarBottom:
          {
            RD_Cfg *panel = rd_cfg_from_handle(rd_regs()->panel);
            rd_cfg_child_from_string_or_alloc(panel, str8_lit("tabs_on_bottom"));
          }break;
          
          //- rjf: files
          case RD_CmdKind_Open:
          {
            String8 path = rd_regs()->file_path;
            FileProperties props = os_properties_from_file_path(path);
            if(props.created != 0)
            {
              rd_cmd(RD_CmdKind_RecordFileInProject);
              rd_cmd(RD_CmdKind_OpenTab,
                     .string = rd_eval_string_from_file_path(scratch.arena, path),
                     .params_tree = md_tree_from_string(scratch.arena, rd_view_rule_kind_info_table[RD_ViewRuleKind_PendingFile].string)->first);
            }
            else
            {
              log_user_errorf("Couldn't open file at \"%S\".", path);
            }
          }break;
          case RD_CmdKind_Switch:
          {
#if 0 // TODO(rjf): @cfg
            RD_Window *ws = rd_window_from_handle(rd_regs()->window);
            RD_Panel *src_panel = rd_panel_from_handle(rd_regs()->panel);
            RD_View *src_view = rd_view_from_handle(rd_regs()->view);
            RD_ViewRuleKind src_view_kind = rd_view_rule_kind_from_string(src_view->spec->string);
            RD_Entity *recent_file = rd_entity_from_handle(rd_regs()->entity);
            if(!rd_entity_is_nil(recent_file))
            {
              String8 recent_file_path = recent_file->string;
              RD_Panel *existing_panel = &rd_nil_panel;
              RD_View *existing_view = &rd_nil_view;
              for(RD_Panel *panel = ws->root_panel; !rd_panel_is_nil(panel); panel = rd_panel_rec_depth_first_pre(panel).next)
              {
                if(!rd_panel_is_nil(panel->first))
                {
                  continue;
                }
                for(RD_View *v = panel->first_tab_view; !rd_view_is_nil(v); v = v->order_next)
                {
                  if(rd_view_is_project_filtered(v)) { continue; }
                  String8 v_path = rd_file_path_from_eval_string(scratch.arena, str8(v->query_buffer, v->query_string_size));
                  RD_ViewRuleKind v_kind = rd_view_rule_kind_from_string(v->spec->string);
                  if(str8_match(v_path, recent_file_path, StringMatchFlag_CaseInsensitive) && v_kind == src_view_kind)
                  {
                    existing_panel = panel;
                    existing_view = v;
                    goto done_existing_view_search__switch;
                  }
                }
              }
              done_existing_view_search__switch:;
              if(rd_view_is_nil(existing_view))
              {
                rd_cmd(RD_CmdKind_OpenTab,
                       .string = rd_eval_string_from_file_path(scratch.arena, recent_file_path),
                       .params_tree = md_tree_from_string(scratch.arena, rd_view_rule_kind_info_table[RD_ViewRuleKind_PendingFile].string)->first);
              }
              else
              {
                rd_cmd(RD_CmdKind_FocusPanel, .panel = rd_handle_from_panel(existing_panel));
                existing_panel->selected_tab_view = rd_handle_from_view(existing_view);
              }
            }
#endif
          }break;
          case RD_CmdKind_SwitchToPartnerFile:
          {
#if 0 // TODO(rjf): @cfg
            RD_Panel *panel = rd_panel_from_handle(rd_regs()->panel);
            RD_View *view = rd_selected_tab_from_panel(panel);
            {
              String8 file_path      = rd_file_path_from_eval_string(scratch.arena, str8(view->query_buffer, view->query_string_size));
              String8 file_full_path = path_normalized_from_string(scratch.arena, file_path);
              String8 file_folder    = str8_chop_last_slash(file_full_path);
              String8 file_name      = str8_skip_last_slash(str8_chop_last_dot(file_full_path));
              String8 file_ext       = str8_skip_last_dot(file_full_path);
              String8 partner_ext_candidates[] =
              {
                str8_lit_comp("h"),
                str8_lit_comp("hpp"),
                str8_lit_comp("hxx"),
                str8_lit_comp("c"),
                str8_lit_comp("cc"),
                str8_lit_comp("cxx"),
                str8_lit_comp("cpp"),
              };
              for(U64 idx = 0; idx < ArrayCount(partner_ext_candidates); idx += 1)
              {
                if(!str8_match(partner_ext_candidates[idx], file_ext, StringMatchFlag_CaseInsensitive))
                {
                  String8 candidate = push_str8f(scratch.arena, "%S.%S", file_name, partner_ext_candidates[idx]);
                  String8 candidate_path = push_str8f(scratch.arena, "%S/%S", file_folder, candidate);
                  FileProperties candidate_props = os_properties_from_file_path(candidate_path);
                  if(candidate_props.modified != 0)
                  {
                    RD_Entity *recent_file = rd_entity_from_name_and_kind(candidate_path, RD_EntityKind_RecentFile);
                    if(!rd_entity_is_nil(recent_file))
                    {
                      rd_cmd(RD_CmdKind_Switch, .entity = rd_handle_from_entity(recent_file));
                    }
                    else
                    {
                      rd_cmd(RD_CmdKind_RecordFileInProject, .file_path = candidate_path);
                      rd_cmd(RD_CmdKind_OpenTab, .string = rd_eval_string_from_file_path(scratch.arena, candidate_path), .params_tree = md_tree_from_string(scratch.arena, view->spec->string)->first);
                    }
                    break;
                  }
                }
              }
            }
#endif
          }break;
          case RD_CmdKind_RecordFileInProject:
          if(rd_regs()->file_path.size != 0)
          {
            String8 path = path_normalized_from_string(scratch.arena, rd_regs()->file_path);
            RD_Cfg *project = rd_cfg_child_from_string(rd_state->root_cfg, str8_lit("project"));
            RD_CfgList recent_files = rd_cfg_child_list_from_string(scratch.arena, project, str8_lit("recent_files"));
            RD_Cfg *recent_file = &rd_nil_cfg;
            for(RD_CfgNode *n = recent_files.first; n != 0; n = n->next)
            {
              if(path_match_normalized(n->v->string, path))
              {
                recent_file = n->v;
                break;
              }
            }
            if(recent_file == &rd_nil_cfg)
            {
              recent_file = rd_cfg_new(project, str8_lit("recent_file"));
              rd_cfg_new(recent_file, path);
            }
            rd_cfg_unhook(project, recent_file);
            rd_cfg_insert_child(project, &rd_nil_cfg, recent_file);
            recent_files = rd_cfg_child_list_from_string(scratch.arena, project, str8_lit("recent_files"));
            if(recent_files.count > 256)
            {
              rd_cfg_release(recent_files.last->v);
            }
          }break;
          case RD_CmdKind_ShowFileInExplorer:
          if(rd_regs()->file_path.size != 0)
          {
            String8 full_path = path_normalized_from_string(scratch.arena, rd_regs()->file_path);
            os_show_in_filesystem_ui(full_path);
          }break;
          
          //- rjf: source <-> disasm
          case RD_CmdKind_GoToDisassembly:
          {
            CTRL_Entity *thread = ctrl_entity_from_handle(d_state->ctrl_entity_store, rd_regs()->thread);
            U64 vaddr = 0;
            for(D_LineNode *n = rd_regs()->lines.first; n != 0; n = n->next)
            {
              CTRL_EntityList modules = ctrl_modules_from_dbgi_key(scratch.arena, d_state->ctrl_entity_store, &n->v.dbgi_key);
              CTRL_Entity *module = ctrl_module_from_thread_candidates(d_state->ctrl_entity_store, thread, &modules);
              if(module != &ctrl_entity_nil)
              {
                vaddr = ctrl_vaddr_from_voff(module, n->v.voff_range.min);
                break;
              }
            }
            rd_cmd(RD_CmdKind_FindCodeLocation, .process = ctrl_entity_ancestor_from_kind(thread, CTRL_EntityKind_Process)->handle, .vaddr = vaddr, .prefer_disasm = 1);
          }break;
          case RD_CmdKind_GoToSource:
          {
            if(rd_regs()->lines.first != 0)
            {
              rd_cmd(RD_CmdKind_FindCodeLocation,
                     .file_path = rd_regs()->lines.first->v.file_path,
                     .cursor    = rd_regs()->lines.first->v.pt,
                     .vaddr     = 0,
                     .process   = ctrl_handle_zero(),
                     .prefer_disasm = 0);
            }
          }break;
          
          //- rjf: panel built-in layout builds
          case RD_CmdKind_ResetToDefaultPanels:
          case RD_CmdKind_ResetToCompactPanels:
          {
            RD_Cfg *window = rd_cfg_from_handle(rd_regs()->window);
            RD_Cfg *panels = rd_cfg_child_from_string(window, str8_lit("panels"));
            RD_PanelTree panel_tree = rd_panel_tree_from_cfg(scratch.arena, window);
            
            //- rjf: define all of the "fixed" tabs we care about
#define FixedTab_XList \
X(watch)\
X(locals)\
X(registers)\
X(globals)\
X(thread_locals)\
X(types)\
X(procedures)\
X(call_stack)\
X(breakpoints)\
X(watch_pins)\
X(output)\
X(targets)\
X(scheduler)\
X(modules)\
X(disasm)\
X(memory)\
X(getting_started)
#define X(name) RD_Cfg *name = &rd_nil_cfg;
            FixedTab_XList
#undef X
            
            //- rjf: find all the fixed tabs, and all text viewers
            RD_CfgList texts = {0};
            for(RD_PanelNode *panel = panel_tree.root;
                panel != &rd_nil_panel_node;
                panel = rd_panel_node_rec__depth_first_pre(panel_tree.root, panel).next)
            {
              for(RD_CfgNode *n = panel->tabs.first; n != 0; n = n->next)
              {
                RD_Cfg *tab = n->v;
                B32 need_unhook = 1;
                if(0){}
#define X(name) else if(str8_match(tab->string, str8_lit(#name), 0)) {name = tab;}
                FixedTab_XList
#undef X
                else if(str8_match(tab->string, str8_lit("text"), 0)) {rd_cfg_list_push(scratch.arena, &texts, tab);}
                else
                {
                  need_unhook = 0;
                }
                if(need_unhook)
                {
                  rd_cfg_unhook(panel->cfg, tab);
                }
              }
            }
            
            //- rjf: release the old panel tree
            rd_cfg_release(panels);
            
            //- rjf: allocate any missing tabs
#define X(name) if(name == &rd_nil_cfg) {name = rd_cfg_alloc(); rd_cfg_equip_string(name, str8_lit(#name));}
            FixedTab_XList
#undef X
            
            //- rjf: eliminate all tab selections
#define X(name) if(name != &rd_nil_cfg) {rd_cfg_release(rd_cfg_child_from_string(name, str8_lit("selected")));}
            FixedTab_XList
#undef X
            for(RD_CfgNode *n = texts.first; n != 0; n = n->next)
            {
              rd_cfg_release(rd_cfg_child_from_string(n->v, str8_lit("selected")));
            }
            
            //- rjf: create the panel root
            panels = rd_cfg_new(window, str8_lit("panels"));
            
            //- rjf: rebuild the new panel tree
            switch(kind)
            {
              default:{}break;
              
              //- rjf: (default layout)
              case RD_CmdKind_ResetToDefaultPanels:
              {
                // rjf: root split
                rd_cfg_child_from_string_or_alloc(window, str8_lit("split_x"));
                RD_Cfg *root_0 = rd_cfg_new(panels, str8_lit("0.85"));
                RD_Cfg *root_1 = rd_cfg_new(panels, str8_lit("0.15"));
                
                // rjf: root_0 split
                RD_Cfg *root_0_0 = rd_cfg_new(root_0, str8_lit("0.80"));
                RD_Cfg *root_0_1 = rd_cfg_new(root_0, str8_lit("0.20"));
                
                // rjf: root_1 split
                RD_Cfg *root_1_0 = rd_cfg_new(root_1, str8_lit("0.50"));
                RD_Cfg *root_1_1 = rd_cfg_new(root_1, str8_lit("0.50"));
                rd_cfg_insert_child(root_1_0, root_1_0->last, targets);
                rd_cfg_insert_child(root_1_1, root_1_1->last, scheduler);
                rd_cfg_new(targets, str8_lit("selected"));
                rd_cfg_new(scheduler, str8_lit("selected"));
                
                // rjf: root 0_0 split
                RD_Cfg *root_0_0_0 = rd_cfg_new(root_0_0, str8_lit("0.25"));
                RD_Cfg *root_0_0_1 = rd_cfg_new(root_0_0, str8_lit("0.75"));
                
                // rjf: root_0_0_0 split
                RD_Cfg *root_0_0_0_0 = rd_cfg_new(root_0_0_0, str8_lit("0.50"));
                RD_Cfg *root_0_0_0_1 = rd_cfg_new(root_0_0_0, str8_lit("0.50"));
                rd_cfg_insert_child(root_0_0_0_0, root_0_0_0_0->last, disasm);
                rd_cfg_new(disasm, str8_lit("selected"));
                rd_cfg_insert_child(root_0_0_0_1, root_0_0_0_1->last, breakpoints);
                rd_cfg_insert_child(root_0_0_0_1, root_0_0_0_1->last, watch_pins);
                rd_cfg_insert_child(root_0_0_0_1, root_0_0_0_1->last, output);
                rd_cfg_insert_child(root_0_0_0_1, root_0_0_0_1->last, memory);
                rd_cfg_new(output, str8_lit("selected"));
                
                // rjf: root_0_1 split
                RD_Cfg *root_0_1_0 = rd_cfg_new(root_0_1, str8_lit("0.60"));
                RD_Cfg *root_0_1_1 = rd_cfg_new(root_0_1, str8_lit("0.40"));
                rd_cfg_insert_child(root_0_1_0, root_0_1_0->last, watch);
                rd_cfg_insert_child(root_0_1_0, root_0_1_0->last, locals);
                rd_cfg_insert_child(root_0_1_0, root_0_1_0->last, registers);
                rd_cfg_insert_child(root_0_1_0, root_0_1_0->last, globals);
                rd_cfg_insert_child(root_0_1_0, root_0_1_0->last, thread_locals);
                rd_cfg_insert_child(root_0_1_0, root_0_1_0->last, types);
                rd_cfg_insert_child(root_0_1_0, root_0_1_0->last, procedures);
                rd_cfg_new(watch, str8_lit("selected"));
                rd_cfg_new(root_0_1_0, str8_lit("tabs_on_bottom"));
                rd_cfg_insert_child(root_0_1_1, root_0_1_1->last, call_stack);
                rd_cfg_insert_child(root_0_1_1, root_0_1_1->last, modules);
                rd_cfg_new(call_stack, str8_lit("selected"));
                rd_cfg_new(root_0_1_1, str8_lit("tabs_on_bottom"));
                
                // rjf: fill main panel with getting started, OR all collected code views
                RD_Cfg *main_panel = root_0_0_1;
                if(getting_started != &rd_nil_cfg)
                {
                  rd_cfg_insert_child(main_panel, main_panel->last, getting_started);
                  rd_cfg_new(getting_started, str8_lit("selected"));
                }
                else if(texts.first)
                {
                  rd_cfg_new(texts.first->v, str8_lit("selected"));
                }
                for(RD_CfgNode *n = texts.first; n != 0; n = n->next)
                {
                  rd_cfg_insert_child(main_panel, main_panel->last, n->v);
                }
                
                // rjf: set main panel as selected
                rd_cfg_new(main_panel, str8_lit("selected"));
              }break;
              
              //- rjf: (compact layout)
              case RD_CmdKind_ResetToCompactPanels:
              {
                // rjf: root split
                rd_cfg_child_from_string_or_alloc(window, str8_lit("split_x"));
                RD_Cfg *root_0 = rd_cfg_new(panels, str8_lit("0.25"));
                RD_Cfg *root_1 = rd_cfg_new(panels, str8_lit("0.75"));
                
                // rjf: root_0 split
                RD_Cfg *root_0_0 = rd_cfg_new(root_0, str8_lit("0.25"));
                RD_Cfg *root_0_1 = rd_cfg_new(root_0, str8_lit("0.25"));
                RD_Cfg *root_0_2 = rd_cfg_new(root_0, str8_lit("0.25"));
                RD_Cfg *root_0_3 = rd_cfg_new(root_0, str8_lit("0.25"));
                rd_cfg_insert_child(root_0_0, root_0_0->last, watch);
                rd_cfg_insert_child(root_0_0, root_0_0->last, types);
                rd_cfg_new(watch, str8_lit("selected"));
                rd_cfg_insert_child(root_0_1, root_0_1->last, scheduler);
                rd_cfg_insert_child(root_0_1, root_0_1->last, targets);
                rd_cfg_insert_child(root_0_1, root_0_1->last, breakpoints);
                rd_cfg_insert_child(root_0_1, root_0_1->last, watch_pins);
                rd_cfg_new(scheduler, str8_lit("selected"));
                rd_cfg_insert_child(root_0_2, root_0_2->last, disasm);
                rd_cfg_insert_child(root_0_2, root_0_2->last, output);
                rd_cfg_new(disasm, str8_lit("selected"));
                rd_cfg_insert_child(root_0_3, root_0_3->last, call_stack);
                rd_cfg_insert_child(root_0_3, root_0_3->last, modules);
                rd_cfg_new(call_stack, str8_lit("selected"));
                
                // rjf: fill main panel with getting started, OR all collected code views
                RD_Cfg *main_panel = root_1;
                if(getting_started != &rd_nil_cfg)
                {
                  rd_cfg_insert_child(main_panel, main_panel->last, getting_started);
                  rd_cfg_new(getting_started, str8_lit("selected"));
                }
                else if(texts.first)
                {
                  rd_cfg_new(texts.first->v, str8_lit("selected"));
                }
                for(RD_CfgNode *n = texts.first; n != 0; n = n->next)
                {
                  rd_cfg_insert_child(main_panel, main_panel->last, n->v);
                }
                
                // rjf: set main panel as selected
                rd_cfg_new(main_panel, str8_lit("selected"));
              }break;
            }
            
            //- rjf: release any unused views from the previous layout
#define X(name) if(name->parent == &rd_nil_cfg) {rd_cfg_release(name);}
            FixedTab_XList
#undef X
          }break;
          
          //- rjf: thread finding
          case RD_CmdKind_FindThread:
          for(RD_WindowState *ws = rd_state->first_window_state; ws != &rd_nil_window_state; ws = ws->order_next)
          {
            DI_Scope *scope = di_scope_open();
            CTRL_Entity *thread = ctrl_entity_from_handle(d_state->ctrl_entity_store, rd_regs()->thread);
            U64 unwind_index = rd_regs()->unwind_count;
            U64 inline_depth = rd_regs()->inline_depth;
            if(thread->kind == CTRL_EntityKind_Thread)
            {
              // rjf: grab rip
              U64 rip_vaddr = d_query_cached_rip_from_thread_unwind(thread, unwind_index);
              
              // rjf: extract thread/rip info
              CTRL_Entity *process = ctrl_entity_ancestor_from_kind(thread, CTRL_EntityKind_Process);
              CTRL_Entity *module = ctrl_module_from_process_vaddr(process, rip_vaddr);
              DI_Key dbgi_key = ctrl_dbgi_key_from_module(module);
              RDI_Parsed *rdi = di_rdi_from_key(scope, &dbgi_key, 0);
              U64 rip_voff = ctrl_voff_from_vaddr(module, rip_vaddr);
              D_LineList lines = d_lines_from_dbgi_key_voff(scratch.arena, &dbgi_key, rip_voff);
              D_Line line = {0};
              {
                U64 idx = 0;
                for(D_LineNode *n = lines.first; n != 0; n = n->next, idx += 1)
                {
                  line = n->v;
                  if(idx == inline_depth)
                  {
                    break;
                  }
                }
              }
              
              // rjf: snap to resolved line
              B32 missing_rip   = (rip_vaddr == 0);
              B32 dbgi_missing  = (dbgi_key.min_timestamp == 0 || dbgi_key.path.size == 0);
              B32 dbgi_pending  = !dbgi_missing && rdi == &di_rdi_parsed_nil;
              B32 has_line_info = (line.voff_range.max != 0);
              B32 has_module    = (module != &ctrl_entity_nil);
              B32 has_dbg_info  = has_module && !dbgi_missing;
              if(!dbgi_pending && (has_line_info || has_module))
              {
                rd_cmd(RD_CmdKind_FindCodeLocation,
                       .file_path    = line.file_path,
                       .cursor       = line.pt,
                       .process      = process->handle,
                       .voff         = rip_voff,
                       .vaddr        = rip_vaddr,
                       .unwind_count = unwind_index,
                       .inline_depth = inline_depth);
              }
              
              // rjf: snap to resolved address w/o line info
              if(!missing_rip && !dbgi_pending && !has_line_info && !has_module)
              {
                rd_cmd(RD_CmdKind_FindCodeLocation,
                       .file_path    = str8_zero(),
                       .process      = process->handle,
                       .module       = module->handle,
                       .voff         = rip_voff,
                       .vaddr        = rip_vaddr,
                       .unwind_count = unwind_index,
                       .inline_depth = inline_depth);
              }
              
              // rjf: retry on stopped, pending debug info
              if(!d_ctrl_targets_running() && (dbgi_pending || missing_rip))
              {
                find_thread_retry = thread->handle;
              }
            }
            di_scope_close(scope);
          }break;
          case RD_CmdKind_FindSelectedThread:
          for(RD_WindowState *ws = rd_state->first_window_state; ws != &rd_nil_window_state; ws = ws->order_next)
          {
            CTRL_Entity *selected_thread = ctrl_entity_from_handle(d_state->ctrl_entity_store, rd_base_regs()->thread);
            rd_cmd(RD_CmdKind_FindThread,
                   .thread       = selected_thread->handle,
                   .unwind_count = rd_base_regs()->unwind_count,
                   .inline_depth = rd_base_regs()->inline_depth);
          }break;
          
          //- rjf: name finding
          case RD_CmdKind_GoToName:
          {
            String8 name = rd_regs()->string;
            if(name.size != 0)
            {
              B32 name_resolved = 0;
              
              // rjf: try to resolve name as a symbol
              U64 voff = 0;
              DI_Key voff_dbgi_key = {0};
              if(!name_resolved)
              {
                DI_KeyList keys = d_push_active_dbgi_key_list(scratch.arena);
                for(DI_KeyNode *n = keys.first; n != 0; n = n->next)
                {
                  U64 binary_voff = d_voff_from_dbgi_key_symbol_name(&n->v, name);
                  if(binary_voff != 0)
                  {
                    voff = binary_voff;
                    voff_dbgi_key = n->v;
                    name_resolved = 1;
                    break;
                  }
                }
              }
              
              // rjf: try to resolve name as a file
              String8 file_path = {0};
              if(!name_resolved)
              {
                // rjf: unpack quoted portion of string
                String8 file_part_of_name = name;
                U64 quote_pos = str8_find_needle(name, 0, str8_lit("\""), 0);
                if(quote_pos < name.size)
                {
                  file_part_of_name = str8_skip(name, quote_pos+1);
                  U64 ender_quote_pos = str8_find_needle(file_part_of_name, 0, str8_lit("\""), 0);
                  file_part_of_name = str8_prefix(file_part_of_name, ender_quote_pos);
                }
                String8List search_parts = str8_split_path(scratch.arena, file_part_of_name);
                
                // rjf: get source path
                RD_Cfg *src_view = rd_cfg_from_handle(rd_regs()->view);
                String8 src_view_expr = rd_view_expr_string();
                String8 src_file_path = rd_file_path_from_eval_string(scratch.arena, src_view_expr);
                String8List src_file_parts = str8_split_path(scratch.arena, src_file_path);
                
                // rjf: search for actual file
                Temp temp = temp_begin(scratch.arena);
                for(String8Node *n = src_file_parts.first; n != 0; n = n->next)
                {
                  temp_end(temp);
                  String8List try_path_parts = {0};
                  for(String8Node *src_n = src_file_parts.first; src_n != n && src_n != 0; src_n = src_n->next)
                  {
                    str8_list_push(temp.arena, &try_path_parts, src_n->string);
                  }
                  for(String8Node *try_n = search_parts.first; try_n != 0; try_n = try_n->next)
                  {
                    str8_list_push(temp.arena, &try_path_parts, try_n->string);
                  }
                  String8 try_path = str8_list_join(temp.arena, &try_path_parts, &(StringJoin){.sep = str8_lit("/")});
                  FileProperties try_props = os_properties_from_file_path(try_path);
                  if(try_props.modified != 0)
                  {
                    name_resolved = 1;
                    file_path = try_path;
                    break;
                  }
                }
              }
              
              // rjf: process resolved info
              if(!name_resolved)
              {
                log_user_errorf("`%S` could not be found.", name);
              }
              
              // rjf: name resolved to voff * dbg info
              if(name_resolved && voff != 0)
              {
                D_LineList lines = d_lines_from_dbgi_key_voff(scratch.arena, &voff_dbgi_key, voff);
                if(lines.first != 0)
                {
                  CTRL_Entity *process = &ctrl_entity_nil;
                  U64 vaddr = 0;
                  if(voff_dbgi_key.path.size != 0)
                  {
                    CTRL_EntityList modules = ctrl_modules_from_dbgi_key(scratch.arena, d_state->ctrl_entity_store, &voff_dbgi_key);
                    CTRL_Entity *module = ctrl_entity_list_first(&modules);
                    process = ctrl_entity_ancestor_from_kind(module, CTRL_EntityKind_Process);
                    if(process != &ctrl_entity_nil)
                    {
                      vaddr = module->vaddr_range.min + lines.first->v.voff_range.min;
                    }
                  }
                  rd_cmd(RD_CmdKind_FindCodeLocation,
                         .file_path = lines.first->v.file_path,
                         .cursor    = lines.first->v.pt,
                         .process   = process->handle,
                         .module    = module->handle,
                         .vaddr     = module->vaddr_range.min + lines.first->v.voff_range.min);
                }
              }
              
              // rjf: name resolved to a file path
              if(name_resolved && file_path.size != 0)
              {
                rd_cmd(RD_CmdKind_FindCodeLocation, .file_path = file_path, .cursor = txt_pt(1, 1), .vaddr = 0);
              }
            }
          }break;
          
          //- rjf: snap-to-code-location
          case RD_CmdKind_FindCodeLocation:
          {
            // NOTE(rjf): This command is where a lot of high-level flow things
            // in the debugger come together. It's that codepath that runs any
            // time a source code location is clicked in the UI, when a thread
            // is selected, or when a thread causes a halt (hitting a breakpoint
            // or exception or something). This is the logic that manages the
            // flow of how views and panels are changed, opened, etc. when
            // something like that happens.
            //
            // The gist of the intended rule for textual source code locations
            // is the following:
            //
            // 1. Try to find a panel that's viewing the file (has it open in a
            //    tab, *and* that tab is selected).
            // 2. Try to find a panel that has the file open in a tab, but does not
            //    currently have that tab selected.
            // 3. Try to find a panel that has ANY source code open in any tab.
            // 4. If the above things fail, try to pick the biggest panel, which
            //    is generally a decent rule (because it matches the popular
            //    debugger usage UI paradigm).
            //
            // The reason why this is a little more complicated than you might
            // imagine is because this debugger frontend does not have any special
            // "code panels" or anything like that, unlike e.g. VS or Remedy. All
            // panels are identical in nature to allow for the user to organize
            // the interface how they want, but in cases like this, we have to
            // "fish out" the best option given the user's configuration. This
            // can't be what the user wants in 100% of cases (this program cannot
            // read anyone's mind), but it does provide expected behavior in
            // common cases.
            //
            // The gist of the intended rule for finding disassembly locations is
            // the following:
            //
            // 1. Try to find a panel that's viewing disassembly already - if so,
            //    snap it to the right address.
            // 2. If there is no disassembly tab open, then we need to open one
            //    ONLY if source code was not found.
            // 3. If we need to open a disassembly tab, we will first try to pick
            //    the biggest empty panel.
            // 4. If there is no empty panel, then we will pick the biggest
            //    panel.
            RD_Cfg *window = rd_cfg_from_handle(rd_regs()->window);
            RD_PanelTree panel_tree = rd_panel_tree_from_cfg(scratch.arena, window);
            
            // rjf: grab things to find. path * point, process * address, etc.
            String8 file_path = {0};
            TxtPt point = {0};
            CTRL_Entity *thread = &ctrl_entity_nil;
            CTRL_Entity *process = &ctrl_entity_nil;
            U64 vaddr = 0;
            B32 require_disasm_snap = 0;
            {
              file_path = rd_mapped_from_file_path(scratch.arena, rd_regs()->file_path);
              point     = rd_regs()->cursor;
              thread    = ctrl_entity_from_handle(d_state->ctrl_entity_store, rd_regs()->thread);
              process   = ctrl_entity_from_handle(d_state->ctrl_entity_store, rd_regs()->process);
              vaddr     = rd_regs()->vaddr;
              if(file_path.size == 0)
              {
                require_disasm_snap = 1;
              }
            }
            
            // rjf: given a src code location, if no vaddr is specified,
            // try to map the src coordinates to a vaddr via line info
            if(vaddr == 0 && file_path.size != 0)
            {
              D_LineList lines = d_lines_from_file_path_line_num(scratch.arena, file_path, point.line);
              for(D_LineNode *n = lines.first; n != 0; n = n->next)
              {
                CTRL_EntityList modules = ctrl_modules_from_dbgi_key(scratch.arena, d_state->ctrl_entity_store, &n->v.dbgi_key);
                CTRL_Entity *module = ctrl_module_from_thread_candidates(d_state->ctrl_entity_store, thread, &modules);
                vaddr = ctrl_vaddr_from_voff(module, n->v.voff_range.min);
                break;
              }
            }
            
            // rjf: first, try to find panel/view pair that already has the src file open
            RD_PanelNode *panel_w_this_src_code = &rd_nil_panel_node;
            RD_Cfg *view_w_this_src_code = &rd_nil_cfg;
            for(RD_PanelNode *panel = panel_tree.root;
                panel != &rd_nil_panel_node;
                panel = rd_panel_node_rec__depth_first_pre(panel_tree.root, panel).next)
            {
              if(panel->first != &rd_nil_panel_node)
              {
                continue;
              }
              for(RD_CfgNode *tab_n = panel->tabs.first; tab_n != 0; tab_n = tab_n->next)
              {
                RD_Cfg *tab = tab_n->v;
                if(rd_cfg_is_project_filtered(tab)) { continue; }
                RD_RegsScope(.view = rd_handle_from_cfg(tab))
                {
                  String8 tab_expr = rd_view_expr_string();
                  String8 tab_file_path = rd_file_path_from_eval_string(scratch.arena, tab_expr);
                  RD_ViewRuleKind tab_view_kind = rd_view_rule_kind_from_string(tab->string);
                  if((tab_view_kind == RD_ViewRuleKind_Text || tab_view_kind == RD_ViewRuleKind_PendingFile) &&
                     path_match_normalized(tab_file_path, file_path))
                  {
                    panel_w_this_src_code = panel;
                    view_w_this_src_code = tab;
                    if(tab == panel->selected_tab)
                    {
                      break;
                    }
                  }
                }
              }
            }
            
            // rjf: find a panel that already has *any* code open (prioritize largest)
            RD_PanelNode *panel_w_any_src_code = &rd_nil_panel_node;
            {
              Rng2F32 root_rect = r2f32(v2f32(0, 0), v2f32(1000, 1000));
              F32 best_panel_area = 0;
              for(RD_PanelNode *panel = panel_tree.root;
                  panel != &rd_nil_panel_node;
                  panel = rd_panel_node_rec__depth_first_pre(panel_tree.root, panel).next)
              {
                if(panel->first != &rd_nil_panel_node)
                {
                  continue;
                }
                Rng2F32 panel_rect = rd_target_rect_from_panel_node(root_rect, panel_tree.root, panel);
                Vec2F32 panel_rect_dim = dim_2f32(panel_rect);
                F32 panel_area = panel_rect_dim.x*panel_rect_dim.y;
                for(RD_CfgNode *tab_n = panel->tabs.first; tab_n != 0; tab_n = tab_n->next)
                {
                  RD_Cfg *tab = tab_n->v;
                  if(rd_cfg_is_project_filtered(tab)) { continue; }
                  RD_ViewRuleKind view_kind = rd_view_rule_kind_from_string(tab->string);
                  if(view_kind == RD_ViewRuleKind_Text && panel_area > best_panel_area)
                  {
                    panel_w_any_src_code = panel;
                    best_panel_area = panel_area;
                    break;
                  }
                }
              }
            }
            
            // rjf: try to find panel/view pair that has disassembly open (prioritize largest)
            RD_PanelNode *panel_w_disasm = &rd_nil_panel_node;
            RD_Cfg *view_w_disasm = &rd_nil_cfg;
            {
              Rng2F32 root_rect = r2f32(v2f32(0, 0), v2f32(1000, 1000));
              F32 best_panel_area = 0;
              for(RD_PanelNode *panel = panel_tree.root;
                  panel != &rd_nil_panel_node;
                  panel = rd_panel_node_rec__depth_first_pre(panel_tree.root, panel).next)
              {
                if(panel->first != &rd_nil_panel_node)
                {
                  continue;
                }
                Rng2F32 panel_rect = rd_target_rect_from_panel_node(root_rect, panel_tree.root, panel);
                Vec2F32 panel_rect_dim = dim_2f32(panel_rect);
                F32 panel_area = panel_rect_dim.x*panel_rect_dim.y;
                for(RD_CfgNode *tab_n = panel->tabs.first; tab_n != 0; tab_n = tab_n->next)
                {
                  RD_Cfg *tab = tab_n->v;
                  if(rd_cfg_is_project_filtered(tab)) { continue; }
                  RD_RegsScope(.view = rd_handle_from_cfg(tab))
                  {
                    B32 tab_is_selected = (tab == panel->selected_tab);
                    RD_ViewRuleKind view_kind = rd_view_rule_kind_from_string(tab->string);
                    String8 expr_string = rd_view_expr_string();
                    if(view_kind == RD_ViewRuleKind_Disasm && expr_string.size == 0 && panel_area > best_panel_area)
                    {
                      panel_w_disasm = panel;
                      view_w_disasm = tab;
                      best_panel_area = panel_area;
                      if(tab_is_selected)
                      {
                        break;
                      }
                    }
                  }
                }
              }
            }
            
            // rjf: find the biggest panel
            RD_PanelNode *biggest_panel = &rd_nil_panel_node;
            {
              Rng2F32 root_rect = r2f32(v2f32(0, 0), v2f32(1000, 1000));
              F32 best_panel_area = 0;
              for(RD_PanelNode *panel = panel_tree.root;
                  panel != &rd_nil_panel_node;
                  panel = rd_panel_node_rec__depth_first_pre(panel_tree.root, panel).next)
              {
                if(panel->first != &rd_nil_panel_node)
                {
                  continue;
                }
                Rng2F32 panel_rect = rd_target_rect_from_panel_node(root_rect, panel_tree.root, panel);
                Vec2F32 panel_rect_dim = dim_2f32(panel_rect);
                F32 panel_area = panel_rect_dim.x*panel_rect_dim.y;
                if((best_panel_area == 0 || panel_area > best_panel_area))
                {
                  best_panel_area = panel_area;
                  biggest_panel = panel;
                }
              }
            }
            
            // rjf: find the biggest empty panel
            RD_PanelNode *biggest_empty_panel = &rd_nil_panel_node;
            {
              Rng2F32 root_rect = r2f32(v2f32(0, 0), v2f32(1000, 1000));
              F32 best_panel_area = 0;
              for(RD_PanelNode *panel = panel_tree.root;
                  panel != &rd_nil_panel_node;
                  panel = rd_panel_node_rec__depth_first_pre(panel_tree.root, panel).next)
              {
                if(panel->first != &rd_nil_panel_node)
                {
                  continue;
                }
                Rng2F32 panel_rect = rd_target_rect_from_panel_node(root_rect, panel_tree.root, panel);
                Vec2F32 panel_rect_dim = dim_2f32(panel_rect);
                F32 panel_area = panel_rect_dim.x*panel_rect_dim.y;
                B32 panel_is_empty = 1;
                for(RD_CfgNode *n = panel->tabs.first; n != 0; n = n->next)
                {
                  RD_Cfg *tab = n->v;
                  if(!rd_cfg_is_project_filtered(tab))
                  {
                    panel_is_empty = 0;
                    break;
                  }
                }
                if(panel_is_empty && (best_panel_area == 0 || panel_area > best_panel_area))
                {
                  best_panel_area = panel_area;
                  biggest_empty_panel = panel;
                }
              }
            }
            
            // rjf: choose panel for source code
            RD_PanelNode *src_code_dst_panel = &rd_nil_panel_node;
            {
              if(src_code_dst_panel == &rd_nil_panel_node) { src_code_dst_panel = panel_w_this_src_code; }
              if(src_code_dst_panel == &rd_nil_panel_node) { src_code_dst_panel = panel_w_any_src_code; }
              if(src_code_dst_panel == &rd_nil_panel_node) { src_code_dst_panel = biggest_empty_panel; }
              if(src_code_dst_panel == &rd_nil_panel_node) { src_code_dst_panel = biggest_panel; }
            }
            
            // rjf: choose panel for disassembly
            RD_PanelNode *disasm_dst_panel = &rd_nil_panel_node;
            {
              if(disasm_dst_panel == &rd_nil_panel_node) { disasm_dst_panel = panel_w_disasm; }
              if(disasm_dst_panel == &rd_nil_panel_node) { disasm_dst_panel = biggest_empty_panel; }
              if(disasm_dst_panel == &rd_nil_panel_node) { disasm_dst_panel = biggest_panel; }
            }
            
            // rjf: if disasm and source code match:
            //        if disasm preferred, cancel source
            //        if source preferred, cancel disasm
            if(disasm_dst_panel == src_code_dst_panel)
            {
              if(rd_regs()->prefer_disasm)
              {
                src_code_dst_panel = &rd_nil_panel_node;
              }
              else
              {
                disasm_dst_panel = &rd_nil_panel_node;
              }
            }
            
            // rjf: if disasm is not preferred, and we have no disassembly view
            // open at all, cancel disasm, so that it doesn't open if the user
            // doesn't want it.
            if(!rd_regs()->prefer_disasm && panel_w_disasm == &rd_nil_panel_node)
            {
              disasm_dst_panel = &rd_nil_panel_node;
            }
            
            // rjf: given the above, find source code location.
            if(file_path.size != 0 && src_code_dst_panel != &rd_nil_panel_node)
            {
              RD_PanelNode *dst_panel = src_code_dst_panel;
              
              // rjf: construct new view if needed
              RD_Cfg *dst_tab = view_w_this_src_code;
              if(dst_panel != &rd_nil_panel_node && dst_tab == &rd_nil_cfg)
              {
                dst_tab = rd_cfg_new(dst_panel->cfg, str8_lit("text"));
                RD_Cfg *expr = rd_cfg_new(dst_tab, str8_lit("expression"));
                rd_cfg_new(expr, rd_eval_string_from_file_path(scratch.arena, file_path));
              }
              
              // rjf: determine if we need a contain or center
              RD_CmdKind cursor_snap_kind = RD_CmdKind_CenterCursor;
              if(dst_panel != &rd_nil_panel_node && dst_tab == view_w_this_src_code && dst_panel->selected_tab == dst_tab)
              {
                cursor_snap_kind = RD_CmdKind_ContainCursor;
              }
              
              // rjf: move cursor & snap-to-cursor
              if(dst_panel != &rd_nil_panel_node) RD_RegsScope(.panel = rd_handle_from_cfg(dst_panel->cfg),
                                                               .view  = rd_handle_from_cfg(dst_tab))
              {
                rd_cmd(RD_CmdKind_FocusTab);
                rd_cmd(RD_CmdKind_GoToLine, .cursor = point);
                rd_cmd(cursor_snap_kind);
              }
              
              // rjf: record
              rd_cmd(RD_CmdKind_RecordFileInProject, .file_path = file_path);
            }
            
            // rjf: given the above, find disassembly location.
            if(process != &ctrl_entity_nil && vaddr != 0 && disasm_dst_panel != &rd_nil_panel_node)
            {
              RD_PanelNode *dst_panel = disasm_dst_panel;
              
              // rjf: construct new tab if needed
              RD_Cfg *dst_tab = view_w_disasm;
              if(dst_panel != &rd_nil_panel_node && view_w_disasm == &rd_nil_cfg)
              {
                dst_tab = rd_cfg_new(dst_panel->cfg, str8_lit("disasm"));
              }
              
              // rjf: determine if we need a contain or center
              RD_CmdKind cursor_snap_kind = RD_CmdKind_CenterCursor;
              if(dst_tab == view_w_disasm && dst_panel->selected_tab == dst_tab)
              {
                cursor_snap_kind = RD_CmdKind_ContainCursor;
              }
              
              // rjf: move cursor & snap-to-cursor
              if(dst_panel != &rd_nil_panel_node) RD_RegsScope(.panel = rd_handle_from_cfg(dst_panel->cfg),
                                                               .view  = rd_handle_from_cfg(dst_tab))
              {
                rd_cmd(RD_CmdKind_FocusTab);
                rd_cmd(RD_CmdKind_GoToAddress, .process = process->handle, .vaddr = vaddr);
                rd_cmd(cursor_snap_kind);
              }
            }
          }break;
          
          //- rjf: filtering
          case RD_CmdKind_Filter:
          {
#if 0 // TODO(rjf): @cfg
            RD_View *view = rd_view_from_handle(rd_regs()->view);
            RD_Panel *panel = rd_panel_from_handle(rd_regs()->panel);
            B32 view_is_tab = 0;
            for(RD_View *tab = panel->first_tab_view; !rd_view_is_nil(tab); tab = tab->order_next)
            {
              if(rd_view_is_project_filtered(tab)) { continue; }
              if(tab == view)
              {
                view_is_tab = 1;
                break;
              }
            }
            if(view_is_tab && view->spec->flags & RD_ViewRuleInfoFlag_CanFilter)
            {
              view->is_filtering ^= 1;
              view->query_cursor = txt_pt(1, 1+(S64)view->query_string_size);
              view->query_mark = txt_pt(1, 1);
            }
#endif
          }break;
          case RD_CmdKind_ClearFilter:
          {
#if 0 // TODO(rjf): @cfg
            RD_View *view = rd_view_from_handle(rd_regs()->view);
            if(!rd_view_is_nil(view))
            {
              view->query_string_size = 0;
              view->is_filtering = 0;
              view->query_cursor = view->query_mark = txt_pt(1, 1);
            }
#endif
          }break;
          case RD_CmdKind_ApplyFilter:
          {
#if 0 // TODO(rjf): @cfg
            RD_View *view = rd_view_from_handle(rd_regs()->view);
            if(!rd_view_is_nil(view))
            {
              view->is_filtering = 0;
            }
#endif
          }break;
          
          //- rjf: query stack
          case RD_CmdKind_PushQuery:
          {
            RD_Cfg *wcfg = rd_cfg_from_handle(rd_regs()->window);
            RD_WindowState *ws = rd_window_state_from_cfg(wcfg);
            if(ws != &rd_nil_window_state)
            {
              Arena *arena = arena_alloc();
              RD_Lister *lister = push_array(arena, RD_Lister, 1);
              lister->arena = arena;
              lister->regs = rd_regs_copy(lister->arena, rd_regs());
              lister->input_string_size = Min(rd_regs()->string.size, sizeof(lister->input_buffer));
              lister->input_cursor = lister->input_mark = txt_pt(1, lister->input_string_size+1);
              MemoryCopy(lister->input_buffer, rd_regs()->string.str, lister->input_string_size);
              SLLStackPush(ws->top_query_lister, lister);
            }
          }break;
          case RD_CmdKind_CompleteQuery:
          {
#if 0 // TODO(rjf): @cfg
            RD_Window *ws = rd_window_from_handle(rd_regs()->window);
            String8 query_cmd_name = ws->query_cmd_name;
            RD_CmdKindInfo *info = rd_cmd_kind_info_from_string(query_cmd_name);
            RD_RegSlot slot = info->query.slot;
            
            // rjf: compound command parameters
            if(slot != RD_RegSlot_Null && !(ws->query_cmd_regs_mask[slot/64] & (1ull<<(slot%64))))
            {
              RD_Regs *regs_copy = rd_regs_copy(ws->query_cmd_arena, rd_regs());
              Rng1U64 offset_range_in_regs = rd_reg_slot_range_table[slot];
              MemoryCopy((U8 *)(ws->query_cmd_regs) + offset_range_in_regs.min,
                         (U8 *)(regs_copy) + offset_range_in_regs.min,
                         dim_1u64(offset_range_in_regs));
              ws->query_cmd_regs_mask[slot/64] |= (1ull<<(slot%64));
            }
            
            // rjf: determine if command is ready to run
            B32 command_ready = 1;
            if(slot != RD_RegSlot_Null && !(ws->query_cmd_regs_mask[slot/64] & (1ull<<(slot%64))))
            {
              command_ready = 0;
            }
            
            // rjf: end this query
            if(!(info->query.flags & RD_QueryFlag_KeepOldInput))
            {
              rd_cmd(RD_CmdKind_CancelQuery);
            }
            
            // rjf: unset command register slot, if we keep old input (and thus need
            // to re-query user)
            if(info->query.flags & RD_QueryFlag_KeepOldInput)
            {
              ws->query_cmd_regs_mask[slot/64] &= ~(1ull<<(slot%64));
            }
            
            // rjf: push command if possible
            if(command_ready)
            {
              rd_push_cmd(ws->query_cmd_name, ws->query_cmd_regs);
            }
#endif
          }break;
          case RD_CmdKind_CancelQuery:
          {
            RD_Cfg *window = rd_cfg_from_handle(rd_regs()->window);
            RD_WindowState *ws = rd_window_state_from_cfg(window);
            RD_Lister *top_lister = ws->top_query_lister;
            if(top_lister != 0)
            {
              SLLStackPop(ws->top_query_lister);
              arena_release(top_lister->arena);
            }
          }break;
          
          //- rjf: developer commands
          case RD_CmdKind_ToggleDevMenu:
          {
            RD_Cfg *window = rd_cfg_from_handle(rd_regs()->window);
            RD_WindowState *ws = rd_window_state_from_cfg(window);
            ws->dev_menu_is_open ^= 1;
          }break;
          
          //- rjf: general entity operations
          case RD_CmdKind_SelectCfg:
          case RD_CmdKind_SelectTarget:
          {
            RD_Cfg *cfg = rd_cfg_from_handle(rd_regs()->cfg);
            RD_CfgList all_of_the_same_kind = rd_cfg_top_level_list_from_string(scratch.arena, cfg->string);
            B32 is_selected = rd_disabled_from_cfg(cfg);
            for(RD_CfgNode *n = all_of_the_same_kind.first; n != 0; n = n->next)
            {
              RD_Cfg *c = n->v;
              rd_cfg_child_from_string_or_alloc(c, str8_lit("disabled"));
            }
            if(!is_selected)
            {
              rd_cfg_release(rd_cfg_child_from_string(cfg, str8_lit("disabled")));
            }
          }break;
          case RD_CmdKind_EnableCfg:
          case RD_CmdKind_EnableBreakpoint:
          case RD_CmdKind_EnableTarget:
          {
            RD_Cfg *cfg = rd_cfg_from_handle(rd_regs()->cfg);
            rd_cfg_release(rd_cfg_child_from_string(cfg, str8_lit("disabled")));
          }break;
          case RD_CmdKind_DisableCfg:
          case RD_CmdKind_DisableBreakpoint:
          case RD_CmdKind_DisableTarget:
          {
            RD_Cfg *cfg = rd_cfg_from_handle(rd_regs()->cfg);
            RD_Cfg *disabled = rd_cfg_child_from_string_or_alloc(cfg, str8_lit("disabled"));
            rd_cfg_new(disabled, str8_lit("1"));
          }break;
          case RD_CmdKind_RemoveCfg:
          {
            RD_Cfg *cfg = rd_cfg_from_handle(rd_regs()->cfg);
            rd_cfg_release(cfg);
          }break;
          case RD_CmdKind_NameCfg:
          {
            RD_Cfg *cfg = rd_cfg_from_handle(rd_regs()->cfg);
            if(rd_regs()->string.size != 0)
            {
              RD_Cfg *label = rd_cfg_child_from_string_or_alloc(cfg, str8_lit("label"));
              rd_cfg_new(label, rd_regs()->string);
            }
            else
            {
              rd_cfg_release(rd_cfg_child_from_string(cfg, str8_lit("label")));
            }
          }break;
          case RD_CmdKind_ConditionCfg:
          {
            RD_Cfg *cfg = rd_cfg_from_handle(rd_regs()->cfg);
            if(rd_regs()->string.size != 0)
            {
              RD_Cfg *cnd = rd_cfg_child_from_string_or_alloc(cfg, str8_lit("condition"));
              rd_cfg_new(cnd, rd_regs()->string);
            }
            else
            {
              rd_cfg_release(rd_cfg_child_from_string(cfg, str8_lit("condition")));
            }
          }break;
          case RD_CmdKind_DuplicateCfg:
          {
            RD_Cfg *src = rd_cfg_from_handle(rd_regs()->cfg);
            RD_Cfg *dst = rd_cfg_deep_copy(src);
            rd_cfg_insert_child(src->parent, src, dst);
          }break;
          case RD_CmdKind_RelocateCfg:
          {
            RD_Cfg *cfg = rd_cfg_from_handle(rd_regs()->cfg);
            RD_Cfg *loc = rd_cfg_child_from_string_or_alloc(cfg, str8_lit("location"));
            for(RD_Cfg *child = loc->first, *next = &rd_nil_cfg; child != &rd_nil_cfg; child = next)
            {
              next = child->next;
              rd_cfg_release(child);
            }
            if(rd_regs()->cursor.line != 0)
            {
              RD_Cfg *file = rd_cfg_new(loc, rd_regs()->file_path);
              RD_Cfg *line = rd_cfg_newf(file, "%I64d", rd_regs()->cursor.line);
              rd_cfg_newf(line, "%I64d", rd_regs()->cursor.column);
            }
            else if(rd_regs()->vaddr != 0)
            {
              rd_cfg_newf(loc, "0x%I64x", rd_regs()->vaddr);
            }
            else if(rd_regs()->string.size != 0)
            {
              rd_cfg_new(loc, rd_regs()->string);
            }
          }break;
          
          //- rjf: breakpoints
          case RD_CmdKind_AddBreakpoint:
          case RD_CmdKind_ToggleBreakpoint:
          {
            String8 file_path = rd_regs()->file_path;
            TxtPt pt = rd_regs()->cursor;
            String8 string = rd_regs()->string;
            U64 vaddr = rd_regs()->vaddr;
            if(file_path.size != 0 || string.size != 0 || vaddr != 0)
            {
              B32 removed_already_existing = 0;
              if(kind == RD_CmdKind_ToggleBreakpoint)
              {
                RD_CfgList bps = rd_cfg_top_level_list_from_string(scratch.arena, str8_lit("breakpoint"));
                for(RD_CfgNode *n = bps.first; n != 0; n = n->next)
                {
                  RD_Cfg *bp = n->v;
                  RD_Cfg *loc = rd_cfg_child_from_string(bp, str8_lit("location"));
                  S64 loc_line = 0;
                  U64 loc_vaddr = 0;
                  B32 loc_matches_file_pt = (file_path.size != 0 && path_match_normalized(loc->first->string, file_path) && try_s64_from_str8_c_rules(loc->first->first->string, &loc_line) && loc_line == pt.line);
                  B32 loc_matches_string  = (string.size != 0 && str8_match(loc->first->string, string, 0));
                  B32 loc_matches_vaddr   = (vaddr != 0 && try_u64_from_str8_c_rules(loc->first->string, &loc_vaddr) && loc_vaddr == vaddr);
                  if(loc_matches_file_pt || loc_matches_string || loc_matches_vaddr)
                  {
                    rd_cfg_release(bp);
                    removed_already_existing = 1;
                    break;
                  }
                }
              }
              if(!removed_already_existing)
              {
                RD_Cfg *project = rd_cfg_child_from_string(rd_state->root_cfg, str8_lit("project"));
                RD_Cfg *bp = rd_cfg_new(project, str8_lit("breakpoint"));
                RD_Cfg *loc = rd_cfg_new(bp, str8_lit("location"));
                if(vaddr != 0)
                {
                  rd_cfg_newf(loc, "0x%I64x", vaddr);
                }
                else if(string.size != 0)
                {
                  rd_cfg_new(loc, string);
                }
                else if(file_path.size != 0)
                {
                  RD_Cfg *file_path_cfg = rd_cfg_new(loc, file_path);
                  rd_cfg_newf(file_path_cfg, "%I64d", pt.line);
                }
              }
            }
          }break;
          case RD_CmdKind_AddAddressBreakpoint:
          {
            rd_cmd(RD_CmdKind_AddBreakpoint, .string = str8_zero());
          }break;
          case RD_CmdKind_AddFunctionBreakpoint:
          {
            rd_cmd(RD_CmdKind_AddBreakpoint, .vaddr = 0);
          }break;
          
          //- rjf: watch pins
          case RD_CmdKind_AddWatchPin:
          case RD_CmdKind_ToggleWatchPin:
          {
            String8 file_path = rd_regs()->file_path;
            TxtPt pt = rd_regs()->cursor;
            String8 string = rd_regs()->string;
            U64 vaddr = rd_regs()->vaddr;
            B32 removed_already_existing = 0;
            if(kind == RD_CmdKind_ToggleWatchPin)
            {
              RD_CfgList wps = rd_cfg_top_level_list_from_string(scratch.arena, str8_lit("watch_pin"));
              for(RD_CfgNode *n = wps.first; n != 0; n = n->next)
              {
                RD_Cfg *wp = n->v;
                RD_Cfg *expr = rd_cfg_child_from_string(wp, str8_lit("expression"));
                RD_Cfg *loc = rd_cfg_child_from_string(wp, str8_lit("location"));
                S64 loc_line = 0;
                U64 loc_vaddr = 0;
                B32 loc_matches_file_pt = (file_path.size != 0 && path_match_normalized(loc->first->string, file_path) && try_s64_from_str8_c_rules(loc->first->first->string, &loc_line) && loc_line == pt.line);
                B32 loc_matches_vaddr   = (vaddr != 0 && try_u64_from_str8_c_rules(loc->first->string, &loc_vaddr) && loc_vaddr == vaddr);
                B32 loc_matches_expr    = (string.size != 0 && str8_match(expr->first->string, string, 0));
                if(loc_matches_expr && (loc_matches_file_pt || loc_matches_vaddr))
                {
                  rd_cfg_release(wp);
                  removed_already_existing = 1;
                  break;
                }
              }
            }
            if(!removed_already_existing)
            {
              RD_Cfg *project = rd_cfg_child_from_string(rd_state->root_cfg, str8_lit("project"));
              RD_Cfg *wp = rd_cfg_new(project, str8_lit("watch_pin"));
              RD_Cfg *expr = rd_cfg_new(wp, str8_lit("expression"));
              RD_Cfg *loc = rd_cfg_new(wp, str8_lit("location"));
              rd_cfg_new(expr, string);
              if(vaddr != 0)
              {
                rd_cfg_newf(loc, "0x%I64x", vaddr);
              }
              else if(file_path.size != 0)
              {
                RD_Cfg *file_path_cfg = rd_cfg_new(loc, file_path);
                rd_cfg_newf(file_path_cfg, "%I64d", pt.line);
              }
            }
          }break;
          
          //- rjf: watches
          case RD_CmdKind_ToggleWatchExpression:
          if(rd_regs()->string.size != 0)
          {
            RD_Entity *existing_watch = rd_entity_from_name_and_kind(rd_regs()->string, RD_EntityKind_Watch);
            if(rd_entity_is_nil(existing_watch))
            {
              RD_Entity *watch = &rd_nil_entity;
              watch = rd_entity_alloc(rd_entity_root(), RD_EntityKind_Watch);
              rd_entity_equip_cfg_src(watch, RD_CfgSrc_Project);
              rd_entity_equip_name(watch, rd_regs()->string);
            }
            else
            {
              rd_entity_mark_for_deletion(existing_watch);
            }
          }break;
          
          //- rjf: cursor operations
          case RD_CmdKind_GoToNameAtCursor:
          case RD_CmdKind_ToggleWatchExpressionAtCursor:
          {
            HS_Scope *hs_scope = hs_scope_open();
            TXT_Scope *txt_scope = txt_scope_open();
            RD_Regs *regs = rd_regs();
            U128 text_key = regs->text_key;
            TXT_LangKind lang_kind = regs->lang_kind;
            TxtRng range = txt_rng(regs->cursor, regs->mark);
            U128 hash = {0};
            TXT_TextInfo info = txt_text_info_from_key_lang(txt_scope, text_key, lang_kind, &hash);
            String8 data = hs_data_from_hash(hs_scope, hash);
            Rng1U64 expr_off_range = {0};
            if(range.min.column != range.max.column)
            {
              expr_off_range = r1u64(txt_off_from_info_pt(&info, range.min), txt_off_from_info_pt(&info, range.max));
            }
            else
            {
              expr_off_range = txt_expr_off_range_from_info_data_pt(&info, data, range.min);
            }
            String8 expr = str8_substr(data, expr_off_range);
            rd_cmd((kind == RD_CmdKind_GoToNameAtCursor ? RD_CmdKind_GoToName :
                    kind == RD_CmdKind_ToggleWatchExpressionAtCursor ? RD_CmdKind_ToggleWatchExpression :
                    RD_CmdKind_GoToName),
                   .string = expr);
            txt_scope_close(txt_scope);
            hs_scope_close(hs_scope);
          }break;
          case RD_CmdKind_SetNextStatement:
          {
            CTRL_Entity *thread = ctrl_entity_from_handle(d_state->ctrl_entity_store, rd_regs()->thread);
            String8 file_path = rd_regs()->file_path;
            U64 new_rip_vaddr = rd_regs()->vaddr_range.min;
            if(file_path.size != 0)
            {
              D_LineList *lines = &rd_regs()->lines;
              for(D_LineNode *n = lines->first; n != 0; n = n->next)
              {
                CTRL_EntityList modules = ctrl_modules_from_dbgi_key(scratch.arena, d_state->ctrl_entity_store, &n->v.dbgi_key);
                CTRL_Entity *module = ctrl_module_from_thread_candidates(d_state->ctrl_entity_store, thread, &modules);
                if(module != &ctrl_entity_nil)
                {
                  new_rip_vaddr = ctrl_vaddr_from_voff(module, n->v.voff_range.min);
                  break;
                }
              }
            }
            rd_cmd(RD_CmdKind_SetThreadIP, .vaddr = new_rip_vaddr);
          }break;
          
          //- rjf: targets
          case RD_CmdKind_AddTarget:
          {
            String8 file_path = rd_regs()->file_path;
            RD_Cfg *project = rd_cfg_child_from_string(rd_state->root_cfg, str8_lit("project"));
            RD_Cfg *target = rd_cfg_new(project, str8_lit("target"));
            RD_Cfg *exe = rd_cfg_new(target, str8_lit("executable"));
            rd_cfg_new(exe, file_path);
            String8 working_directory = str8_chop_last_slash(file_path);
            if(working_directory.size != 0)
            {
              RD_Cfg *wdir = rd_cfg_new(target, str8_lit("working_directory"));
              rd_cfg_newf(wdir, "%S/", working_directory);
            }
            rd_cmd(RD_CmdKind_SelectCfg, .cfg = rd_handle_from_cfg(target));
          }break;
          
          //- rjf: jit-debugger registration
          case RD_CmdKind_RegisterAsJITDebugger:
          {
#if OS_WINDOWS
            char filename_cstr[MAX_PATH] = {0};
            GetModuleFileName(0, filename_cstr, sizeof(filename_cstr));
            String8 debugger_binary_path = str8_cstring(filename_cstr);
            String8 name8 = str8_lit("Debugger");
            String8 data8 = push_str8f(scratch.arena, "%S --jit_pid:%%ld --jit_code:%%ld --jit_addr:0x%%p", debugger_binary_path);
            String16 name16 = str16_from_8(scratch.arena, name8);
            String16 data16 = str16_from_8(scratch.arena, data8);
            B32 likely_not_in_admin_mode = 0;
            {
              HKEY reg_key = 0;
              LSTATUS status = 0;
              status = RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\WOW6432Node\\Microsoft\\Windows NT\\CurrentVersion\\AeDebug\\", 0, KEY_SET_VALUE, &reg_key);
              likely_not_in_admin_mode = (status == ERROR_ACCESS_DENIED);
              status = RegSetValueExW(reg_key, (LPCWSTR)name16.str, 0, REG_SZ, (BYTE *)data16.str, data16.size*sizeof(U16)+2);
              RegCloseKey(reg_key);
            }
            {
              HKEY reg_key = 0;
              LSTATUS status = 0;
              status = RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\AeDebug\\", 0, KEY_SET_VALUE, &reg_key);
              likely_not_in_admin_mode = (status == ERROR_ACCESS_DENIED);
              status = RegSetValueExW(reg_key, (LPCWSTR)name16.str, 0, REG_SZ, (BYTE *)data16.str, data16.size*sizeof(U16)+2);
              RegCloseKey(reg_key);
            }
            if(likely_not_in_admin_mode)
            {
              log_user_error(str8_lit("Could not register as the just-in-time debugger, access was denied; try running the debugger as administrator."));
            }
#else
            log_user_error(str8_lit("Registering as the just-in-time debugger is currently not supported on this system."));
#endif
          }break;
          
          //- rjf: developer commands
          case RD_CmdKind_LogMarker:
          {
            log_infof("\"#MARKER\"");
          }break;
          
          //- rjf: os event passthrough
          case RD_CmdKind_OSEvent:
          {
            OS_Event *os_event = rd_regs()->os_event;
            RD_WindowState *ws = rd_window_state_from_os_handle(os_event->window);
            if(os_event != 0 && ws != &rd_nil_window_state)
            {
              UI_Event ui_event = zero_struct;
              UI_EventKind kind = UI_EventKind_Null;
              {
                switch(os_event->kind)
                {
                  default:{}break;
                  case OS_EventKind_Press:     {kind = UI_EventKind_Press;}break;
                  case OS_EventKind_Release:   {kind = UI_EventKind_Release;}break;
                  case OS_EventKind_MouseMove: {kind = UI_EventKind_MouseMove;}break;
                  case OS_EventKind_Text:      {kind = UI_EventKind_Text;}break;
                  case OS_EventKind_Scroll:    {kind = UI_EventKind_Scroll;}break;
                  case OS_EventKind_FileDrop:  {kind = UI_EventKind_FileDrop;}break;
                }
              }
              ui_event.kind         = kind;
              ui_event.key          = os_event->key;
              ui_event.modifiers    = os_event->modifiers;
              ui_event.string       = os_event->character ? str8_from_32(ui_build_arena(), str32(&os_event->character, 1)) : str8_zero();
              ui_event.paths        = str8_list_copy(ui_build_arena(), &os_event->strings);
              ui_event.pos          = os_event->pos;
              ui_event.delta_2f32   = os_event->delta;
              ui_event.timestamp_us = os_event->timestamp_us;
              ui_event_list_push(scratch.arena, &ws->ui_events, &ui_event);
            }
          }break;
          
          //- rjf: debug control context management operations
          case RD_CmdKind_SelectThread:
          {
            CTRL_Entity *thread = ctrl_entity_from_handle(d_state->ctrl_entity_store, rd_regs()->thread);
            CTRL_Entity *process = ctrl_entity_ancestor_from_kind(thread, CTRL_EntityKind_Process);
            CTRL_Entity *module = ctrl_module_from_process_vaddr(process, ctrl_query_cached_rip_from_thread(d_state->ctrl_entity_store, thread->handle));
            CTRL_Entity *machine = ctrl_entity_ancestor_from_kind(process, CTRL_EntityKind_Machine);
            rd_state->base_regs.v.unwind_count = 0;
            rd_state->base_regs.v.inline_depth = 0;
            rd_state->base_regs.v.thread  = thread->handle;
            rd_state->base_regs.v.module  = module->handle;
            rd_state->base_regs.v.process = process->handle;
            rd_state->base_regs.v.machine = machine->handle;
            rd_cmd(RD_CmdKind_FindThread, .thread = thread->handle, .unwind_count = 0, .inline_depth = 0);
          }break;
          case RD_CmdKind_SelectUnwind:
          {
            DI_Scope *di_scope = di_scope_open();
            CTRL_Entity *thread = ctrl_entity_from_handle(d_state->ctrl_entity_store, rd_base_regs()->thread);
            CTRL_Entity *process = ctrl_entity_ancestor_from_kind(thread, CTRL_EntityKind_Process);
            CTRL_Unwind base_unwind = d_query_cached_unwind_from_thread(thread);
            CTRL_CallStack rich_unwind = ctrl_call_stack_from_unwind(scratch.arena, di_scope, process, &base_unwind);
            if(rd_regs()->unwind_count < rich_unwind.concrete_frame_count)
            {
              CTRL_CallStackFrame *frame = &rich_unwind.frames[rd_regs()->unwind_count];
              U64 rip_vaddr = regs_rip_from_arch_block(thread->arch, frame->regs);
              CTRL_Entity *module = ctrl_module_from_process_vaddr(process, rip_vaddr);
              rd_state->base_regs.v.module = module->handle;
              rd_state->base_regs.v.unwind_count = rd_regs()->unwind_count;
              rd_state->base_regs.v.inline_depth = 0;
              if(rd_regs()->inline_depth <= frame->inline_frame_count)
              {
                rd_state->base_regs.v.inline_depth = rd_regs()->inline_depth;
              }
            }
            rd_cmd(RD_CmdKind_FindThread, .thread = thread->handle, .unwind_count = rd_state->base_regs.v.unwind_count, .inline_depth = rd_state->base_regs.v.inline_depth);
            di_scope_close(di_scope);
          }break;
          case RD_CmdKind_UpOneFrame:
          case RD_CmdKind_DownOneFrame:
          {
            DI_Scope *di_scope = di_scope_open();
            CTRL_Entity *thread = ctrl_entity_from_handle(d_state->ctrl_entity_store, rd_base_regs()->thread);
            CTRL_Entity *process = ctrl_entity_ancestor_from_kind(thread, CTRL_EntityKind_Process);
            CTRL_Unwind base_unwind = d_query_cached_unwind_from_thread(thread);
            CTRL_CallStack rich_unwind = ctrl_call_stack_from_unwind(scratch.arena, di_scope, process, &base_unwind);
            U64 crnt_unwind_idx = rd_state->base_regs.v.unwind_count;
            U64 crnt_inline_dpt = rd_state->base_regs.v.inline_depth;
            U64 next_unwind_idx = crnt_unwind_idx;
            U64 next_inline_dpt = crnt_inline_dpt;
            if(crnt_unwind_idx < rich_unwind.concrete_frame_count)
            {
              CTRL_CallStackFrame *f = &rich_unwind.frames[crnt_unwind_idx];
              switch(kind)
              {
                default:{}break;
                case RD_CmdKind_UpOneFrame:
                {
                  if(crnt_inline_dpt < f->inline_frame_count)
                  {
                    next_inline_dpt += 1;
                  }
                  else if(crnt_unwind_idx > 0)
                  {
                    next_unwind_idx -= 1;
                    next_inline_dpt = 0;
                  }
                }break;
                case RD_CmdKind_DownOneFrame:
                {
                  if(crnt_inline_dpt > 0)
                  {
                    next_inline_dpt -= 1;
                  }
                  else if(crnt_unwind_idx < rich_unwind.concrete_frame_count)
                  {
                    next_unwind_idx += 1;
                    next_inline_dpt = (f+1)->inline_frame_count;
                  }
                }break;
              }
            }
            rd_cmd(RD_CmdKind_SelectUnwind,
                   .unwind_count = next_unwind_idx,
                   .inline_depth = next_inline_dpt);
            di_scope_close(di_scope);
          }break;
          
          //- rjf: meta controls
          case RD_CmdKind_Edit:
          {
            RD_Cfg *window = rd_cfg_from_handle(rd_regs()->window);
            RD_WindowState *ws = rd_window_state_from_cfg(window);
            UI_Event evt = zero_struct;
            evt.kind       = UI_EventKind_Press;
            evt.slot       = UI_EventActionSlot_Edit;
            ui_event_list_push(scratch.arena, &ws->ui_events, &evt);
          }break;
          case RD_CmdKind_Accept:
          {
            RD_Cfg *window = rd_cfg_from_handle(rd_regs()->window);
            RD_WindowState *ws = rd_window_state_from_cfg(window);
            UI_Event evt = zero_struct;
            evt.kind       = UI_EventKind_Press;
            evt.slot       = UI_EventActionSlot_Accept;
            ui_event_list_push(scratch.arena, &ws->ui_events, &evt);
          }break;
          case RD_CmdKind_Cancel:
          {
            RD_Cfg *window = rd_cfg_from_handle(rd_regs()->window);
            RD_WindowState *ws = rd_window_state_from_cfg(window);
            UI_Event evt = zero_struct;
            evt.kind       = UI_EventKind_Press;
            evt.slot       = UI_EventActionSlot_Cancel;
            ui_event_list_push(scratch.arena, &ws->ui_events, &evt);
          }break;
          
          //- rjf: directional movement & text controls
          //
          // NOTE(rjf): These all get funneled into a separate intermediate that
          // can be used by the UI build phase for navigation and stuff, as well
          // as builder codepaths that want to use these controls to modify text.
          //
          case RD_CmdKind_MoveLeft:
          {
            RD_Cfg *window = rd_cfg_from_handle(rd_regs()->window);
            RD_WindowState *ws = rd_window_state_from_cfg(window);
            UI_Event evt = zero_struct;
            evt.kind       = UI_EventKind_Navigate;
            evt.flags      = UI_EventFlag_PickSelectSide|UI_EventFlag_ZeroDeltaOnSelect|UI_EventFlag_ExplicitDirectional;
            evt.delta_unit = UI_EventDeltaUnit_Char;
            evt.delta_2s32 = v2s32(-1, +0);
            ui_event_list_push(scratch.arena, &ws->ui_events, &evt);
          }break;
          case RD_CmdKind_MoveRight:
          {
            RD_Cfg *window = rd_cfg_from_handle(rd_regs()->window);
            RD_WindowState *ws = rd_window_state_from_cfg(window);
            UI_Event evt = zero_struct;
            evt.kind       = UI_EventKind_Navigate;
            evt.flags      = UI_EventFlag_PickSelectSide|UI_EventFlag_ZeroDeltaOnSelect|UI_EventFlag_ExplicitDirectional;
            evt.delta_unit = UI_EventDeltaUnit_Char;
            evt.delta_2s32 = v2s32(+1, +0);
            ui_event_list_push(scratch.arena, &ws->ui_events, &evt);
          }break;
          case RD_CmdKind_MoveUp:
          {
            RD_Cfg *window = rd_cfg_from_handle(rd_regs()->window);
            RD_WindowState *ws = rd_window_state_from_cfg(window);
            UI_Event evt = zero_struct;
            evt.kind       = UI_EventKind_Navigate;
            evt.flags      = UI_EventFlag_ExplicitDirectional;
            evt.delta_unit = UI_EventDeltaUnit_Char;
            evt.delta_2s32 = v2s32(+0, -1);
            ui_event_list_push(scratch.arena, &ws->ui_events, &evt);
          }break;
          case RD_CmdKind_MoveDown:
          {
            RD_Cfg *window = rd_cfg_from_handle(rd_regs()->window);
            RD_WindowState *ws = rd_window_state_from_cfg(window);
            UI_Event evt = zero_struct;
            evt.kind       = UI_EventKind_Navigate;
            evt.flags      = UI_EventFlag_ExplicitDirectional;
            evt.delta_unit = UI_EventDeltaUnit_Char;
            evt.delta_2s32 = v2s32(+0, +1);
            ui_event_list_push(scratch.arena, &ws->ui_events, &evt);
          }break;
          case RD_CmdKind_MoveLeftSelect:
          {
            RD_Cfg *window = rd_cfg_from_handle(rd_regs()->window);
            RD_WindowState *ws = rd_window_state_from_cfg(window);
            UI_Event evt = zero_struct;
            evt.kind       = UI_EventKind_Navigate;
            evt.flags      = UI_EventFlag_KeepMark|UI_EventFlag_ExplicitDirectional;
            evt.delta_unit = UI_EventDeltaUnit_Char;
            evt.delta_2s32 = v2s32(-1, +0);
            ui_event_list_push(scratch.arena, &ws->ui_events, &evt);
          }break;
          case RD_CmdKind_MoveRightSelect:
          {
            RD_Cfg *window = rd_cfg_from_handle(rd_regs()->window);
            RD_WindowState *ws = rd_window_state_from_cfg(window);
            UI_Event evt = zero_struct;
            evt.kind       = UI_EventKind_Navigate;
            evt.flags      = UI_EventFlag_KeepMark|UI_EventFlag_ExplicitDirectional;
            evt.delta_unit = UI_EventDeltaUnit_Char;
            evt.delta_2s32 = v2s32(+1, +0);
            ui_event_list_push(scratch.arena, &ws->ui_events, &evt);
          }break;
          case RD_CmdKind_MoveUpSelect:
          {
            RD_Cfg *window = rd_cfg_from_handle(rd_regs()->window);
            RD_WindowState *ws = rd_window_state_from_cfg(window);
            UI_Event evt = zero_struct;
            evt.kind       = UI_EventKind_Navigate;
            evt.flags      = UI_EventFlag_KeepMark|UI_EventFlag_ExplicitDirectional;
            evt.delta_unit = UI_EventDeltaUnit_Char;
            evt.delta_2s32 = v2s32(+0, -1);
            ui_event_list_push(scratch.arena, &ws->ui_events, &evt);
          }break;
          case RD_CmdKind_MoveDownSelect:
          {
            RD_Cfg *window = rd_cfg_from_handle(rd_regs()->window);
            RD_WindowState *ws = rd_window_state_from_cfg(window);
            UI_Event evt = zero_struct;
            evt.kind       = UI_EventKind_Navigate;
            evt.flags      = UI_EventFlag_KeepMark|UI_EventFlag_ExplicitDirectional;
            evt.delta_unit = UI_EventDeltaUnit_Char;
            evt.delta_2s32 = v2s32(+0, +1);
            ui_event_list_push(scratch.arena, &ws->ui_events, &evt);
          }break;
          case RD_CmdKind_MoveLeftChunk:
          {
            RD_Cfg *window = rd_cfg_from_handle(rd_regs()->window);
            RD_WindowState *ws = rd_window_state_from_cfg(window);
            UI_Event evt = zero_struct;
            evt.kind       = UI_EventKind_Navigate;
            evt.flags      = UI_EventFlag_ExplicitDirectional;
            evt.delta_unit = UI_EventDeltaUnit_Word;
            evt.delta_2s32 = v2s32(-1, +0);
            ui_event_list_push(scratch.arena, &ws->ui_events, &evt);
          }break;
          case RD_CmdKind_MoveRightChunk:
          {
            RD_Cfg *window = rd_cfg_from_handle(rd_regs()->window);
            RD_WindowState *ws = rd_window_state_from_cfg(window);
            UI_Event evt = zero_struct;
            evt.kind       = UI_EventKind_Navigate;
            evt.flags      = UI_EventFlag_ExplicitDirectional;
            evt.delta_unit = UI_EventDeltaUnit_Word;
            evt.delta_2s32 = v2s32(+1, +0);
            ui_event_list_push(scratch.arena, &ws->ui_events, &evt);
          }break;
          case RD_CmdKind_MoveUpChunk:
          {
            RD_Cfg *window = rd_cfg_from_handle(rd_regs()->window);
            RD_WindowState *ws = rd_window_state_from_cfg(window);
            UI_Event evt = zero_struct;
            evt.kind       = UI_EventKind_Navigate;
            evt.flags      = UI_EventFlag_ExplicitDirectional;
            evt.delta_unit = UI_EventDeltaUnit_Word;
            evt.delta_2s32 = v2s32(+0, -1);
            ui_event_list_push(scratch.arena, &ws->ui_events, &evt);
          }break;
          case RD_CmdKind_MoveDownChunk:
          {
            RD_Cfg *window = rd_cfg_from_handle(rd_regs()->window);
            RD_WindowState *ws = rd_window_state_from_cfg(window);
            UI_Event evt = zero_struct;
            evt.kind       = UI_EventKind_Navigate;
            evt.flags      = UI_EventFlag_ExplicitDirectional;
            evt.delta_unit = UI_EventDeltaUnit_Word;
            evt.delta_2s32 = v2s32(+0, +1);
            ui_event_list_push(scratch.arena, &ws->ui_events, &evt);
          }break;
          case RD_CmdKind_MoveUpPage:
          {
            RD_Cfg *window = rd_cfg_from_handle(rd_regs()->window);
            RD_WindowState *ws = rd_window_state_from_cfg(window);
            UI_Event evt = zero_struct;
            evt.kind       = UI_EventKind_Navigate;
            evt.delta_unit = UI_EventDeltaUnit_Page;
            evt.delta_2s32 = v2s32(+0, -1);
            ui_event_list_push(scratch.arena, &ws->ui_events, &evt);
          }break;
          case RD_CmdKind_MoveDownPage:
          {
            RD_Cfg *window = rd_cfg_from_handle(rd_regs()->window);
            RD_WindowState *ws = rd_window_state_from_cfg(window);
            UI_Event evt = zero_struct;
            evt.kind       = UI_EventKind_Navigate;
            evt.delta_unit = UI_EventDeltaUnit_Page;
            evt.delta_2s32 = v2s32(+0, +1);
            ui_event_list_push(scratch.arena, &ws->ui_events, &evt);
          }break;
          case RD_CmdKind_MoveUpWhole:
          {
            RD_Cfg *window = rd_cfg_from_handle(rd_regs()->window);
            RD_WindowState *ws = rd_window_state_from_cfg(window);
            UI_Event evt = zero_struct;
            evt.kind       = UI_EventKind_Navigate;
            evt.delta_unit = UI_EventDeltaUnit_Whole;
            evt.delta_2s32 = v2s32(+0, -1);
            ui_event_list_push(scratch.arena, &ws->ui_events, &evt);
          }break;
          case RD_CmdKind_MoveDownWhole:
          {
            RD_Cfg *window = rd_cfg_from_handle(rd_regs()->window);
            RD_WindowState *ws = rd_window_state_from_cfg(window);
            UI_Event evt = zero_struct;
            evt.kind       = UI_EventKind_Navigate;
            evt.delta_unit = UI_EventDeltaUnit_Whole;
            evt.delta_2s32 = v2s32(+0, +1);
            ui_event_list_push(scratch.arena, &ws->ui_events, &evt);
          }break;
          case RD_CmdKind_MoveLeftChunkSelect:
          {
            RD_Cfg *window = rd_cfg_from_handle(rd_regs()->window);
            RD_WindowState *ws = rd_window_state_from_cfg(window);
            UI_Event evt = zero_struct;
            evt.kind       = UI_EventKind_Navigate;
            evt.flags      = UI_EventFlag_KeepMark|UI_EventFlag_ExplicitDirectional;
            evt.delta_unit = UI_EventDeltaUnit_Word;
            evt.delta_2s32 = v2s32(-1, +0);
            ui_event_list_push(scratch.arena, &ws->ui_events, &evt);
          }break;
          case RD_CmdKind_MoveRightChunkSelect:
          {
            RD_Cfg *window = rd_cfg_from_handle(rd_regs()->window);
            RD_WindowState *ws = rd_window_state_from_cfg(window);
            UI_Event evt = zero_struct;
            evt.kind       = UI_EventKind_Navigate;
            evt.flags      = UI_EventFlag_KeepMark|UI_EventFlag_ExplicitDirectional;
            evt.delta_unit = UI_EventDeltaUnit_Word;
            evt.delta_2s32 = v2s32(+1, +0);
            ui_event_list_push(scratch.arena, &ws->ui_events, &evt);
          }break;
          case RD_CmdKind_MoveUpChunkSelect:
          {
            RD_Cfg *window = rd_cfg_from_handle(rd_regs()->window);
            RD_WindowState *ws = rd_window_state_from_cfg(window);
            UI_Event evt = zero_struct;
            evt.kind       = UI_EventKind_Navigate;
            evt.flags      = UI_EventFlag_KeepMark|UI_EventFlag_ExplicitDirectional;
            evt.delta_unit = UI_EventDeltaUnit_Word;
            evt.delta_2s32 = v2s32(+0, -1);
            ui_event_list_push(scratch.arena, &ws->ui_events, &evt);
          }break;
          case RD_CmdKind_MoveDownChunkSelect:
          {
            RD_Cfg *window = rd_cfg_from_handle(rd_regs()->window);
            RD_WindowState *ws = rd_window_state_from_cfg(window);
            UI_Event evt = zero_struct;
            evt.kind       = UI_EventKind_Navigate;
            evt.flags      = UI_EventFlag_KeepMark|UI_EventFlag_ExplicitDirectional;
            evt.delta_unit = UI_EventDeltaUnit_Word;
            evt.delta_2s32 = v2s32(+0, +1);
            ui_event_list_push(scratch.arena, &ws->ui_events, &evt);
          }break;
          case RD_CmdKind_MoveUpPageSelect:
          {
            RD_Cfg *window = rd_cfg_from_handle(rd_regs()->window);
            RD_WindowState *ws = rd_window_state_from_cfg(window);
            UI_Event evt = zero_struct;
            evt.kind       = UI_EventKind_Navigate;
            evt.flags      = UI_EventFlag_KeepMark;
            evt.delta_unit = UI_EventDeltaUnit_Page;
            evt.delta_2s32 = v2s32(+0, -1);
            ui_event_list_push(scratch.arena, &ws->ui_events, &evt);
          }break;
          case RD_CmdKind_MoveDownPageSelect:
          {
            RD_Cfg *window = rd_cfg_from_handle(rd_regs()->window);
            RD_WindowState *ws = rd_window_state_from_cfg(window);
            UI_Event evt = zero_struct;
            evt.kind       = UI_EventKind_Navigate;
            evt.flags      = UI_EventFlag_KeepMark;
            evt.delta_unit = UI_EventDeltaUnit_Page;
            evt.delta_2s32 = v2s32(+0, +1);
            ui_event_list_push(scratch.arena, &ws->ui_events, &evt);
          }break;
          case RD_CmdKind_MoveUpWholeSelect:
          {
            RD_Cfg *window = rd_cfg_from_handle(rd_regs()->window);
            RD_WindowState *ws = rd_window_state_from_cfg(window);
            UI_Event evt = zero_struct;
            evt.kind       = UI_EventKind_Navigate;
            evt.flags      = UI_EventFlag_KeepMark;
            evt.delta_unit = UI_EventDeltaUnit_Whole;
            evt.delta_2s32 = v2s32(+0, -1);
            ui_event_list_push(scratch.arena, &ws->ui_events, &evt);
          }break;
          case RD_CmdKind_MoveDownWholeSelect:
          {
            RD_Cfg *window = rd_cfg_from_handle(rd_regs()->window);
            RD_WindowState *ws = rd_window_state_from_cfg(window);
            UI_Event evt = zero_struct;
            evt.kind       = UI_EventKind_Navigate;
            evt.flags      = UI_EventFlag_KeepMark;
            evt.delta_unit = UI_EventDeltaUnit_Whole;
            evt.delta_2s32 = v2s32(+0, +1);
            ui_event_list_push(scratch.arena, &ws->ui_events, &evt);
          }break;
          case RD_CmdKind_MoveUpReorder:
          {
            RD_Cfg *window = rd_cfg_from_handle(rd_regs()->window);
            RD_WindowState *ws = rd_window_state_from_cfg(window);
            UI_Event evt = zero_struct;
            evt.kind       = UI_EventKind_Navigate;
            evt.flags      = UI_EventFlag_Reorder;
            evt.delta_unit = UI_EventDeltaUnit_Char;
            evt.delta_2s32 = v2s32(+0, -1);
            ui_event_list_push(scratch.arena, &ws->ui_events, &evt);
          }break;
          case RD_CmdKind_MoveDownReorder:
          {
            RD_Cfg *window = rd_cfg_from_handle(rd_regs()->window);
            RD_WindowState *ws = rd_window_state_from_cfg(window);
            UI_Event evt = zero_struct;
            evt.kind       = UI_EventKind_Navigate;
            evt.flags      = UI_EventFlag_Reorder;
            evt.delta_unit = UI_EventDeltaUnit_Char;
            evt.delta_2s32 = v2s32(+0, +1);
            ui_event_list_push(scratch.arena, &ws->ui_events, &evt);
          }break;
          case RD_CmdKind_MoveHome:
          {
            RD_Cfg *window = rd_cfg_from_handle(rd_regs()->window);
            RD_WindowState *ws = rd_window_state_from_cfg(window);
            UI_Event evt = zero_struct;
            evt.kind       = UI_EventKind_Navigate;
            evt.delta_unit = UI_EventDeltaUnit_Line;
            evt.delta_2s32 = v2s32(-1, +0);
            ui_event_list_push(scratch.arena, &ws->ui_events, &evt);
          }break;
          case RD_CmdKind_MoveEnd:
          {
            RD_Cfg *window = rd_cfg_from_handle(rd_regs()->window);
            RD_WindowState *ws = rd_window_state_from_cfg(window);
            UI_Event evt = zero_struct;
            evt.kind       = UI_EventKind_Navigate;
            evt.delta_unit = UI_EventDeltaUnit_Line;
            evt.delta_2s32 = v2s32(+1, +0);
            ui_event_list_push(scratch.arena, &ws->ui_events, &evt);
          }break;
          case RD_CmdKind_MoveHomeSelect:
          {
            RD_Cfg *window = rd_cfg_from_handle(rd_regs()->window);
            RD_WindowState *ws = rd_window_state_from_cfg(window);
            UI_Event evt = zero_struct;
            evt.kind       = UI_EventKind_Navigate;
            evt.flags      = UI_EventFlag_KeepMark;
            evt.delta_unit = UI_EventDeltaUnit_Line;
            evt.delta_2s32 = v2s32(-1, +0);
            ui_event_list_push(scratch.arena, &ws->ui_events, &evt);
          }break;
          case RD_CmdKind_MoveEndSelect:
          {
            RD_Cfg *window = rd_cfg_from_handle(rd_regs()->window);
            RD_WindowState *ws = rd_window_state_from_cfg(window);
            UI_Event evt = zero_struct;
            evt.kind       = UI_EventKind_Navigate;
            evt.flags      = UI_EventFlag_KeepMark;
            evt.delta_unit = UI_EventDeltaUnit_Line;
            evt.delta_2s32 = v2s32(+1, +0);
            ui_event_list_push(scratch.arena, &ws->ui_events, &evt);
          }break;
          case RD_CmdKind_SelectAll:
          {
            RD_Cfg *window = rd_cfg_from_handle(rd_regs()->window);
            RD_WindowState *ws = rd_window_state_from_cfg(window);
            UI_Event evt1 = zero_struct;
            evt1.kind       = UI_EventKind_Navigate;
            evt1.delta_unit = UI_EventDeltaUnit_Whole;
            evt1.delta_2s32 = v2s32(-1, +0);
            ui_event_list_push(scratch.arena, &ws->ui_events, &evt1);
            UI_Event evt2 = zero_struct;
            evt2.kind       = UI_EventKind_Navigate;
            evt2.flags      = UI_EventFlag_KeepMark;
            evt2.delta_unit = UI_EventDeltaUnit_Whole;
            evt2.delta_2s32 = v2s32(+1, +0);
            ui_event_list_push(scratch.arena, &ws->ui_events, &evt2);
          }break;
          case RD_CmdKind_DeleteSingle:
          {
            RD_Cfg *window = rd_cfg_from_handle(rd_regs()->window);
            RD_WindowState *ws = rd_window_state_from_cfg(window);
            UI_Event evt = zero_struct;
            evt.kind       = UI_EventKind_Edit;
            evt.flags      = UI_EventFlag_Delete;
            evt.delta_unit = UI_EventDeltaUnit_Char;
            evt.delta_2s32 = v2s32(+1, +0);
            ui_event_list_push(scratch.arena, &ws->ui_events, &evt);
          }break;
          case RD_CmdKind_DeleteChunk:
          {
            RD_Cfg *window = rd_cfg_from_handle(rd_regs()->window);
            RD_WindowState *ws = rd_window_state_from_cfg(window);
            UI_Event evt = zero_struct;
            evt.kind       = UI_EventKind_Edit;
            evt.flags      = UI_EventFlag_Delete;
            evt.delta_unit = UI_EventDeltaUnit_Word;
            evt.delta_2s32 = v2s32(+1, +0);
            ui_event_list_push(scratch.arena, &ws->ui_events, &evt);
          }break;
          case RD_CmdKind_BackspaceSingle:
          {
            RD_Cfg *window = rd_cfg_from_handle(rd_regs()->window);
            RD_WindowState *ws = rd_window_state_from_cfg(window);
            UI_Event evt = zero_struct;
            evt.kind       = UI_EventKind_Edit;
            evt.flags      = UI_EventFlag_Delete|UI_EventFlag_ZeroDeltaOnSelect;
            evt.delta_unit = UI_EventDeltaUnit_Char;
            evt.delta_2s32 = v2s32(-1, +0);
            ui_event_list_push(scratch.arena, &ws->ui_events, &evt);
          }break;
          case RD_CmdKind_BackspaceChunk:
          {
            RD_Cfg *window = rd_cfg_from_handle(rd_regs()->window);
            RD_WindowState *ws = rd_window_state_from_cfg(window);
            UI_Event evt = zero_struct;
            evt.kind       = UI_EventKind_Edit;
            evt.flags      = UI_EventFlag_Delete;
            evt.delta_unit = UI_EventDeltaUnit_Word;
            evt.delta_2s32 = v2s32(-1, +0);
            ui_event_list_push(scratch.arena, &ws->ui_events, &evt);
          }break;
          case RD_CmdKind_Copy:
          {
            RD_Cfg *window = rd_cfg_from_handle(rd_regs()->window);
            RD_WindowState *ws = rd_window_state_from_cfg(window);
            UI_Event evt = zero_struct;
            evt.kind  = UI_EventKind_Edit;
            evt.flags = UI_EventFlag_Copy|UI_EventFlag_KeepMark;
            ui_event_list_push(scratch.arena, &ws->ui_events, &evt);
          }break;
          case RD_CmdKind_Cut:
          {
            RD_Cfg *window = rd_cfg_from_handle(rd_regs()->window);
            RD_WindowState *ws = rd_window_state_from_cfg(window);
            UI_Event evt = zero_struct;
            evt.kind  = UI_EventKind_Edit;
            evt.flags = UI_EventFlag_Copy|UI_EventFlag_Delete;
            ui_event_list_push(scratch.arena, &ws->ui_events, &evt);
          }break;
          case RD_CmdKind_Paste:
          {
            RD_Cfg *window = rd_cfg_from_handle(rd_regs()->window);
            RD_WindowState *ws = rd_window_state_from_cfg(window);
            UI_Event evt = zero_struct;
            evt.kind   = UI_EventKind_Text;
            evt.string = os_get_clipboard_text(scratch.arena);
            ui_event_list_push(scratch.arena, &ws->ui_events, &evt);
          }break;
          case RD_CmdKind_InsertText:
          {
            RD_Cfg *window = rd_cfg_from_handle(rd_regs()->window);
            RD_WindowState *ws = rd_window_state_from_cfg(window);
            UI_Event evt = zero_struct;
            evt.kind   = UI_EventKind_Text;
            evt.string = rd_regs()->string;
            ui_event_list_push(scratch.arena, &ws->ui_events, &evt);
          }break;
        }
      }
    }
    
    ////////////////////////////
    //- rjf: gather targets
    //
    D_TargetArray targets = {0};
    ProfScope("gather targets")
    {
      RD_CfgList target_cfgs = rd_cfg_top_level_list_from_string(scratch.arena, str8_lit("target"));
      targets.count = target_cfgs.count;
      targets.v = push_array(scratch.arena, D_Target, targets.count);
      U64 idx = 0;
      for(RD_CfgNode *n = target_cfgs.first; n != 0; n = n->next)
      {
        RD_Cfg *src = n->v;
        B32 src_is_disabled = rd_disabled_from_cfg(src);
        if(src_is_disabled)
        {
          targets.count -= 1;
          continue;
        }
        targets.v[idx] = rd_target_from_cfg(scratch.arena, src);
        idx += 1;
      }
    }
    
    ////////////////////////////
    //- rjf: gather breakpoints & meta-evals (for the engine, meta-evals can only be referenced by breakpoints)
    //
    D_BreakpointArray breakpoints = {0};
    CTRL_MetaEvalArray meta_evals = {0};
    ProfScope("gather breakpoints & meta-evals")
    {
      typedef struct MetaEvalNode MetaEvalNode;
      struct MetaEvalNode
      {
        MetaEvalNode *next;
        CTRL_MetaEval *meval;
      };
      U64 meval_count = 0;
      MetaEvalNode *first_meval = 0;
      MetaEvalNode *last_meval = 0;
      RD_CfgList bp_cfgs = rd_cfg_top_level_list_from_string(scratch.arena, str8_lit("breakpoint"));
      breakpoints.count = bp_cfgs.count;
      breakpoints.v = push_array(scratch.arena, D_Breakpoint, breakpoints.count);
      U64 idx = 0;
      for(RD_CfgNode *n = bp_cfgs.first; n != 0; n = n->next)
      {
        RD_Cfg *src_bp = n->v;
        B32 src_bp_is_disabled = rd_disabled_from_cfg(src_bp);
        if(src_bp_is_disabled)
        {
          breakpoints.count -= 1;
          continue;
        }
        RD_Location src_bp_loc = rd_location_from_cfg(src_bp);
        String8 src_bp_cnd = rd_cfg_child_from_string(src_bp, str8_lit("condition"))->first->string;
        
        //- rjf: walk conditional breakpoint expression tree - for each leaf identifier,
        // determine if it resolves to a meta-evaluation. if it does, compute the meta
        // evaluation data & store.
        //
        // for many conditions, we can statically-disqualify the breakpoint, if it only
        // references frontend-controlled meta-evaluation state. in such cases, we just
        // want to never send the user breakpoint to the control thread, since it cannot
        // be hit anyways. so in this pass, we can also gather information about whether
        // or not it is 'static', w.r.t. the control thread.
        //
        B32 is_static_for_ctrl_thread = 0;
        if(src_bp_cnd.size != 0)
        {
          typedef struct ExprWalkTask ExprWalkTask;
          struct ExprWalkTask
          {
            ExprWalkTask *next;
            E_Expr *expr;
          };
          E_Expr *expr = e_parse_expr_from_text(scratch.arena, src_bp_cnd);
          ExprWalkTask start_task = {0, expr};
          ExprWalkTask *first_task = &start_task;
          for(ExprWalkTask *t = first_task; t != 0; t = t->next)
          {
            if(t->expr->kind == E_ExprKind_LeafIdent)
            {
              E_Expr *macro_expr = e_string2expr_lookup(e_ir_state->ctx->macro_map, t->expr->string);
              if(macro_expr != &e_expr_nil)
              {
                E_Eval eval = e_eval_from_expr(scratch.arena, macro_expr);
                switch(eval.space.kind)
                {
                  default:{is_static_for_ctrl_thread = 0;}break;
                  case E_SpaceKind_Null:
                  case RD_EvalSpaceKind_MetaCfg:
                  {
                    is_static_for_ctrl_thread = 1;
                  }break;
                }
              }
            }
            for(E_Expr *child = t->expr->first; child != &e_expr_nil; child = child->next)
            {
              ExprWalkTask *task = push_array(scratch.arena, ExprWalkTask, 1);
              task->expr = child;
              task->next = t->next;
              t->next = task;
            }
          }
        }
        
        //- rjf: if this breakpoint is conditioned & static for the control thread, then
        // we can evaluate this condition early, and decide whether or not to send this
        // breakpoint.
        B32 is_statically_disqualified = 0;
        if(is_static_for_ctrl_thread)
        {
          E_Eval eval = e_eval_from_string(scratch.arena, src_bp_cnd);
          E_Eval value_eval = e_value_eval_from_eval(eval);
          if(value_eval.value.u64 == 0)
          {
            is_statically_disqualified = 1;
          }
        }
        
        //- rjf: statically disqualified? -> skip
        if(is_statically_disqualified)
        {
          breakpoints.count -= 1;
          continue;
        }
        
        //- rjf: fill breakpoint
        D_Breakpoint *dst_bp = &breakpoints.v[idx];
        dst_bp->file_path   = src_bp_loc.file_path;
        dst_bp->pt          = src_bp_loc.pt;
        dst_bp->symbol_name = src_bp_loc.name;
        dst_bp->vaddr       = src_bp_loc.vaddr;
        dst_bp->condition   = src_bp_cnd;
        idx += 1;
      }
      
      //- rjf: meta-eval list -> array
      meta_evals.count = meval_count;
      meta_evals.v = push_array(scratch.arena, CTRL_MetaEval, meta_evals.count);
      {
        U64 idx = 0;
        for(MetaEvalNode *n = first_meval; n != 0; n = n->next)
        {
          MemoryCopyStruct(&meta_evals.v[idx], n->meval);
          idx += 1;
        }
      }
    }
    
    ////////////////////////////
    //- rjf: gather path maps
    //
    D_PathMapArray path_maps = {0};
    {
      RD_CfgList maps = rd_cfg_top_level_list_from_string(scratch.arena, str8_lit("file_path_map"));
      path_maps.count = maps.count;
      path_maps.v = push_array(scratch.arena, D_PathMap, path_maps.count);
      U64 idx = 0;
      for(RD_CfgNode *n = maps.first; n != 0; n = n->next, idx += 1)
      {
        RD_Cfg *map = n->v;
        path_maps.v[idx].src = rd_cfg_child_from_string(map, str8_lit("source"))->first->string;
        path_maps.v[idx].dst = rd_cfg_child_from_string(map, str8_lit("dest"))->first->string;
      }
    }
    
    ////////////////////////////
    //- rjf: gather exception code filters
    //
    U64 exception_code_filters[(CTRL_ExceptionCodeKind_COUNT+63)/64] = {0};
    {
      Temp scratch = scratch_begin(0, 0);
      for(CTRL_ExceptionCodeKind k = (CTRL_ExceptionCodeKind)0; k < CTRL_ExceptionCodeKind_COUNT; k = (CTRL_ExceptionCodeKind)(k+1))
      {
        if(ctrl_exception_code_kind_default_enable_table[k])
        {
          exception_code_filters[k/64] |= 1ull<<(k%64);
        }
      }
      RD_CfgList exception_code_filters_roots = rd_cfg_top_level_list_from_string(scratch.arena, str8_lit("exception_code_filters"));
      for(RD_CfgNode *n = exception_code_filters_roots.first; n != 0; n = n->next)
      {
        for(RD_Cfg *rule = n->v->first; rule != &rd_nil_cfg; rule = rule->next)
        {
          String8 name = rule->string;
          String8 val_string = rule->first->string;
          U64 val = 0;
          if(try_u64_from_str8_c_rules(val_string, &val))
          {
            CTRL_ExceptionCodeKind kind = CTRL_ExceptionCodeKind_Null;
            for(CTRL_ExceptionCodeKind k = (CTRL_ExceptionCodeKind)(CTRL_ExceptionCodeKind_Null+1);
                k < CTRL_ExceptionCodeKind_COUNT;
                k = (CTRL_ExceptionCodeKind)(k+1))
            {
              if(str8_match(name, ctrl_exception_code_kind_lowercase_code_string_table[k], 0))
              {
                kind = k;
                break;
              }
            }
            if(kind != CTRL_ExceptionCodeKind_Null)
            {
              if(val)
              {
                exception_code_filters[kind/64] |= (1ull<<(kind%64));
              }
              else
              {
                exception_code_filters[kind/64] &= ~(1ull<<(kind%64));
              }
            }
          }
        }
      }
      scratch_end(scratch);
    }
    
    ////////////////////////////
    //- rjf: tick debug engine
    //
    U64 cmd_count_pre_tick = rd_state->cmds[0].count;
    D_EventList engine_events = d_tick(scratch.arena, &targets, &breakpoints, &path_maps, exception_code_filters, &meta_evals);
    
    ////////////////////////////
    //- rjf: process debug engine events
    //
    for(D_EventNode *n = engine_events.first; n != 0; n = n->next)
    {
      D_Event *evt = &n->v;
      switch(evt->kind)
      {
        default:{}break;
        case D_EventKind_ProcessEnd:
        if(rd_state->quit_after_success)
        {
          CTRL_EntityList processes = ctrl_entity_list_from_kind(d_state->ctrl_entity_store, CTRL_EntityKind_Process);
          if(evt->code == 0 && processes.count == 0)
          {
            rd_cmd(RD_CmdKind_Exit);
          }
          else if(evt->code != 0)
          {
            rd_state->quit_after_success = 0;
          }
        }break;
        case D_EventKind_Stop:
        {
          B32 need_refocus = (evt->cause != D_EventCause_SoftHalt);
          CTRL_Entity *thread = ctrl_entity_from_handle(d_state->ctrl_entity_store, evt->thread);
          U64 vaddr = evt->vaddr;
          CTRL_Entity *process = ctrl_entity_ancestor_from_kind(thread, CTRL_EntityKind_Process);
          CTRL_Entity *module = ctrl_module_from_process_vaddr(process, vaddr);
          DI_Key dbgi_key = ctrl_dbgi_key_from_module(module);
          U64 voff = ctrl_voff_from_vaddr(module, vaddr);
          U64 test_cached_vaddr = ctrl_query_cached_rip_from_thread(d_state->ctrl_entity_store, thread->handle);
          
          // rjf: valid stop thread? -> select & snap
          if(need_refocus && thread != &ctrl_entity_nil && evt->cause != D_EventCause_Halt)
          {
            rd_cmd(RD_CmdKind_SelectThread, .thread = thread->handle);
          }
          
          // rjf: no stop-causing thread, but have selected thread? -> snap to selected
          CTRL_Entity *selected_thread = ctrl_entity_from_handle(d_state->ctrl_entity_store, rd_base_regs()->thread);
          if(need_refocus && (evt->cause == D_EventCause_Halt || thread == &ctrl_entity_nil) && selected_thread != &ctrl_entity_nil)
          {
            rd_cmd(RD_CmdKind_SelectThread, .thread = selected_thread->handle);
          }
          
          // rjf: no stop-causing thread, but don't have selected thread? -> snap to first available thread
          if(need_refocus && thread == &ctrl_entity_nil && selected_thread == &ctrl_entity_nil)
          {
            CTRL_EntityList threads = ctrl_entity_list_from_kind(d_state->ctrl_entity_store, CTRL_EntityKind_Thread);
            CTRL_Entity *first_available_thread = ctrl_entity_list_first(&threads);
            rd_cmd(RD_CmdKind_SelectThread, .thread = first_available_thread->handle);
          }
          
          // rjf: increment breakpoint hit counts
          if(evt->cause == D_EventCause_UserBreakpoint)
          {
            RD_CfgList bps = rd_cfg_top_level_list_from_string(scratch.arena, str8_lit("breakpoint"));
            for(RD_CfgNode *n = bps.first; n != 0; n = n->next)
            {
              RD_Cfg *bp = n->v;
              RD_Cfg *hit_count_root = rd_cfg_child_from_string_or_alloc(bp, str8_lit("hit_count"));
              U64 hit_count = 0;
              try_u64_from_str8_c_rules(hit_count_root->first->string, &hit_count);
              RD_Location loc = rd_location_from_cfg(bp);
              D_LineList loc_lines = d_lines_from_file_path_line_num(scratch.arena, loc.file_path, loc.pt.line);
              if(loc_lines.first != 0)
              {
                for(D_LineNode *n = loc_lines.first; n != 0; n = n->next)
                {
                  if(contains_1u64(n->v.voff_range, voff))
                  {
                    hit_count += 1;
                    break;
                  }
                }
              }
              else if(loc.vaddr != 0 && vaddr == loc.vaddr)
              {
                hit_count += 1;
              }
              else if(loc.name.size != 0)
              {
                U64 symb_voff = d_voff_from_dbgi_key_symbol_name(&dbgi_key, loc.name);
                if(symb_voff == voff)
                {
                  hit_count += 1;
                }
              }
              rd_cfg_release_all_children(hit_count_root);
              rd_cfg_newf(hit_count_root, "%I64u", hit_count);
            }
          }
          
          // rjf: focus window if none focused, and if we have a thread to snap to
          if(need_refocus && (selected_thread != &ctrl_entity_nil || thread != &ctrl_entity_nil))
          {
            B32 any_window_is_focused = 0;
            for(RD_WindowState *ws = rd_state->first_window_state; ws != &rd_nil_window_state; ws = ws->order_next)
            {
              if(os_window_is_focused(ws->os))
              {
                any_window_is_focused = 1;
                break;
              }
            }
            if(!any_window_is_focused)
            {
              RD_Cfg *last_focused_window = rd_cfg_from_handle(rd_state->last_focused_window);
              RD_WindowState *ws = rd_window_state_from_cfg(last_focused_window);
              if(ws == &rd_nil_window_state)
              {
                ws = rd_state->first_window_state;
              }
              if(ws != &rd_nil_window_state)
              {
                os_window_set_minimized(ws->os, 0);
                os_window_bring_to_front(ws->os);
                os_window_focus(ws->os);
              }
            }
          }
        }break;
      }
    }
    
    ////////////////////////////
    //- rjf: early-out if no new commands
    //
    if(rd_state->cmds[0].count == cmd_count_pre_tick)
    {
      break;
    }
  }
  
  //////////////////////////////
  //- rjf: retry find-thread
  //
  if(!ctrl_handle_match(ctrl_handle_zero(), find_thread_retry))
  {
    rd_cmd(RD_CmdKind_FindThread, .thread = find_thread_retry);
  }
  
  //////////////////////////////
  //- rjf: animate confirmation
  //
  {
    F32 rate = rd_setting_b32_from_name(str8_lit("menu_animations")) ? 1 - pow_f32(2, (-10.f * rd_state->frame_dt)) : 1.f;
    B32 popup_open = rd_state->popup_active;
    rd_state->popup_t += rate * ((F32)!!popup_open-rd_state->popup_t);
    if(abs_f32(rd_state->popup_t - (F32)!!popup_open) > 0.005f)
    {
      rd_request_frame();
    }
  }
  
  ////////////////////////////
  //- rjf: rotate command slots, bump command gen counter
  //
  // in this step, we rotate the ring buffer of command batches (command
  // arenas & lists). when the cmds_gen (the position of the ring buffer)
  // is even, the command queue is in a "read/write" mode, and this is uniquely
  // usable by the core - this is done so that commands in the core can push
  // other commands, and have those other commands processed on the same frame.
  //
  // in view code, however, they can only use the current command queue in a
  // "read only" mode, because new commands pushed by those views must be
  // processed first by the core. so, before calling into view code, the
  // cmds_gen is incremented to be *odd*. this way, the views will *write*
  // commands into the 0 slot, but *read* from the 1 slot (which will contain
  // this frame's commands).
  //
  // after view code runs, the generation number is incremented back to even.
  // the commands pushed by the view will be in the queue, and the core can
  // treat that queue as r/w again.
  //
  if(depth == 0)
  {
    // rjf: rotate
    {
      Arena *first_arena = rd_state->cmds_arenas[0];
      RD_CmdList first_cmds = rd_state->cmds[0];
      MemoryCopy(rd_state->cmds_arenas,
                 rd_state->cmds_arenas+1,
                 sizeof(rd_state->cmds_arenas[0])*(ArrayCount(rd_state->cmds_arenas)-1));
      MemoryCopy(rd_state->cmds,
                 rd_state->cmds+1,
                 sizeof(rd_state->cmds[0])*(ArrayCount(rd_state->cmds)-1));
      rd_state->cmds_arenas[ArrayCount(rd_state->cmds_arenas)-1] = first_arena;
      rd_state->cmds[ArrayCount(rd_state->cmds_arenas)-1] = first_cmds;
    }
    
    // rjf: clear next batch
    {
      arena_clear(rd_state->cmds_arenas[0]);
      MemoryZeroStruct(&rd_state->cmds[0]);
    }
    
    // rjf: bump
    {
      rd_state->cmds_gen += 1;
    }
  }
  
  //////////////////////////////
  //- rjf: compute all ambiguous paths from view titles
  //
  ProfScope("compute all ambiguous paths from view titles")
  {
    Temp scratch = scratch_begin(0, 0);
    rd_state->ambiguous_path_slots_count = 512;
    rd_state->ambiguous_path_slots = push_array(rd_frame_arena(), RD_AmbiguousPathNode *, rd_state->ambiguous_path_slots_count);
    for(RD_WindowState *ws = rd_state->first_window_state; ws != &rd_nil_window_state; ws = ws->order_next)
    {
      RD_Cfg *window = rd_cfg_from_handle(ws->cfg_handle);
      RD_PanelTree panel_tree = rd_panel_tree_from_cfg(scratch.arena, window);
      for(RD_PanelNode *p = panel_tree.root; p != &rd_nil_panel_node; p = rd_panel_node_rec__depth_first_pre(panel_tree.root, p).next)
      {
        for(RD_CfgNode *tab_n = p->tabs.first; tab_n != 0; tab_n = tab_n->next)
        {
          RD_Cfg *tab = tab_n->v;
          if(rd_cfg_is_project_filtered(tab))
          {
            continue;
          }
          RD_RegsScope(.view = rd_handle_from_cfg(tab))
          {
            String8 eval_string = rd_view_expr_string();
            String8 file_path = rd_file_path_from_eval_string(scratch.arena, eval_string);
            if(file_path.size != 0)
            {
              String8 name = str8_skip_last_slash(file_path);
              U64 hash = d_hash_from_string__case_insensitive(name);
              U64 slot_idx = hash%rd_state->ambiguous_path_slots_count;
              RD_AmbiguousPathNode *node = 0;
              for(RD_AmbiguousPathNode *n = rd_state->ambiguous_path_slots[slot_idx];
                  n != 0;
                  n = n->next)
              {
                if(str8_match(n->name, name, StringMatchFlag_CaseInsensitive))
                {
                  node = n;
                  break;
                }
              }
              if(node == 0)
              {
                node = push_array(rd_frame_arena(), RD_AmbiguousPathNode, 1);
                SLLStackPush(rd_state->ambiguous_path_slots[slot_idx], node);
                node->name = push_str8_copy(rd_frame_arena(), name);
              }
              str8_list_push(rd_frame_arena(), &node->paths, push_str8_copy(rd_frame_arena(), file_path));
            }
          }
        }
      }
    }
    scratch_end(scratch);
  }
  
  //////////////////////////////
  //- rjf: set name matching parameters; begin matching
  //
  {
    DI_KeyList keys_list = d_push_active_dbgi_key_list(scratch.arena);
    DI_KeyArray keys = di_key_array_from_list(scratch.arena, &keys_list);
    di_match_store_begin(rd_state->match_store, keys);
  }
  
  //////////////////////////////
  //- rjf: update/render all windows
  //
  {
    dr_begin_frame();
    RD_CfgList windows = rd_cfg_top_level_list_from_string(scratch.arena, str8_lit("window"));
    for(RD_CfgNode *n = windows.first; n != 0; n = n->next)
    {
      RD_Cfg *window = n->v;
      RD_WindowState *w = rd_window_state_from_cfg(window);
      B32 window_is_focused = os_window_is_focused(w->os);
      if(window_is_focused)
      {
        rd_state->last_focused_window = w->cfg_handle;
      }
      rd_push_regs();
      rd_regs()->window = w->cfg_handle;
      rd_window_frame();
      MemoryZeroStruct(&w->ui_events);
      RD_Regs *window_regs = rd_pop_regs();
      if(rd_handle_match(rd_state->last_focused_window, w->cfg_handle))
      {
        MemoryCopyStruct(rd_regs(), window_regs);
      }
    }
  }
  
  //////////////////////////////
  //- rjf: simulate lag
  //
  if(DEV_simulate_lag)
  {
    os_sleep_milliseconds(300);
  }
  
  //////////////////////////////
  //- rjf: end drag/drop if needed
  //
  if(rd_state->drag_drop_state == RD_DragDropState_Dropping)
  {
    rd_state->drag_drop_state = RD_DragDropState_Null;
  }
  
  //////////////////////////////
  //- rjf: clear frame request state
  //
  if(rd_state->num_frames_requested > 0)
  {
    rd_state->num_frames_requested -= 1;
  }
  
  //////////////////////////////
  //- rjf: close scopes
  //
  if(depth == 0)
  {
    di_scope_close(rd_state->frame_di_scope);
  }
  
  //////////////////////////////
  //- rjf: submit rendering to all windows
  //
  ProfScope("submit rendering to all windows")
  {
    r_begin_frame();
    for(RD_WindowState *w = rd_state->first_window_state; w != &rd_nil_window_state; w = w->order_next)
    {
      r_window_begin_frame(w->os, w->r);
      dr_submit_bucket(w->os, w->r, w->draw_bucket);
      r_window_end_frame(w->os, w->r);
    }
    r_end_frame();
  }
  
  //////////////////////////////
  //- rjf: show windows after first frame
  //
  if(depth == 0)
  {
    RD_HandleList windows_to_show = {0};
    for(RD_WindowState *w = rd_state->first_window_state; w != &rd_nil_window_state; w = w->order_next)
    {
      if(w->frames_alive == 1)
      {
        rd_handle_list_push(scratch.arena, &windows_to_show, w->cfg_handle);
      }
    }
    for(RD_HandleNode *n = windows_to_show.first; n != 0; n = n->next)
    {
      RD_Cfg *window = rd_cfg_from_handle(n->handle);
      RD_WindowState *ws = rd_window_state_from_cfg(window);
      DeferLoop(depth += 1, depth -= 1) os_window_first_paint(ws->os);
    }
  }
  
  //////////////////////////////
  //- rjf: eliminate entities that are marked for deletion
  //
  ProfScope("eliminate deleted entities")
  {
    for(RD_Entity *entity = rd_entity_root(), *next = 0; !rd_entity_is_nil(entity); entity = next)
    {
      next = rd_entity_rec_depth_first_pre(entity, &rd_nil_entity).next;
      if(entity->flags & RD_EntityFlag_MarkedForDeletion)
      {
        B32 undoable = (rd_entity_kind_flags_table[entity->kind] & RD_EntityKindFlag_UserDefinedLifetime);
        
        // rjf: fixup next entity to iterate to
        next = rd_entity_rec_depth_first(entity, &rd_nil_entity, OffsetOf(RD_Entity, next), OffsetOf(RD_Entity, next)).next;
        
        // rjf: eliminate root entity if we're freeing it
        if(entity == rd_state->entities_root)
        {
          rd_state->entities_root = &rd_nil_entity;
        }
        
        // rjf: unhook & release this entity tree
        rd_entity_change_parent(entity, entity->parent, &rd_nil_entity, &rd_nil_entity);
        rd_entity_release(entity);
      }
    }
  }
  
  //////////////////////////////
  //- rjf: determine frame time, record into history
  //
  U64 end_time_us = os_now_microseconds();
  U64 frame_time_us = end_time_us-begin_time_us;
  rd_state->frame_time_us_history[rd_state->frame_index%ArrayCount(rd_state->frame_time_us_history)] = frame_time_us;
  
  //////////////////////////////
  //- rjf: bump frame time counters
  //
  rd_state->frame_index += 1;
  rd_state->time_in_seconds += rd_state->frame_dt;
  
  //////////////////////////////
  //- rjf: bump command batch ring buffer generation
  //
  if(depth == 0)
  {
    rd_state->cmds_gen += 1;
  }
  
  //////////////////////////////
  //- rjf: collect logs
  //
  ProfScope("collect logs")
  {
    LogScopeResult log = log_scope_end(scratch.arena);
    os_append_data_to_file_path(rd_state->log_path, log.strings[LogMsgKind_Info]);
    if(log.strings[LogMsgKind_UserError].size != 0)
    {
      for(RD_WindowState *ws = rd_state->first_window_state; ws != &rd_nil_window_state; ws = ws->order_next)
      {
        ws->error_string_size = Min(sizeof(ws->error_buffer), log.strings[LogMsgKind_UserError].size);
        MemoryCopy(ws->error_buffer, log.strings[LogMsgKind_UserError].str, ws->error_string_size);
        ws->error_t = 1.f;
      }
    }
  }
  
  scratch_end(scratch);
  ProfEnd();
}
