<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>本地收藏 · 私密存档 · 管理后台</title>
<style>
*{box-sizing:border-box;margin:0;padding:0}
:root{
  --bg:#0f1923;--card:#1a2636;--border:#2d3f55;
  --accent:#f97316;--accent2:#fb923c;--text:#e2e8f0;
  --muted:#64748b;--success:#22c55e;--error:#ef4444;
  --warn:#eab308;--font:"PingFang SC","Microsoft YaHei",system-ui,sans-serif;
}
html,body{height:100%}
body{font-family:var(--font);background:var(--bg);color:var(--text);font-size:14px;line-height:1.6}
a{color:var(--accent);text-decoration:none}
a:hover{text-decoration:underline}
button{cursor:pointer;font-family:var(--font)}

/* ---- Layout ---- */
.layout{display:flex;height:100vh}
.sidebar{width:240px;background:var(--card);border-right:1px solid var(--border);display:flex;flex-direction:column;flex-shrink:0;overflow-y:auto}
.main{flex:1;display:flex;flex-direction:column;overflow:hidden}
.topbar{background:var(--card);border-bottom:1px solid var(--border);padding:0 24px;display:flex;align-items:center;gap:16px;height:60px;flex-shrink:0}
.content{flex:1;overflow-y:auto;padding:24px}

/* ---- Sidebar ---- */
.sidebar-logo{padding:20px 16px;display:flex;align-items:center;gap:10px;border-bottom:1px solid var(--border)}
.sidebar-logo img{width:28px;height:28px;border-radius:6px}
.sidebar-logo span{font-size:15px;font-weight:700;color:var(--text)}
.sidebar-section{padding:12px 0}
.sidebar-title{font-size:11px;color:var(--muted);text-transform:uppercase;letter-spacing:.08em;padding:4px 16px 8px}
.sidebar-item{display:flex;align-items:center;gap:8px;padding:7px 16px;cursor:pointer;color:var(--muted);transition:all .15s;border-left:3px solid transparent;font-size:.9rem}
.sidebar-item:hover{background:#1e2d40;color:var(--text)}
.sidebar-item.active{background:rgba(249,115,22,.12);color:var(--accent);border-left-color:var(--accent)}
.sidebar-item .count{margin-left:auto;background:var(--border);color:var(--muted);border-radius:10px;padding:1px 7px;font-size:11px}
.sidebar-item.active .count{background:rgba(249,115,22,.2);color:var(--accent)}
.tag-cloud{padding:8px 16px;display:flex;flex-wrap:wrap;gap:5px}
.tag-pill{background:var(--bg);border:1px solid var(--border);border-radius:4px;padding:2px 8px;font-size:11px;color:var(--muted);cursor:pointer;transition:all .15s}
.tag-pill:hover,.tag-pill.active{border-color:var(--accent);color:var(--accent);background:rgba(249,115,22,.1)}
.sidebar-footer{padding:12px 16px;border-top:1px solid var(--border);margin-top:auto;font-size:.8rem;color:var(--muted)}

/* ---- Topbar ---- */
.search-box{flex:1;position:relative}
.search-box input{width:100%;background:var(--bg);border:1px solid var(--border);border-radius:8px;color:var(--text);padding:9px 14px 9px 38px;font-size:.9rem;outline:none;transition:border-color .2s}
.search-box input:focus{border-color:var(--accent)}
.search-box input::placeholder{color:var(--muted)}
.search-box::before{content:"🔍";position:absolute;left:12px;top:50%;transform:translateY(-50%);font-size:.85rem;opacity:.5}
.top-actions{display:flex;gap:10px}
.btn{padding:8px 16px;border:none;border-radius:8px;font-size:.88rem;font-weight:500;transition:all .15s;white-space:nowrap}
.btn-primary{background:var(--accent);color:#fff}
.btn-primary:hover{opacity:.88}
.btn-ghost{background:transparent;border:1px solid var(--border);color:var(--muted)}
.btn-ghost:hover{border-color:var(--accent);color:var(--accent)}
.btn-danger{background:rgba(239,68,68,.15);border:1px solid rgba(239,68,68,.3);color:var(--error)}
.btn-danger:hover{background:rgba(239,68,68,.25)}

/* ---- Stats bar ---- */
.stats-bar{display:flex;gap:24px;padding:12px 0;margin-bottom:4px;flex-wrap:wrap}
.stat-item{display:flex;align-items:center;gap:6px;font-size:.82rem;color:var(--muted)}
.stat-item strong{color:var(--text);font-size:1rem}

/* ---- Article List ---- */
.article-list{display:flex;flex-direction:column;gap:10px}
.article-card{background:var(--card);border:1px solid var(--border);border-radius:12px;padding:16px 18px;transition:border-color .2s;cursor:pointer}
.article-card:hover{border-color:rgba(249,115,22,.4)}
.article-card-header{display:flex;align-items:flex-start;gap:12px;margin-bottom:8px}
.article-card-title{flex:1;font-size:1rem;font-weight:600;line-height:1.4;color:var(--text);display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.article-card-actions{margin-left:auto;display:flex;gap:4px;flex-shrink:0;opacity:0;transition:opacity .2s}
.article-card:hover .article-card-actions{opacity:1}
.icon-btn{width:30px;height:30px;border:none;border-radius:6px;background:var(--bg);color:var(--muted);display:flex;align-items:center;justify-content:center;font-size:14px;transition:all .15s}
.icon-btn:hover{background:rgba(249,115,22,.15);color:var(--accent)}
.icon-btn.danger:hover{background:rgba(239,68,68,.15);color:var(--error)}
.article-card-meta{display:flex;gap:12px;flex-wrap:wrap;align-items:center;margin-bottom:8px}
.meta-chip{font-size:.78rem;color:var(--muted);background:var(--bg);border-radius:4px;padding:1px 7px}
.meta-chip.site{color:var(--accent2);border:1px solid rgba(249,115,22,.3);background:rgba(249,115,22,.08)}
.article-card-tags{display:flex;flex-wrap:wrap;gap:4px;margin-bottom:8px}
.tag-dot{font-size:.72rem;color:var(--muted);border:1px solid var(--border);border-radius:3px;padding:1px 6px}
.tag-dot:hover{border-color:var(--accent);color:var(--accent)}
.article-card-notes{font-size:.82rem;color:var(--muted);background:var(--bg);border-radius:6px;padding:8px 10px;margin-bottom:8px;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.article-card-footer{font-size:.78rem;color:var(--muted);display:flex;justify-content:space-between;align-items:center}
.article-card-url{color:var(--muted);text-overflow:ellipsis;overflow:hidden;white-space:nowrap;max-width:400px}
.source-link-btn{font-size:.75rem;color:var(--accent);cursor:pointer;text-decoration:none}
.source-link-btn:hover{text-decoration:underline}

/* ---- Pagination ---- */
.pagination{display:flex;align-items:center;justify-content:center;gap:8px;padding:20px 0}
.page-btn{padding:6px 14px;border:1px solid var(--border);border-radius:6px;background:var(--card);color:var(--muted);font-size:.85rem;transition:all .15s}
.page-btn:hover{border-color:var(--accent);color:var(--accent)}
.page-btn.active{background:var(--accent);color:#fff;border-color:var(--accent)}
.page-info{font-size:.82rem;color:var(--muted)}

/* ---- Reader Modal ---- */
.modal-overlay{position:fixed;inset:0;background:rgba(0,0,0,.7);z-index:1000;display:flex;align-items:center;justify-content:center;padding:20px;display:none}
.modal-overlay.show{display:flex}
.modal-reader{background:var(--card);border:1px solid var(--border);border-radius:16px;width:100%;max-width:800px;max-height:90vh;display:flex;flex-direction:column;overflow:hidden}
.modal-header{display:flex;align-items:center;gap:12px;padding:16px 20px;border-bottom:1px solid var(--border);flex-shrink:0}
.modal-title{flex:1;font-size:1rem;font-weight:600;color:var(--text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.modal-body{flex:1;overflow-y:auto;padding:0}
.modal-body iframe{width:100%;height:100%;min-height:70vh;border:none;background:#f8f7f4}
.modal-close{width:32px;height:32px;border:none;border-radius:8px;background:var(--bg);color:var(--muted);font-size:18px;display:flex;align-items:center;justify-content:center;transition:all .15s}
.modal-close:hover{background:rgba(239,68,68,.15);color:var(--error)}

/* ---- Empty ---- */
.empty-state{padding:60px 20px;text-align:center;color:var(--muted)}
.empty-state .emoji{font-size:3rem;margin-bottom:12px;display:block}

/* ---- Toast ---- */
.toast{position:fixed;bottom:24px;right:24px;background:var(--card);border:1px solid var(--border);border-radius:10px;padding:12px 18px;font-size:.88rem;color:var(--text);z-index:2000;display:none;animation:slideIn .3s ease}
.toast.show{display:block}
.toast.ok{border-color:rgba(34,197,94,.4);background:rgba(34,197,94,.12);color:var(--success)}
.toast.err{border-color:rgba(239,68,68,.4);background:rgba(239,68,68,.12);color:var(--error)}
@keyframes slideIn{from{transform:translateX(20px);opacity:0}to{transform:translateX(0);opacity:1}}

/* ---- Confirm dialog ---- */
.confirm-overlay{position:fixed;inset:0;background:rgba(0,0,0,.6);z-index:3000;display:none;align-items:center;justify-content:center}
.confirm-overlay.show{display:flex}
.confirm-box{background:var(--card);border:1px solid var(--border);border-radius:14px;padding:28px;max-width:360px;text-align:center}
.confirm-box h3{font-size:1rem;margin-bottom:10px}
.confirm-box p{font-size:.88rem;color:var(--muted);margin-bottom:20px}
.confirm-actions{display:flex;gap:10px;justify-content:center}
</style>
</head>
<body>

<div class="layout">
  <!-- ==================== 侧边栏 ==================== -->
  <aside class="sidebar">
    <div class="sidebar-logo">
      <img src="icons/icon32.png" alt="logo">
      <span>本地收藏</span>
    </div>

    <div class="sidebar-section">
      <div class="sidebar-title">导航</div>
      <div class="sidebar-item active" data-filter="all" onclick="setFilter('all')">
        📥 全部文章 <span class="count" id="countAll">5</span>
      </div>
    </div>

        <div class="sidebar-section">
      <div class="sidebar-title">📁 分类</div>
            <div class="sidebar-item" data-filter="category:测试分类" onclick="setFilter('category:测试分类')">
        📁 测试分类        <span class="count">0</span>
      </div>
          </div>
    
    
    <div class="sidebar-footer">
      共 5 篇文章<br>
      存储于本地文件夹
    </div>
  </aside>

  <!-- ==================== 主内容 ==================== -->
  <main class="main">
    <div class="topbar">
      <div class="search-box">
        <input type="search" id="searchInput" placeholder="搜索标题、摘要、笔记...（回车）" onkeydown="if(event.key==='Enter')doSearch()">
      </div>
      <div class="top-actions">
        <button class="btn btn-ghost" onclick="exportAll()">📦 导出全部</button>
        <button class="btn btn-primary" onclick="refreshList()">🔄 刷新</button>
      </div>
    </div>

    <div class="content">
      <!-- 统计 -->
      <div class="stats-bar">
        <div class="stat-item">📥 共 <strong id="statTotal">5</strong> 篇</div>
        <div class="stat-item">📁 <strong>1</strong> 个分类</div>
        <div class="stat-item">🏷️ <strong>0</strong> 个标签</div>
      </div>

      <!-- 文章列表 -->
      <div class="article-list" id="articleList"></div>

      <!-- 分页 -->
      <div class="pagination" id="pagination"></div>

      <!-- 空状态 -->
      <div class="empty-state" id="emptyState" style="display:none">
        <span class="emoji">📭</span>
        <p>还没有收藏任何文章<br><br>安装浏览器插件，点击图标即可保存好文</p>
      </div>
    </div>
  </main>
</div>

<!-- 阅读弹窗 -->
<div class="modal-overlay" id="readerModal">
  <div class="modal-reader">
    <div class="modal-header">
      <div class="modal-title" id="readerTitle"></div>
      <button class="btn btn-ghost" onclick="downloadArticle()" title="下载 HTML">💾 下载</button>
      <button class="modal-close" onclick="closeReader()">×</button>
    </div>
    <div class="modal-body">
      <iframe id="readerFrame" src=""></iframe>
    </div>
  </div>
</div>

<!-- Toast -->
<div class="toast" id="toast"></div>

<!-- 确认删除 -->
<div class="confirm-overlay" id="confirmOverlay">
  <div class="confirm-box">
    <h3>确认删除？</h3>
    <p>删除后无法恢复，文章将从本地永久移除。</p>
    <div class="confirm-actions">
      <button class="btn btn-ghost" onclick="closeConfirm()">取消</button>
      <button class="btn btn-danger" id="confirmBtn">确认删除</button>
    </div>
  </div>
</div>

<script>
const API = '/api';
let currentFilter = 'all';
let currentTag = '';
let currentPage = 0;
let currentArticles = [];
let pendingDeleteId = null;
let currentArticleId = null;
let searchVal = '';

// ================================================================
// 初始化
// ================================================================
document.addEventListener('DOMContentLoaded', () => {
  loadArticles();
  // 侧边栏交互
  document.querySelectorAll('.sidebar-item[data-filter]').forEach(el => {
    el.addEventListener('click', () => {
      document.querySelectorAll('.sidebar-item').forEach(x => x.classList.remove('active'));
      el.classList.add('active');
    });
  });
});

// ================================================================
// 加载文章列表
// ================================================================
async function loadArticles(offset = 0) {
  currentPage = offset;
  const params = new URLSearchParams({ limit: 30, offset });

  let category = '', tag = '', search = '';
  if (currentFilter.startsWith('category:')) category = currentFilter.split(':').slice(1).join(':');
  if (currentTag) tag = currentTag;
  if (searchVal) search = searchVal;

  if (category) params.set('category', category);
  if (search) params.set('search', search);

  try {
    const r = await fetch(`${API}/list.php?${params}`);
    const data = await r.json();
    currentArticles = data.items || [];
    renderList(currentArticles);
    renderPagination(data.total, data.limit, data.offset);
    document.getElementById('statTotal').textContent = data.total;
    document.getElementById('countAll').textContent = data.total;
    document.getElementById('emptyState').style.display = data.total === 0 ? 'block' : 'none';
  } catch (e) {
    toast('加载失败: ' + e.message, 'err');
  }
}

function renderList(items) {
  const el = document.getElementById('articleList');
  if (!items.length) { el.innerHTML = ''; return; }

  el.innerHTML = items.map(a => `
    <div class="article-card" onclick="openArticle('${a.id}')">
      <div class="article-card-header">
        <div class="article-card-title">${escHtml(a.title || '(无标题)')}</div>
        <div class="article-card-actions" onclick="event.stopPropagation()">
          <button class="icon-btn" onclick="openArticle('${a.id}')" title="阅读">📖</button>
          <button class="icon-btn" onclick="downloadArticleById('${a.id}')" title="下载">💾</button>
          <button class="icon-btn danger" onclick="confirmDelete('${a.id}')" title="删除">🗑</button>
        </div>
      </div>
      <div class="article-card-meta">
        ${a.site_name || a.site ? `<span class="meta-chip site">${escHtml(a.site_name || a.site)}</span>` : ''}
        ${a.author ? `<span class="meta-chip">👤 ${escHtml(a.author)}</span>` : ''}
        ${a.published ? `<span class="meta-chip">📅 ${a.published}</span>` : ''}
        <span class="meta-chip">💾 ${fmtDate(a.saved_at)}</span>
        ${a.word_count ? `<span class="meta-chip">📝 ${a.word_count}字</span>` : ''}
      </div>
      ${(a.tags || []).length ? `
      <div class="article-card-tags">
        ${a.tags.map(t => `<span class="tag-dot" onclick="toggleTag('${escAttr(t)}');event.stopPropagation()">#${escHtml(t)}</span>`).join('')}
      </div>` : ''}
      ${a.notes ? `<div class="article-card-notes">📝 ${escHtml(a.notes)}</div>` : ''}
      <div class="article-card-footer">
        <a class="source-link-btn" href="${escAttr(a.url)}" target="_blank" rel="noopener" onclick="event.stopPropagation()">🔗 ${escHtml(a.url)}</a>
        <span class="meta-chip">阅读 →</span>
      </div>
    </div>
  `).join('');
}

function renderPagination(total, limit, offset) {
  const totalPages = Math.ceil(total / limit);
  const current = Math.floor(offset / limit);
  const el = document.getElementById('pagination');
  if (totalPages <= 1) { el.innerHTML = ''; return; }

  let html = '';
  if (current > 0) html += `<button class="page-btn" onclick="loadArticles(${current - 1})">‹ 上一页</button>`;
  for (let i = 0; i < totalPages; i++) {
    if (i < 3 || i >= totalPages - 2 || Math.abs(i - current) <= 1) {
      html += `<button class="page-btn ${i === current ? 'active' : ''}" onclick="loadArticles(${i})">${i + 1}</button>`;
    } else if (i === 3 && current > 4) {
      html += '<span class="page-info">...</span>';
    }
  }
  if (current < totalPages - 1) html += `<button class="page-btn" onclick="loadArticles(${current + 1})">下一页 ›</button>`;
  el.innerHTML = html;
}

// ================================================================
// 搜索 & 筛选
// ================================================================
function doSearch() {
  searchVal = document.getElementById('searchInput').value.trim();
  loadArticles(0);
}

function setFilter(f) {
  currentFilter = f;
  currentTag = '';
  document.querySelectorAll('.tag-pill').forEach(el => el.classList.remove('active'));
  loadArticles(0);
}

function toggleTag(tag) {
  currentTag = currentTag === tag ? '' : tag;
  document.querySelectorAll('.tag-pill').forEach(el => {
    el.classList.toggle('active', el.dataset.tag === currentTag);
  });
  if (currentTag) {
    document.querySelectorAll('.sidebar-item').forEach(x => x.classList.remove('active'));
  }
  loadArticles(0);
}

function refreshList() {
  loadArticles(currentPage);
}

// ================================================================
// 阅读
// ================================================================
async function openArticle(id) {
  currentArticleId = id;
  try {
    const r = await fetch(`${API}/get.php?id=${id}`);
    const data = await r.json();
    if (!data.ok) throw new Error(data.error);
    document.getElementById('readerTitle').textContent = data.meta.title || '文章阅读';
    document.getElementById('readerFrame').src = `/articles/${id}/content.html`;
    document.getElementById('readerModal').classList.add('show');
  } catch (e) {
    toast('加载失败: ' + e.message, 'err');
  }
}

function closeReader() {
  document.getElementById('readerModal').classList.remove('show');
  document.getElementById('readerFrame').src = 'about:blank';
}

// ================================================================
// 删除
// ================================================================
function confirmDelete(id) {
  pendingDeleteId = id;
  document.getElementById('confirmOverlay').classList.add('show');
  document.getElementById('confirmBtn').onclick = () => doDelete(id);
}

function closeConfirm() {
  document.getElementById('confirmOverlay').classList.remove('show');
  pendingDeleteId = null;
}

async function doDelete(id) {
  closeConfirm();
  try {
    const r = await fetch(`${API}/delete.php`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ id }),
    });
    const data = await r.json();
    if (!data.ok) throw new Error(data.error);
    toast('已删除', 'ok');
    loadArticles(currentPage);
  } catch (e) {
    toast('删除失败: ' + e.message, 'err');
  }
}

// ================================================================
// 下载
// ================================================================
function downloadArticle() {
  if (!currentArticleId) return;
  downloadArticleById(currentArticleId);
}

async function downloadArticleById(id) {
  window.open(`/articles/${id}/content.html`, '_blank');
}

// ================================================================
// 导出全部
// ================================================================
function exportAll() {
  window.open('/articles/', '_blank');
}

// ================================================================
// 工具
// ================================================================
function escHtml(s) {
  if (!s) return '';
  return String(s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;');
}
function escAttr(s) {
  return escHtml(s).replace(/'/g, '&#39;');
}
function fmtDate(iso) {
  if (!iso) return '';
  try { return new Date(iso).toLocaleString('zh-CN', { year:'numeric',month:'2-digit',day:'2-digit',hour:'2-digit',minute:'2-digit' }); }
  catch(_) { return iso; }
}
function toast(msg, type = 'info') {
  const el = document.getElementById('toast');
  el.textContent = msg;
  el.className = `toast show ${type}`;
  setTimeout(() => el.classList.remove('show'), 3000);
}

// ESC 关闭弹窗
document.addEventListener('keydown', e => {
  if (e.key === 'Escape') { closeReader(); closeConfirm(); }
});
</script>
</body>
</html>
