feat: v6.2.0 Web Clipper — extension navigateur (capture article/selection/bookmark/screenshot)
- Service app/services/web_clipper.py: sanitize HTML, html->blocks, extraction article, creation page workspace-aware, rate limit 50/h, device registration - Router app/routers/web_clipper.py: POST /api/v2/web-clipper/clip, GET /status, POST /auth/verify, GET/DELETE /devices, GET /extensions (download page), auth via session ou Bearer (api_tokens / extension_devices) - Migration 19: extension_devices + extension_clips (+ indexes) - Extension Manifest V3: content.js (floating button, selection), background.js (clip + contextMenus), popup.html/js, clipper.css, icons - Settings UI: onglet Extensions (liste devices, revoke, test clip, liens download), page /extensions - Tests: 16 tests web_clipper (sanitize, blocks, article/bookmark/selection/screenshot, bearer, rate-limit, devices, extensions page) - Bump version 6.1.0 -> 6.2.0
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
// Popup logic
|
||||
const $ = (id) => document.getElementById(id);
|
||||
|
||||
async function load() {
|
||||
const cfg = await chrome.runtime.sendMessage({ action: "getConfig" });
|
||||
$("serverUrl").value = cfg.serverUrl || "http://localhost:8080";
|
||||
$("token").value = cfg.token || "";
|
||||
}
|
||||
|
||||
async function save() {
|
||||
const serverUrl = $("serverUrl").value.trim() || "http://localhost:8080";
|
||||
const token = $("token").value.trim();
|
||||
// deviceId preserved
|
||||
const cfg = await chrome.runtime.sendMessage({ action: "getConfig" });
|
||||
cfg.serverUrl = serverUrl.replace(/\/$/, "");
|
||||
cfg.token = token;
|
||||
await chrome.runtime.sendMessage({ action: "setConfig", config: cfg });
|
||||
show("Saved ✓", "ok");
|
||||
}
|
||||
|
||||
async function verify() {
|
||||
await save();
|
||||
const res = await chrome.runtime.sendMessage({ action: "auth" });
|
||||
if (res && res.authenticated) show(`Connected as ${res.user?.login || "user"} — ${res.devices} device(s)`, "ok");
|
||||
else if (res && res.authenticated === false) show("Not authenticated — check token / server URL", "err");
|
||||
else show(JSON.stringify(res).slice(0, 200), "warn");
|
||||
}
|
||||
|
||||
function show(msg, kind) {
|
||||
const el = $("status");
|
||||
el.textContent = msg;
|
||||
el.className = "status " + kind;
|
||||
}
|
||||
|
||||
async function doClip(type) {
|
||||
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
|
||||
if (!tab) return;
|
||||
// Get page data via scripting
|
||||
const [{ result }] = await chrome.scripting.executeScript({
|
||||
target: { tabId: tab.id },
|
||||
func: (clipType) => {
|
||||
const sel = window.getSelection();
|
||||
const html = document.documentElement.outerHTML;
|
||||
const selectionHtml = (() => {
|
||||
if (!sel || sel.rangeCount === 0) return "";
|
||||
const d = document.createElement("div");
|
||||
d.appendChild(sel.getRangeAt(0).cloneContents());
|
||||
return d.innerHTML;
|
||||
})();
|
||||
return {
|
||||
url: location.href,
|
||||
title: document.title,
|
||||
html: html,
|
||||
content_type: sel && sel.toString().trim().length > 20 ? "selection" : clipType,
|
||||
selection_html: selectionHtml,
|
||||
metadata: {
|
||||
og_description: document.querySelector('meta[property="og:description"]')?.content || document.querySelector('meta[name="description"]')?.content || ""
|
||||
}
|
||||
};
|
||||
},
|
||||
args: [type]
|
||||
});
|
||||
const resp = await chrome.runtime.sendMessage({ action: "clip", data: result });
|
||||
if (resp && resp.status === "ok") show(`Clipped → page ${resp.page_id} ✓`, "ok");
|
||||
else show(resp.detail || resp.error || "Clip failed", "err");
|
||||
}
|
||||
|
||||
$("save").addEventListener("click", save);
|
||||
$("verify").addEventListener("click", verify);
|
||||
$("clipArticle").addEventListener("click", () => doClip("article"));
|
||||
$("clipBookmark").addEventListener("click", () => doClip("bookmark"));
|
||||
$("clipSelection").addEventListener("click", () => doClip("selection"));
|
||||
$("openFlowDeck").addEventListener("click", (e) => { e.preventDefault(); chrome.tabs.create({ url: $("serverUrl").value || "http://localhost:8080" }); });
|
||||
$("openExtensions").addEventListener("click", (e) => { e.preventDefault(); chrome.tabs.create({ url: ($("serverUrl").value || "http://localhost:8080") + "/extensions" }); });
|
||||
|
||||
load();
|
||||
Reference in New Issue
Block a user