File size: 7,847 Bytes
fbd9d3d
 
 
 
 
 
 
e5e756a
 
 
 
 
 
 
 
fbd9d3d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
bb96b1a
fbd9d3d
 
 
 
 
e5e756a
fbd9d3d
 
 
e5e756a
 
 
 
 
 
fbd9d3d
 
e5e756a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
fbd9d3d
 
 
 
 
 
cc826a1
e5e756a
 
fbd9d3d
 
 
 
 
 
 
 
 
 
 
 
e5e756a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
fbd9d3d
e5e756a
 
 
 
 
 
 
 
 
 
 
 
 
fbd9d3d
 
 
e5e756a
 
 
 
fbd9d3d
 
 
e5e756a
fbd9d3d
e5e756a
 
 
 
 
 
 
cc826a1
e5e756a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
fbd9d3d
e5e756a
fbd9d3d
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
<!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>