๐Ÿ“‚ solution-1.0

โ† ่ฟ”ๅ›žไธŠ็บง
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ๅˆ†่ต„ไบง</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; background: #0f0f1a; color: #e0e0e0; height: 100vh; overflow: hidden; }
.header { background: #1a1a2e; padding: 12px 20px; border-bottom: 1px solid #2a2a4a; display: flex; align-items: center; gap: 12px; }
.header h1 { font-size: 16px; color: #FFD700; }
.header .subtitle { font-size: 12px; color: #888; }
.container { display: flex; height: calc(100vh - 50px); }
.panel { display: flex; flex-direction: column; }
.panel-left { width: 280px; min-width: 240px; border-right: 1px solid #2a2a4a; }
.panel-mid { width: 260px; min-width: 220px; border-right: 1px solid #2a2a4a; }
.panel-skills { width: 340px; min-width: 280px; border-right: 1px solid #2a2a4a; }
.panel-right { flex: 1; }
.panel-header { padding: 8px 14px; background: #16162a; border-bottom: 1px solid #2a2a4a; display: flex; justify-content: space-between; align-items: center; }
.panel-header h2 { font-size: 13px; color: #aaa; }
.panel-header .count { font-size: 11px; color: #666; background: #1e1e3a; padding: 2px 8px; border-radius: 8px; }
.list-area { flex: 1; overflow-y: auto; padding: 4px 0; }
.search-box { padding: 8px 14px; background: #16162a; border-bottom: 1px solid #2a2a4a; }
.search-box input { width: 100%; padding: 6px 10px; border: 1px solid #2a2a4a; border-radius: 4px; background: #1a1a30; color: #e0e0e0; font-size: 12px; outline: none; }
.search-box input:focus { border-color: #FFD700; }

/* ็›ฎๅฝ•ๆ ‘ */
.dir { user-select: none; }
.dir-header { padding: 5px 10px 5px 14px; cursor: pointer; display: flex; align-items: center; gap: 4px; font-size: 12px; color: #aaa; border-radius: 3px; }
.dir-header:hover { background: #1e1e38; }
.dir-header .toggle { width: 14px; font-size: 10px; color: #666; }
.dir-header .dname { flex: 1; }
.dir-header .dcount { font-size: 10px; color: #555; }
.dir-children { display: none; }
.dir.open > .dir-children { display: block; }
.dir-actions { display: none; margin-left: auto; gap: 2px; }
.dir-header:hover .dir-actions { display: flex; }
.dir-actions span { font-size: 10px; color: #666; padding: 0 4px; cursor: pointer; border-radius: 2px; }
.dir-actions span:hover { background: #2a2a4a; color: #FFD700; }

/* task items */
.task-item { padding: 5px 10px 5px 14px; cursor: pointer; font-size: 12px; display: flex; align-items: center; gap: 6px; border-left: 2px solid transparent; }
.task-item:hover { background: #1e1e38; }
.task-item.active { background: #252550; border-left-color: #FFD700; }
.task-item .tname { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.task-actions { display: none; gap: 2px; flex-shrink: 0; }
.task-item:hover .task-actions { display: flex; }
.task-actions span { font-size: 10px; color: #666; padding: 0 4px; cursor: pointer; border-radius: 2px; }
.task-actions span:hover { background: #2a2a4a; color: #FFD700; }
.task-actions .del:hover { color: #ef4444; }
.status-dot { width: 6px; height: 6px; border-radius: 50%; flex-shrink: 0; }
.dot-active { background: #4caf50; }
.dot-running { background: #ffa726; }
.dot-created { background: #666; }
.dot-completed { background: #888; }

/* skill items */
.skill-item { padding: 6px 12px; font-size: 12px; border-bottom: 1px solid #1e1e30; }
.skill-item:hover { background: #1a1a30; }
.skill-header { display: flex; align-items: center; gap: 6px; }
.skill-name { font-weight: bold; color: #e0e0e0; flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.skill-cat { font-size: 10px; color: #888; background: #1e1e30; padding: 1px 6px; border-radius: 4px; }
.skill-desc { font-size: 11px; color: #888; margin-top: 2px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.skill-tasks { margin-top: 4px; display: flex; flex-wrap: wrap; gap: 3px; }
.skill-task-badge { font-size: 10px; padding: 1px 6px; border-radius: 4px; background: #2a3a5a; color: #7eb8ff; white-space: nowrap; }
.skill-task-badge-none { background: #2a2a2a; color: #666; cursor: pointer; }
.skill-task-badge-none:hover { background: #3a2a2a; color: #ff7eb8; }
.skill-actions { margin-top: 4px; display: flex; gap: 4px; }
.btn-assign { font-size: 10px; padding: 2px 10px; border: 1px solid #3a5a3a; border-radius: 4px; background: #1a2a1a; color: #4ade80; cursor: pointer; }
.btn-assign:hover { background: #2a4a2a; }
.skill-assign-form { margin-top: 4px; display: flex; gap: 4px; }
.skill-assign-form select { flex: 1; padding: 3px 6px; border: 1px solid #2a2a4a; border-radius: 3px; background: #1a1a30; color: #e0e0e0; font-size: 11px; outline: none; }
.skill-assign-form select:focus { border-color: #FFD700; }
.skill-assign-form .btn-confirm { font-size: 10px; padding: 2px 8px; border: none; border-radius: 3px; background: #FFD700; color: #1a1a2e; cursor: pointer; }
.skill-category-group { font-size: 11px; color: #888; padding: 4px 12px; background: #131325; border-bottom: 1px solid #1e1e30; display: flex; align-items: center; gap: 6px; }
.skill-category-group .cat-toggle { cursor: pointer; font-size: 10px; }
.skill-category-group .cat-count { font-size: 10px; color: #555; }

/* doc items */
.doc-item { padding: 6px 14px; cursor: pointer; font-size: 12px; display: flex; align-items: center; gap: 8px; border-left: 2px solid transparent; }
.doc-item:hover { background: #1e1e38; }
.doc-item .doc-title { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: #ccc; }
.doc-item .doc-actions { display: none; gap: 4px; flex-shrink: 0; }
.doc-item:hover .doc-actions { display: flex; }
.doc-actions span { font-size: 10px; color: #888; padding: 2px 6px; cursor: pointer; border-radius: 3px; }
.doc-actions span:hover { background: #2a2a4a; color: #FFD700; }
.task-badge { font-size: 10px; padding: 1px 8px; border-radius: 8px; background: #2a3a5a; color: #7eb8ff; white-space: nowrap; flex-shrink: 0; max-width: 120px; overflow: hidden; text-overflow: ellipsis; }
.task-badge-none { background: #2a2a2a; color: #666; }
.doc-task-select { width: 100%; padding: 8px 10px; border: 1px solid #2a2a4a; border-radius: 4px; background: #1a1a30; color: #e0e0e0; font-size: 13px; margin-bottom: 12px; }
.doc-task-select:focus { border-color: #FFD700; outline: none; }
.doc-task-label { font-size: 11px; color: #888; margin-bottom: 6px; }

/* โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
   ๆŠฝๅฑ‰้ข„่งˆ
   โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• */
.drawer-overlay {
  display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%;
  background: rgba(0,0,0,0.55); z-index: 200;
}
.drawer {
  position: fixed; top: 0; right: -55vw; width: 55vw; height: 100vh;
  background: #14142a; border-left: 1px solid #2a2a4a;
  z-index: 201; transition: right 0.28s ease;
  display: flex; flex-direction: column;
  box-shadow: -4px 0 20px rgba(0,0,0,0.4);
}
.drawer.open { right: 0; }
.drawer-header {
  display: flex; align-items: center; padding: 12px 18px;
  border-bottom: 1px solid #2a2a4a; flex-shrink: 0;
}
.drawer-title { flex: 1; font-size: 15px; font-weight: 600; color: #FFD700; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.drawer-close { font-size: 18px; color: #888; cursor: pointer; padding: 4px 8px; border-radius: 4px; }
.drawer-close:hover { background: #2a2a4a; color: #FFD700; }
.drawer-content {
  flex: 1; overflow-y: auto; padding: 18px;
  font-size: 14px; line-height: 1.7; color: #d0d0d0;
}
.drawer-content h1 { font-size: 20px; color: #e0e0e0; margin: 16px 0 10px; padding-bottom: 6px; border-bottom: 1px solid #2a2a4a; }
.drawer-content h2 { font-size: 17px; color: #e0e0e0; margin: 14px 0 8px; }
.drawer-content h3 { font-size: 15px; color: #ccc; margin: 12px 0 6px; }
.drawer-content p { margin: 8px 0; }
.drawer-content ul, .drawer-content ol { margin: 8px 0 8px 20px; }
.drawer-content li { margin: 4px 0; }
.drawer-content code { background: #1e1e38; padding: 2px 6px; border-radius: 3px; font-size: 13px; color: #cba6f7; }
.drawer-content pre { background: #0d0d1a; padding: 14px 16px; border-radius: 6px; overflow-x: auto; margin: 10px 0; border: 1px solid #2a2a4a; font-size: 13px; }
.drawer-content pre code { background: none; padding: 0; color: inherit; }
.drawer-content blockquote { border-left: 3px solid #FFD700; padding: 6px 14px; margin: 10px 0; background: #1a1a30; color: #aaa; }
.drawer-content a { color: #7eb8ff; }
.drawer-content a:hover { text-decoration: underline; }
.drawer-content hr { border: none; border-top: 1px solid #2a2a4a; margin: 16px 0; }
.drawer-content table { width: 100%; border-collapse: collapse; margin: 10px 0; font-size: 13px; }
.drawer-content th, .drawer-content td { border: 1px solid #2a2a4a; padding: 6px 10px; text-align: left; }
.drawer-content th { background: #1a1a30; color: #ccc; }
.drawer-content tr:nth-child(even) { background: #12122a; }
.drawer-content img { max-width: 100%; border-radius: 6px; margin: 10px 0; }
.drawer-content .yaml-frontmatter { display: none; }
.drawer-loading { padding: 40px; text-align: center; color: #666; font-size: 14px; }
.drawer-skill-link { font-size: 11px; color: #888; padding: 4px 14px; background: #1a1a30; border-radius: 4px; cursor: pointer; display: inline-block; }
.drawer-skill-link:hover { background: #2a2a4a; color: #FFD700; }
.dir-badge { font-size: 10px; padding: 2px 6px; border-radius: 4px; background: #1e2a3a; color: #7eb8ff; white-space: nowrap; flex-shrink: 0; max-width: 160px; overflow: hidden; text-overflow: ellipsis; margin-left: 6px; cursor: default; }
.dir-badge-none { background: #2a2a2a; color: #666; }
.dir-badge:hover { background: #2a3a4a; }

.btn-sm { padding: 3px 10px; border: 1px solid #2a2a4a; border-radius: 3px; background: #1a1a30; color: #aaa; cursor: pointer; font-size: 11px; }
.btn-sm:hover { background: #2a2a4a; color: #FFD700; }
.btn-primary { background: #FFD700; color: #1a1a2e; border: none; padding: 4px 14px; border-radius: 4px; cursor: pointer; font-size: 12px; font-weight: bold; }
.btn-primary:hover { background: #FFC000; }
.modal-overlay { display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.5); z-index: 100; }
.modal { position: fixed; top: 50%; left: 50%; transform: translate(-50%,-50%); background: #1a1a2e; padding: 20px; border-radius: 8px; border: 1px solid #2a2a4a; z-index: 101; min-width: 300px; max-width: 500px; }
.modal h3 { font-size: 14px; margin-bottom: 12px; }
.modal input, .modal select { width: 100%; padding: 8px 10px; border: 1px solid #2a2a4a; border-radius: 4px; background: #1a1a30; color: #e0e0e0; font-size: 13px; margin-bottom: 12px; }
.modal-actions { display: flex; gap: 8px; justify-content: flex-end; }
.toast { position: fixed; bottom: 20px; left: 50%; transform: translateX(-50%); background: #2a2a4a; color: #e0e0e0; padding: 8px 20px; border-radius: 6px; font-size: 13px; display: none; z-index: 200; }
.dir-empty { padding: 8px 28px; font-size: 11px; color: #444; }
.move-dir-select { width: 100%; padding: 8px 10px; border: 1px solid #2a2a4a; border-radius: 4px; background: #1a1a30; color: #e0e0e0; font-size: 13px; margin-bottom: 12px; }
.move-dir-select:focus { border-color: #FFD700; outline: none; }
.skill-search { margin-bottom: 0; border-bottom: 1px solid #2a2a4a; }
</style>
</head>
<body>

<div class="header">
  <h1>๐Ÿ›๏ธ ๅˆ†่ต„ไบง</h1>
  <span class="subtitle">็›ฎๅฝ• ยท Task ยท Skill ยท ๆ–‡ๆกฃ</span>
  <span style="margin-left:auto;display:flex;gap:8px;">
    <button class="btn-sm" onclick="showCreateTask()">+ ๆ–ฐๅปบ Task</button>
    <button class="btn-sm" onclick="showCreateDir()">+ ๆ–ฐๅปบ็›ฎๅฝ•</button>
    <span class="count" id="totalCount">0</span>
  </span>
</div>

<div class="container">
  <!-- ๅˆ—1๏ผš็›ฎๅฝ•ๆ ‘ -->
  <div class="panel panel-left">
    <div class="panel-header"><h2>๐Ÿ“‚ ็›ฎๅฝ•ๆ ‘</h2><span class="count" id="dirCount">0</span></div>
    <div class="list-area" id="dirTree"></div>
    <div class="search-box">
      <input type="text" id="taskSearch" placeholder="ๆœ็ดขtask..." oninput="onSearchInput()">
    </div>
  </div>

  <!-- ๅˆ—2๏ผšTaskๅˆ—่กจ -->
  <div class="panel panel-mid">
    <div class="panel-header">
      <h2 id="selDirTitle">๐Ÿ“‹ Task ๅˆ—่กจ</h2>
      <span class="count" id="selDirCount">0</span>
    </div>
    <div class="list-area" id="taskList">
      <div style="padding:20px;text-align:center;color:#555;">โ† ้€‰ๆ‹ฉไธ€ไธช็›ฎๅฝ•</div>
    </div>
  </div>

  <!-- ๅˆ—3๏ผšๆŠ€่ƒฝ็ฎก็† -->
  <div class="panel panel-skills">
    <div class="panel-header">
      <h2>โšก Skill ๅฝ’ๅฑž็ฎก็†</h2>
      <span class="count" id="skillCount">0</span>
    </div>
    <div class="search-box skill-search">
      <input type="text" id="skillSearch" placeholder="ๆœ็ดขskill..." oninput="renderSkills()">
    </div>
    <div class="list-area" id="skillList">
      <div style="padding:20px;text-align:center;color:#555;">ๅŠ ่ฝฝไธญ...</div>
    </div>
  </div>

  <!-- ๅˆ—4๏ผšๆ–‡ๆกฃๅˆ—่กจ -->
  <div class="panel panel-right">
    <div class="panel-header">
      <h2>๐Ÿ“„ ๆ–‡ๆกฃ ยท ๅฝ’ๅฑžTask</h2>
      <span class="count" id="docCount">0</span>
    </div>
    <div id="docPanel" style="padding:20px;text-align:center;color:#555;">โ† ้€‰ๆ‹ฉไธ€ไธชTask</div>
  </div>
</div>

<!-- Modal -->
<div class="modal-overlay" id="modalOverlay" onclick="closeModal()"></div>
<div class="modal" id="modal" style="display:none;">
  <h3 id="modalTitle">ๆ–ฐๅปบ็›ฎๅฝ•</h3>
  <input type="text" id="modalInput" placeholder="็›ฎๅฝ•ๅ็งฐ">
  <div id="modalInputContainer"></div>
  <div style="font-size:11px;color:#555;margin-bottom:8px;">ไธŠ็บง็›ฎๅฝ•: <span id="modalParent"></span></div>
  <div class="modal-actions">
    <button class="btn-sm" onclick="closeModal()">ๅ–ๆถˆ</button>
    <button class="btn-primary" id="modalConfirm" onclick="confirmModal()">็กฎๅฎš</button>
  </div>
</div>

<div class="toast" id="toast"></div>

<!-- Drawer -->
<div class="drawer-overlay" id="drawerOverlay" onclick="closeDrawer()"></div>
<div class="drawer" id="drawer">
  <div class="drawer-header">
    <span class="drawer-title" id="drawerTitle"></span>
    <span class="drawer-close" onclick="closeDrawer()">โœ•</span>
  </div>
  <div class="drawer-content" id="drawerContent"></div>
</div>

<script src="/chat/marked.umd.js"></script>
<script src="/task-selector.js"></script>
<script>
let allTasks = [];
let allDirs = [];
let allTasksList = [];
let allSkills = [];
let allTaskListForSelect = [];
let selectedDirId = null;
let selectedTaskId = null;
let openDirIds = new Set();
let modalMode = 'create';
let modalContext = {};
let editingDocId = null;
let openSkillCat = new Set();

async function api(url, method, body) {
  const opts = { method, headers:{'Content-Type':'application/json'} };
  if (body) opts.body = JSON.stringify(body);
  const r = await fetch(url, opts);
  return r.json();
}

async function init() {
  await loadData();
}

async function loadData() {
  const [taskData, dirData, tasksData, skillsData] = await Promise.all([
    api('/api/tasks-with-folder'),
    api('/api/task-dirs'),
    api('/api/tasks'),
    api('/api/skills')
  ]);
  allTasks = (taskData.tasks || []).sort((a,b) => a.name.localeCompare(b.name));
  allDirs = dirData.dirs || [];
  allTasksList = tasksData || [];
  allSkills = skillsData || [];
  allTaskListForSelect = allTasksList;

  document.getElementById('totalCount').textContent = allTasks.length + ' tasks';
  document.getElementById('dirCount').textContent = allDirs.length + ' ็›ฎๅฝ•';
  document.getElementById('skillCount').textContent = allSkills.length + ' skills';
  renderAll();
  renderTaskList();
  renderSkills();
  renderDocList();
}

function getChildren(dirs, parentId) {
  return dirs.filter(d => d.parent_id === parentId).sort((a,b) => (a.sort_order||0) - (b.sort_order||0));
}

function getDirPath(dirId) {
  const parts = [];
  let current = allDirs.find(d => d.id === dirId);
  while (current) {
    parts.unshift(current.name);
    current = allDirs.find(d => d.id === current.parent_id);
  }
  return parts.join(' / ');
}

function renderDirTree() {
  const root = allDirs.find(d => d.parent_id === null);
  if (!root) return '<div style="padding:14px;color:#555;">ๆš‚ๆ— ็›ฎๅฝ•</div>';
  return renderNodes(allDirs, root.id, 0);
}

function renderNodes(dirs, parentId, depth) {
  const children = getChildren(dirs, parentId);
  if (!children.length) return '';

  let html = '';
  children.forEach(d => {
    const hasChildren = dirs.some(x => x.parent_id === d.id);
    const tasksHere = allTasks.filter(t => t.dir_id === d.id);
    let subHtml = '';
    if (hasChildren) {
      subHtml = renderNodes(dirs, d.id, depth + 1);
    }
    const q = (document.getElementById('taskSearch').value || '').toLowerCase();
    const dirMatch = !q || (d.name || '').toLowerCase().includes(q);
    const taskMatch = !q || tasksHere.some(t => (t.name || '').toLowerCase().includes(q));
    const subMatch = !q || subHtml.trim();
    if (q && !dirMatch && !taskMatch && !subMatch) return;

    const isOpen = openDirIds.has(d.id) || selectedDirId === d.id ? 'open' : '';
    html += `<div class="dir ${isOpen}" data-dir-id="${d.id}">`;
    html += `<div class="dir-header" style="padding-left:${14 + depth*14}px" draggable="true"
        ondragstart="onDirDragStart(event, '${d.id}')"
        ondragover="onDirDragOver(event)" ondragleave="onDirDragLeave(event)" ondrop="onDirDrop(event, '${d.id}')"
        onclick="toggleDir(this)">`;
    html += `<span class="toggle">โ–ถ</span>`;
    html += `<span class="dname">๐Ÿ“ ${d.name}</span>`;
    html += `<span class="dcount">${tasksHere.length}</span>`;
    html += `<span class="dir-actions">`;
    html += `<span onclick="event.stopPropagation();showCreateSubDir('${d.id}','${d.name}')">+</span>`;
    html += `<span onclick="event.stopPropagation();showRenameDir('${d.id}','${d.name}')">โœ๏ธ</span>`;
    html += `<span onclick="event.stopPropagation();deleteDir('${d.id}','${d.name}')">๐Ÿ—‘</span>`;
    html += `</span></div>`;
    html += `<div class="dir-children">`;
    html += subHtml;

    tasksHere.forEach(t => {
      if (!matchesSearch(t)) return;
      const dot = 'dot-' + (t.status || 'created');
      html += `<div class="task-item ${t.id === selectedTaskId ? 'active' : ''}" draggable="true"
          ondragstart="onTaskDragStart(event, '${t.id}')"
          onclick="selectTask('${t.id}', this)" style="padding-left:${30 + depth*14}px">`;
      html += `<span class="status-dot ${dot}"></span>`;
      html += `<span class="tname">${t.name}</span>`;
      html += `<span class="task-actions">
        <span onclick="event.stopPropagation();showMoveTaskDir('${t.id}','${t.name.replace(/'/g, "\\'")}')" title="ไฟฎๆ”นๅฝ’ๅฑž็›ฎๅฝ•">๐Ÿ“‚</span>
        <span onclick="event.stopPropagation();showRenameTask('${t.id}','${t.name.replace(/'/g, "\\'")}')" title="้‡ๅ‘ฝๅ">โœ๏ธ</span>
        <span class="del" onclick="event.stopPropagation();deleteTask('${t.id}','${t.name.replace(/'/g, "\\'")}')" title="ๅˆ ้™ค">๐Ÿ—‘</span>
      </span></div>`;
    });

    html += `</div></div>`;
  });
  return html;
}

function renderAll() {
  document.getElementById('dirTree').innerHTML = renderDirTree();
}

function getDirBadgeHtml(dirId) {
  if (!dirId) return `<span class="dir-badge dir-badge-none" title="ๆœชๅฝ’็ฑป">๐Ÿ“‚ ๆœชๅˆ†็ฑป</span>`;
  const dir = allDirs.find(d => d.id === dirId);
  if (!dir) return `<span class="dir-badge dir-badge-none" title="็›ฎๅฝ•ๅทฒๅˆ ้™ค">๐Ÿ“‚ ๆœช็Ÿฅ</span>`;
  const path = getDirPath(dirId);
  return `<span class="dir-badge" title="${path}">๐Ÿ“‚ ${dir.name}</span>`;
}

function getDirOptionsHtml(selectedDirId) {
  const roots = allDirs.filter(d => d.parent_id === null);
  let opts = `<option value="">โ€” ๆœชๅˆ†็ฑป โ€”</option>`;
  function addDirOptions(parentId, depth) {
    const children = allDirs.filter(d => d.parent_id === parentId).sort((a,b) => (a.sort_order||0)-(b.sort_order||0));
    children.forEach(d => {
      const sel = d.id === selectedDirId ? ' selected' : '';
      opts += `<option value="${d.id}"${sel}>${'&nbsp;&nbsp;'.repeat(depth)}${d.name}</option>`;
      addDirOptions(d.id, depth + 1);
    });
  }
  addDirOptions(null, 0);
  return opts;
}

function renderTaskList() {
  const q = (document.getElementById('taskSearch').value || '').toLowerCase();
  const tasks = q ? allTasks.filter(t => t.name.toLowerCase().includes(q)) : allTasks;

  document.getElementById('selDirTitle').innerHTML = '๐Ÿ“‹ ๅ…จ้ƒจ Task';
  document.getElementById('selDirCount').textContent = tasks.length;

  let html = '';
  tasks.forEach(t => {
    const dot = 'dot-' + (t.status || 'created');
    const dirBadge = getDirBadgeHtml(t.dir_id);
    html += `<div class="task-item ${t.id === selectedTaskId ? 'active' : ''}" onclick="selectTask('${t.id}', this)">`;
    html += `<span class="status-dot ${dot}"></span>`;
    html += `<span class="tname">${t.name}</span>`;
    html += dirBadge;
    html += `<span class="task-actions">
      <span onclick="event.stopPropagation();showMoveTaskDir('${t.id}','${t.name.replace(/'/g, "\\'")}')" title="ไฟฎๆ”นๅฝ’ๅฑž็›ฎๅฝ•">๐Ÿ“‚</span>
      <span onclick="event.stopPropagation();showRenameTask('${t.id}','${t.name.replace(/'/g, "\\'")}')" title="้‡ๅ‘ฝๅ">โœ๏ธ</span>
      <span class="del" onclick="event.stopPropagation();deleteTask('${t.id}','${t.name.replace(/'/g, "\\'")}')" title="ๅˆ ้™ค">๐Ÿ—‘</span>
    </span></div>`;
  });
  if (!html) html = '<div class="dir-empty">๐Ÿ“ญ ๆš‚ๆ— task</div>';
  document.getElementById('taskList').innerHTML = html;
}

// โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
//  ๆŠ€่ƒฝๆธฒๆŸ“
// โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•

function renderSkills() {
  const q = (document.getElementById('skillSearch').value || '').toLowerCase();
  
  // ๆŒ‰ category ๅˆ†็ป„
  const groups = {};
  allSkills.forEach(s => {
    if (q && !s.name.toLowerCase().includes(q) && !(s.description||'').toLowerCase().includes(q)) return;
    const cat = s.category || 'ๆœชๅˆ†็ฑป';
    if (!groups[cat]) groups[cat] = [];
    groups[cat].push(s);
  });

  let html = '';
  const catOrder = Object.keys(groups).sort((a,b) => a.localeCompare(b));
  catOrder.forEach(cat => {
    const skills = groups[cat];
    const isOpen = openSkillCat.has(cat);
    html += `<div class="skill-category-group">`;
    html += `<span class="cat-toggle" onclick="toggleSkillCat('${cat}')">${isOpen ? 'โ–ผ' : 'โ–ถ'}</span>`;
    html += `<span>${cat}</span>`;
    html += `<span class="cat-count">${skills.length}</span>`;
    html += `</div>`;
    if (isOpen) {
      skills.forEach(s => {
        const hasTasks = s.tasks && s.tasks.length > 0;
        const taskHtml = hasTasks
          ? s.tasks.map(t => `<span class="skill-task-badge">๐Ÿ“‹ ${t.task_name}</span>`).join('')
          : `<span class="skill-task-badge skill-task-badge-none" onclick="showSkillAssignForm('${s.name}', this)">+ ๆœชๅฝ’ๅฑž</span>`;
        
        html += `<div class="skill-item" data-skill="${s.name}">`;
        html += `<div class="skill-header">`;
        html += `<span class="skill-name">โšก ${s.name}</span>`;
        html += `<span class="skill-cat">${s.category || ''}</span>`;
        html += `</div>`;
        if (s.description) html += `<div class="skill-desc">${s.description}</div>`;
        html += `<div class="skill-tasks">${taskHtml}</div>`;
        html += `<div class="skill-actions" id="assign-form-${s.name}" style="display:none;">`;
        html += `</div>`;
        html += `</div>`;
      });
    }
  });
  if (!html) html = '<div class="dir-empty">๐Ÿ” ๆฒกๆœ‰ๅŒน้…็š„ skill</div>';
  document.getElementById('skillList').innerHTML = html;
}

function toggleSkillCat(cat) {
  if (openSkillCat.has(cat)) openSkillCat.delete(cat);
  else openSkillCat.add(cat);
  renderSkills();
}

function showSkillAssignForm(skillName, el) {
  TaskSelector.show({
    title: `โšก ้€‰ๆ‹ฉใ€Œ${skillName}ใ€็š„ๅฝ’ๅฑž Task`,
    onSelect: async (task) => {
      await api('/api/skill-tasks', 'POST', {
        skill_name: skillName,
        task_ids: [task.id]
      });
      showToast(`โœ… ${skillName} โ†’ ๅทฒๅฝ’ๅฑžใ€Œ${task.name}ใ€`);
      await loadData();
    }
  });
}

async function confirmSkillAssign(skillName) {
  // ๆ—งๆ–นๆณ•ไฟ็•™ๅ…ผๅฎน๏ผŒไฝ†ไธๅ†่ขซ่ฐƒ็”จ
}

// โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
//  ๆ–‡ๆกฃ
// โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•

async function renderDocList() {
  let docs = [];
  try {
    docs = await api('/api/documents-with-tasks');
  } catch(e) { console.error('load docs failed', e); }
  document.getElementById('docCount').textContent = docs.length;
  
  // ๆœชๅฝ’็ฑป็š„ๆ–‡ๆกฃๆŽ’ๅ‰้ข
  docs.sort((a, b) => {
    const aEmpty = (a.tasks || []).length === 0 ? 0 : 1;
    const bEmpty = (b.tasks || []).length === 0 ? 0 : 1;
    return aEmpty - bEmpty;
  });
  
  let html = '';
  docs.forEach(d => {
    const tasks = d.tasks || [];
    const badgeHtml = tasks.length > 0
      ? tasks.map(t => `<span class="task-badge" title="${t.name}">๐Ÿ“‹ ${t.name}</span>`).join('')
      : `<span class="task-badge task-badge-none">ๆœชๅฝ’็ฑป</span>`;
    
    html += `<div class="doc-item" data-doc-id="${d.id}">`;
    html += `<span class="doc-title" title="${d.title || d.id}">๐Ÿ“„ ${d.title || d.id}</span>`;
    html += badgeHtml;
    html += `<span class="doc-actions">`;
    html += `<span onclick="editDocTask('${d.id}','${(d.title || d.id).replace(/'/g, "\\'")}')" title="ไฟฎๆ”นๅฝ’ๅฑžtask">โœ๏ธ ๅฝ’ๅฑž</span>`;
    html += `</span></div>`;
  });
  if (!html) html = '<div style="padding:20px;text-align:center;color:#555;">ๆš‚ๆ— ๆ–‡ๆกฃ</div>';
  document.getElementById('docPanel').innerHTML = html;
}

function editDocTask(docId, docTitle) {
  // ่Žทๅ–ๅฝ“ๅ‰ๅทฒ้€‰็š„ task
  const docEl = document.querySelector(`.doc-item[data-doc-id="${docId}"]`);
  let currentTaskId = null;
  if (docEl) {
    const badges = docEl.querySelectorAll('.task-badge');
    for (const b of badges) {
      currentTaskId = allTasksList.find(t => b.textContent.includes(t.name))?.id;
      if (currentTaskId) break;
    }
  }
  
  // ไฟๅญ˜ๅˆฐๅ…จๅฑ€ๅ˜้‡ไพ›ๅ›ž่ฐƒไฝฟ็”จ
  window._editingDocId = docId;
  window._editingDocTitle = docTitle;

  TaskSelector.show({
    title: `๐Ÿ“„ ้€‰ๆ‹ฉใ€Œ${docTitle}ใ€็š„ๅฝ’ๅฑž Task`,
    currentTaskId: currentTaskId,
    onSelect: async (task) => {
      await api('/api/doc-tasks', 'POST', {
        doc_id: docId,
        task_ids: [task.id]
      });
      showToast(`โœ… ๆ–‡ๆกฃๅทฒๅฝ’ๅฑžใ€Œ${task.name}ใ€`);
      await renderDocList();
    }
  });
}

// โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
//  ้€š็”จ
// โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•

function onSearchInput() {
  renderAll();
  renderTaskList();
}

function toggleDir(el) {
  const dir = el.closest('.dir');
  const dirId = dir.dataset.dirId;
  dir.classList.toggle('open');
  const nowOpen = dir.classList.contains('open');
  el.querySelector('.toggle').textContent = nowOpen ? 'โ–ผ' : 'โ–ถ';
  if (dirId) {
    if (nowOpen) openDirIds.add(dirId);
    else openDirIds.delete(dirId);
  }
}

function selectTask(taskId, el) {
  selectedTaskId = taskId;
  document.querySelectorAll('.task-item').forEach(e => e.classList.remove('active'));
  if (el) el.classList.add('active');
}

function matchesSearch(obj) {
  const q = (document.getElementById('taskSearch').value || '').toLowerCase();
  if (!q) return true;
  return (obj.name || '').toLowerCase().includes(q);
}

// Modal
function showCreateTask() {
  modalMode = 'create_task';
  modalContext = {};
  document.getElementById('modalTitle').textContent = 'ๆ–ฐๅปบ Task';
  document.getElementById('modalOverlay').style.display = 'block';
  document.getElementById('modal').style.display = 'block';
  document.getElementById('modalInput').style.display = '';
  document.getElementById('modalInput').value = '';
  document.getElementById('modalInput').placeholder = 'Task ๅ็งฐ';
  document.getElementById('modalParent').style.display = 'none';
  document.getElementById('modalInputContainer').innerHTML = `
    <div class="doc-task-label">ๅฝ’ๅฑž็›ฎๅฝ•๏ผˆๅฏ้€‰๏ผ‰</div>
    <select class="move-dir-select" id="taskDirSelect">${getDirOptionsHtml('')}</select>
  `;
  document.getElementById('modalConfirm').textContent = 'ๅˆ›ๅปบ';
  document.getElementById('modalConfirm').onclick = confirmModal;
  document.getElementById('modalInput').focus();
}
function showCreateDir() { openModal('create', { parentId: 'td_root', parentName: 'ๆ น็›ฎๅฝ•' }, 'ๆ–ฐๅปบ็›ฎๅฝ•'); }
function showCreateSubDir(parentId, parentName) { openModal('create', { parentId, parentName }, `ๅœจใ€Œ${parentName}ใ€ไธ‹ๆ–ฐๅปบๅญ็›ฎๅฝ•`); }
function showRenameDir(dirId, name) { openModal('rename', { dirId, currentName: name }, '้‡ๅ‘ฝๅ็›ฎๅฝ•', name); }

function openModal(mode, ctx, title, val) {
  modalMode = mode;
  modalContext = ctx;
  document.getElementById('modalTitle').textContent = title;
  document.getElementById('modalOverlay').style.display = 'block';
  document.getElementById('modal').style.display = 'block';
  document.getElementById('modalInputContainer').innerHTML = '';
  document.getElementById('modalInput').style.display = '';
  document.getElementById('modalParent').style.display = '';
  document.getElementById('modalInput').value = val || '';
  document.getElementById('modalParent').textContent = ctx.parentName || '';
  document.getElementById('modalConfirm').textContent = '็กฎๅฎš';
  document.getElementById('modalConfirm').onclick = confirmModal;
  document.getElementById('modalInput').focus();
}

function closeModal() {
  document.getElementById('modalOverlay').style.display = 'none';
  document.getElementById('modal').style.display = 'none';
  document.getElementById('modalInputContainer').innerHTML = '';
  document.getElementById('modalInput').style.display = '';
  document.getElementById('modalParent').style.display = '';
  document.getElementById('modalConfirm').textContent = '็กฎๅฎš';
  document.getElementById('modalConfirm').onclick = confirmModal;
  editingDocId = null;
}

async function confirmModal() {
  const val = document.getElementById('modalInput').value.trim();
  if (!val) return;

  if (modalMode === 'create') {
    const r = await api('/api/task-dirs', 'POST', {
      action: 'create', name: val, parent_id: modalContext.parentId
    });
    if (r.ok) { showToast('โœ… ็›ฎๅฝ•ๅทฒๅˆ›ๅปบ'); await loadData(); }
  } else if (modalMode === 'create_task') {
    const select = document.getElementById('taskDirSelect');
    const dirId = select ? (select.value || null) : null;
    const r = await api('/api/tasks/create', 'POST', { name: val, dir_id: dirId });
    if (r.ok) { showToast('โœ… Task ๅทฒๅˆ›ๅปบ'); await loadData(); }
    else showToast('โŒ ' + (r.error || 'ๅˆ›ๅปบๅคฑ่ดฅ'));
  } else if (modalMode === 'rename') {
    const r = await api('/api/task-dirs', 'POST', {
      action: 'rename', id: modalContext.dirId, name: val
    });
    if (r.ok) { showToast('โœ… ๅทฒ้‡ๅ‘ฝๅ'); await loadData(); }
  } else if (modalMode === 'rename_task') {
    const r = await api('/api/tasks/rename', 'POST', {
      task_id: modalContext.taskId, name: val
    });
    if (r.ok) { showToast('โœ… Task ๅทฒ้‡ๅ‘ฝๅ'); await loadData(); }
    else showToast('โŒ ' + (r.error || '้‡ๅ‘ฝๅๅคฑ่ดฅ'));
  }
  closeModal();
}

async function deleteDir(dirId, name) {
  if (!confirm(`็กฎ่ฎคๅˆ ้™คใ€Œ${name}ใ€๏ผŸ`)) return;
  const r = await api('/api/task-dirs', 'POST', { action: 'delete', id: dirId });
  if (r.ok) { showToast('๐Ÿ—‘ ๅทฒๅˆ ้™ค'); await loadData(); }
  else showToast('โŒ ' + (r.error || 'ๅˆ ้™คๅคฑ่ดฅ'));
}

function showRenameTask(taskId, name) {
  openModal('rename_task', { taskId, currentName: name }, '้‡ๅ‘ฝๅ Task', name);
}

function showMoveTaskDir(taskId, taskName) {
  const task = allTasks.find(t => t.id === taskId);
  const currentDirId = task ? task.dir_id : null;
  const dirPath = currentDirId ? getDirPath(currentDirId) : 'ๆœชๅˆ†็ฑป';

  document.getElementById('modalTitle').textContent = `็งปๅŠจใ€Œ${taskName}ใ€ๅˆฐ็›ฎๅฝ•`;
  document.getElementById('modalOverlay').style.display = 'block';
  document.getElementById('modal').style.display = 'block';
  document.getElementById('modalInputContainer').innerHTML = `
    <div class="doc-task-label">้€‰ๆ‹ฉ็›ฎๆ ‡็›ฎๅฝ•</div>
    <div style="font-size:11px;color:#555;margin-bottom:10px;">ๅฝ“ๅ‰: ${dirPath}</div>
    <select class="move-dir-select" id="moveDirSelect">${getDirOptionsHtml(currentDirId)}</select>
  `;
  document.getElementById('modalInput').style.display = 'none';
  document.getElementById('modalParent').style.display = 'none';
  document.getElementById('modalConfirm').textContent = '็งปๅŠจ';
  modalMode = 'move_task';
  modalContext = { taskId, taskName };
  document.getElementById('modalConfirm').onclick = confirmMoveTaskDir;
}

async function confirmMoveTaskDir() {
  const select = document.getElementById('moveDirSelect');
  if (!select) return;
  const dirId = select.value || null;
  const taskId = modalContext.taskId;
  if (!taskId) return;
  const r = await api('/api/task-dirs', 'POST', { action: 'move_task', task_id: taskId, dir_id: dirId });
  if (r.ok) {
    const dirName = dirId ? (allDirs.find(d => d.id === dirId)?.name || 'ๆ น็›ฎๅฝ•') : 'ๆœชๅˆ†็ฑป';
    showToast(`โœ… ๅทฒ็งปๅŠจๅˆฐใ€Œ${dirName}ใ€`);
    closeModal();
    await loadData();
  } else {
    showToast('โŒ ' + (r.error || '็งปๅŠจๅคฑ่ดฅ'));
  }
}

async function deleteTask(taskId, name) {
  if (!confirm(`็กฎ่ฎคๅˆ ้™คTaskใ€Œ${name}ใ€๏ผŸ`)) return;
  const r = await api('/api/tasks/delete', 'POST', { task_id: taskId });
  if (r.ok) { showToast('๐Ÿ—‘ Task ๅทฒๅˆ ้™ค'); selectedTaskId = null; await loadData(); }
  else showToast('โŒ ' + (r.error || 'ๅˆ ้™คๅคฑ่ดฅ'));
}

// โ”€โ”€ ๆ‹–ๆ‹ฝ โ”€โ”€
let draggedTaskId = null;
let draggedDirId = null;

function onTaskDragStart(event, taskId) {
  draggedTaskId = taskId;
  draggedDirId = null;
  event.dataTransfer.effectAllowed = 'move';
  event.dataTransfer.setData('text/plain', 'task:' + taskId);
  event.target.classList.add('dragging');
}

function onDirDragStart(event, dirId) {
  event.stopPropagation();
  draggedDirId = dirId;
  draggedTaskId = null;
  event.dataTransfer.effectAllowed = 'move';
  event.dataTransfer.setData('text/plain', 'dir:' + dirId);
  event.target.closest('.dir-header').classList.add('dragging');
}

function onDirDragOver(event) {
  event.preventDefault();
  event.dataTransfer.dropEffect = 'move';
  event.currentTarget.classList.add('drag-over');
}

function onDirDragLeave(event) {
  event.currentTarget.classList.remove('drag-over');
}

async function onDirDrop(event, dirId) {
  event.preventDefault();
  event.currentTarget.classList.remove('drag-over');

  if (draggedTaskId) {
    const task = allTasks.find(t => t.id === draggedTaskId);
    if (!task) { draggedTaskId = null; return; }
    if (task.dir_id === dirId) { draggedTaskId = null; showToast('โ„น๏ธ ๅทฒๅœจๅŒไธ€็›ฎๅฝ•'); return; }
    const dir = allDirs.find(d => d.id === dirId);
    await moveTask(draggedTaskId, dirId);
    showToast(`โœ… ${task.name} โ†’ ๅทฒ็งป่‡ณใ€Œ${dir ? dir.name : dirId}ใ€`);
    draggedTaskId = null;
    document.querySelectorAll('.dragging').forEach(el => el.classList.remove('dragging'));
    return;
  }

  if (draggedDirId) {
    if (draggedDirId === dirId) { draggedDirId = null; showToast('โ„น๏ธ ไธ่ƒฝๆ‹–ๅˆฐ่‡ชๅทฑไธ‹้ข'); return; }
    function isDescendant(pid) {
      const children = allDirs.filter(d => d.parent_id === pid);
      return children.some(d => d.id === draggedDirId || isDescendant(d.id));
    }
    if (isDescendant(dirId)) {
      draggedDirId = null; showToast('โŒ ไธ่ƒฝๆ‹–ๅˆฐๅญ็›ฎๅฝ•'); return;
    }
    const srcDir = allDirs.find(d => d.id === draggedDirId);
    const tgtDir = allDirs.find(d => d.id === dirId);
    const r = await api('/api/task-dirs', 'POST', { action: 'move_dir', id: draggedDirId, parent_id: dirId });
    if (r.ok) {
      showToast(`โœ… ${srcDir ? srcDir.name : ''} โ†’ ใ€Œ${tgtDir ? tgtDir.name : dirId}ใ€`);
    } else {
      showToast('โŒ ' + (r.error || '็งปๅŠจๅคฑ่ดฅ'));
    }
    draggedDirId = null;
    document.querySelectorAll('.dragging').forEach(el => el.classList.remove('dragging'));
    await loadData();
  }
}

async function moveTask(taskId, dirId) {
  await api('/api/task-dirs', 'POST', { action: 'move_task', task_id: taskId, dir_id: dirId });
  await loadData();
}

function showToast(msg) {
  const t = document.getElementById('toast');
  t.textContent = msg; t.style.display = 'block';
  setTimeout(() => t.style.display = 'none', 2000);
}

// โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
//  ๆŠฝๅฑ‰้ข„่งˆ
// โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•

let _markedLoaded = false;
function ensureMarked() {
  if (typeof marked !== 'undefined') { _markedLoaded = true; return true; }
  return false;
}

function stripFrontmatter(text) {
  // ็งป้™ค YAML frontmatter
  return text.replace(/^---[\s\S]*?---\n*/, '').trim();
}

function openSkillPreview(name) {
  const skill = allSkills.find(s => s.name === name);
  if (!skill) { showToast('โŒ Skill ไธๅญ˜ๅœจ'); return; }
  document.getElementById('drawerTitle').textContent = 'โšก ' + name;
  document.getElementById('drawerContent').innerHTML = '<div class="drawer-loading">ๅŠ ่ฝฝไธญ...</div>';
  document.getElementById('drawer').classList.add('open');
  document.getElementById('drawerOverlay').style.display = 'block';
  
  fetch('/api/skill-content?name=' + encodeURIComponent(name))
    .then(r => r.json())
    .then(data => {
      if (data.error) {
        document.getElementById('drawerContent').innerHTML = '<div style="color:#ef4444;padding:20px;">โŒ ' + data.error + '</div>';
        return;
      }
      const desc = skill.description ? '<p style="color:#888;font-size:13px;margin-bottom:12px;">' + escapeHtml(skill.description) + '</p>' : '';
      const tasks = skill.tasks && skill.tasks.length > 0
        ? '<div style="margin-bottom:12px;display:flex;flex-wrap:wrap;gap:4px;">' +
          skill.tasks.map(t => '<span class="task-badge">๐Ÿ“‹ ' + escapeHtml(t.task_name) + '</span>').join('') +
          '</div>'
        : '';
      const raw = stripFrontmatter(data.content);
      let html;
      if (ensureMarked()) {
        // marked ๅ…จๅฑ€ๆ–นๅผ
        const markedLib = marked;
        html = markedLib.parse(raw, { breaks: true, gfm: true });
      } else {
        html = '<pre style="white-space:pre-wrap;">' + escapeHtml(raw) + '</pre>';
      }
      document.getElementById('drawerContent').innerHTML = desc + tasks + '<hr>' + html;
    })
    .catch(e => {
      document.getElementById('drawerContent').innerHTML = '<div style="color:#ef4444;padding:20px;">โŒ ๅŠ ่ฝฝๅคฑ่ดฅ: ' + e.message + '</div>';
    });
}

function openDocPreview(docId, title) {
  document.getElementById('drawerTitle').textContent = '๐Ÿ“„ ' + title;
  document.getElementById('drawerContent').innerHTML = '<div class="drawer-loading">ๅŠ ่ฝฝไธญ...</div>';
  document.getElementById('drawer').classList.add('open');
  document.getElementById('drawerOverlay').style.display = 'block';
  
  fetch('/api/doc-content?doc_id=' + encodeURIComponent(docId))
    .then(r => r.json())
    .then(data => {
      if (data.error || !data.content) {
        document.getElementById('drawerContent').innerHTML = '<div style="color:#ef4444;padding:20px;">โŒ ' + (data.error || 'ๆ–‡ๆกฃๅ†…ๅฎนไธบ็ฉบ') + '</div>';
        return;
      }
      const raw = stripFrontmatter(data.content);
      let html;
      if (ensureMarked()) {
        const markedLib = marked;
        html = markedLib.parse(raw, { breaks: true, gfm: true });
      } else {
        html = '<pre style="white-space:pre-wrap;">' + escapeHtml(raw) + '</pre>';
      }
      document.getElementById('drawerContent').innerHTML = html;
    })
    .catch(e => {
      document.getElementById('drawerContent').innerHTML = '<div style="color:#ef4444;padding:20px;">โŒ ๅŠ ่ฝฝๅคฑ่ดฅ: ' + e.message + '</div>';
    });
}

function closeDrawer() {
  document.getElementById('drawer').classList.remove('open');
  document.getElementById('drawerOverlay').style.display = 'none';
  document.getElementById('drawerContent').innerHTML = '';
}

function escapeHtml(text) {
  const d = document.createElement('div');
  d.textContent = text;
  return d.innerHTML;
}

// ็ป™ skill ๅ็งฐๅ’Œๆ–‡ๆกฃๆ ‡้ข˜ๆทปๅŠ ็‚นๅ‡ป้ข„่งˆ
document.addEventListener('click', function(e) {
  // Skill ๅ็งฐ็‚นๅ‡ป
  const skillNameEl = e.target.closest('.skill-name');
  if (skillNameEl) {
    const name = skillNameEl.textContent.replace('โšก ', '').trim();
    if (name) openSkillPreview(name);
    return;
  }
  // ๆ–‡ๆกฃๆ ‡้ข˜็‚นๅ‡ป
  const docTitleEl = e.target.closest('.doc-title');
  if (docTitleEl) {
    const docItem = docTitleEl.closest('.doc-item');
    if (docItem) {
      const docId = docItem.dataset.docId;
      const title = docTitleEl.textContent.replace('๐Ÿ“„ ', '').trim();
      if (docId) openDocPreview(docId, title);
    }
    return;
  }
});

// โ”€โ”€ ๅˆๅง‹ๅŒ–่‡ชๅŠจๅฑ•ๅผ€ โ”€โ”€
openSkillCat.add('ๆœชๅˆ†็ฑป');
openSkillCat.add('utilities');
openSkillCat.add('infrastructure');
openSkillCat.add('core');
openSkillCat.add('communication');
openSkillCat.add('finance');
openSkillCat.add('frontend');

init();
</script>
</body>
</html>