message / plugins /content /frontend /index.html
hunian
refactor(plugins): 插件短名并统一 MCP tool 为 {plugin}-{tool}
cc826a1
Raw
History Blame Contribute Delete
7.85 kB
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>内容提取</title>
<link rel="stylesheet" href="/static/plugin-ui.css">
<style>
.tabs { display: flex; gap: 4px; margin-bottom: 8px; flex-wrap: wrap; }
.tab { padding: 6px 12px; border-radius: 4px 4px 0 0; cursor: pointer; background: var(--bg-muted); color: var(--text-muted); border: 1px solid var(--border); border-bottom: none; }
.tab.active { background: var(--bg); color: var(--text); font-weight: 600; }
.tab-content { display: none; }
.tab-content.active { display: block; }
.result-actions { display: flex; gap: 8px; margin-bottom: 8px; }
</style>
</head>
<body>
<main class="plugin-shell">
<header class="plugin-header">
<div>
<h1 class="plugin-title">内容提取</h1>
<p class="plugin-subtitle">从支持的内容链接提取标题、正文和图片 OCR 文本。</p>
</div>
<span id="statusPill" class="plugin-status">检查中</span>
</header>
<div id="message" class="plugin-message"></div>
<section class="plugin-grid">
<div class="plugin-panel">
<h2 class="plugin-panel-title">提取参数</h2>
<div class="plugin-stack">
<div class="plugin-field">
<label class="plugin-label" for="url">内容链接</label>
<input id="url" type="url" placeholder="https://www.xiaohongshu.com/explore/... 或 https://weibo.com/...">
</div>
<label class="plugin-row">
<input id="includeOcr" type="checkbox" checked style="width:auto">
<span>启用图片 OCR</span>
</label>
<button id="extractBtn" class="plugin-button" onclick="extractContent()">开始提取</button>
</div>
</div>
<!-- 运行日志面板 -->
<div id="logPanel"></div>
</section>
<!-- 结果区 -->
<section id="resultSection" style="display:none;">
<div class="plugin-panel">
<h2 class="plugin-panel-title">提取结果</h2>
<!-- 操作按钮 -->
<div class="result-actions">
<button class="plugin-button secondary" onclick="copyResult()">复制结果</button>
<button class="plugin-button secondary" onclick="downloadResult()">下载 JSON</button>
</div>
<!-- Tabs -->
<div class="tabs">
<div class="tab active" data-tab="normalized" onclick="switchTab('normalized')">结构化内容</div>
<div class="tab" data-tab="raw" onclick="switchTab('raw')">原始内容</div>
<div class="tab" data-tab="ocr" onclick="switchTab('ocr')">OCR 文本</div>
</div>
<!-- Tab 内容 -->
<div id="tab-normalized" class="tab-content active">
<pre id="outputNormalized" class="plugin-output"></pre>
</div>
<div id="tab-raw" class="tab-content">
<pre id="outputRaw" class="plugin-output"></pre>
</div>
<div id="tab-ocr" class="tab-content">
<pre id="outputOcr" class="plugin-output"></pre>
</div>
</div>
</section>
</main>
<script src="/static/plugin-ui.js"></script>
<script>
const API = "/plugins/content/api";
let currentLogPanel = null;
let lastResult = null;
async function loadStatus() {
try {
const data = await PluginUI.request(`${API}/status`);
const pill = document.getElementById("statusPill");
pill.textContent = data.enabled === false ? "插件未启用" : "可用";
pill.className = `plugin-status ${data.enabled === false ? "warn" : "ok"}`;
} catch {
document.getElementById("statusPill").textContent = "状态未知";
}
}
// 切换 tab
function switchTab(tabName) {
document.querySelectorAll(".tab").forEach(t => t.classList.remove("active"));
document.querySelectorAll(".tab-content").forEach(c => c.classList.remove("active"));
document.querySelector(`[data-tab="${tabName}"]`).classList.add("active");
document.getElementById(`tab-${tabName}`).classList.add("active");
}
// 复制结果
function copyResult() {
if (lastResult) {
PluginUI.copyText(JSON.stringify(lastResult, null, 2));
PluginUI.showMessage("message", "已复制到剪贴板", "success");
}
}
// 下载结果
function downloadResult() {
if (lastResult) {
const blob = new Blob([JSON.stringify(lastResult, null, 2)], { type: "application/json" });
PluginUI.downloadBlob(blob, `content_extract_${lastResult.source_type || 'result'}.json`);
}
}
// 提取内容
async function extractContent() {
const btn = document.getElementById("extractBtn");
btn.disabled = true;
// 清理旧日志面板
if (currentLogPanel) {
PluginUI.stopPolling(currentLogPanel);
currentLogPanel.remove();
}
// 隐藏结果区
document.getElementById("resultSection").style.display = "none";
PluginUI.clearMessage("message");
try {
const url = document.getElementById("url").value.trim();
if (!url) throw new Error("请输入内容链接");
const includeOcr = document.getElementById("includeOcr").checked;
// 发送提取请求
const data = await PluginUI.request(`${API}/extract`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ url, include_ocr: includeOcr }),
});
if (data.run_id) {
// 创建日志面板
currentLogPanel = PluginUI.createRunLog({ title: "提取日志" });
document.getElementById("logPanel").appendChild(currentLogPanel);
// 开始轮询事件
PluginUI.pollRunEvents("content", data.run_id, currentLogPanel, {
onRunComplete: (run) => {
btn.disabled = false;
if (run.status === "succeeded" && run.result) {
// 显示结果区
document.getElementById("resultSection").style.display = "block";
lastResult = run.result;
// 填充 tabs
document.getElementById("outputNormalized").textContent =
run.result.normalized_content || run.result.content || "无结构化内容";
document.getElementById("outputRaw").textContent =
run.result.raw_html || run.result.raw_text || "无原始内容";
document.getElementById("outputOcr").textContent =
run.result.images_text || "无 OCR 文本";
}
},
onError: (err) => {
btn.disabled = false;
PluginUI.showMessage("message", `轮询错误: ${err.message}`, "error");
},
});
} else {
// 同步返回结果(wait=true)
btn.disabled = false;
if (data.status === "succeeded") {
document.getElementById("resultSection").style.display = "block";
lastResult = data;
document.getElementById("outputNormalized").textContent =
data.normalized_content || data.content || "无结构化内容";
document.getElementById("outputRaw").textContent =
data.raw_html || data.raw_text || "无原始内容";
document.getElementById("outputOcr").textContent =
data.images_text || "无 OCR 文本";
} else {
PluginUI.showMessage("message", data.error || "提取失败", "error");
}
}
} catch (error) {
btn.disabled = false;
PluginUI.showMessage("message", error.message, "error");
}
}
loadStatus();
</script>
</body>
</html>