diff --git a/mv2_simple_crx/src/actions/amazon.js b/mv2_simple_crx/src/actions/amazon.js index a62e067..0b33af6 100644 --- a/mv2_simple_crx/src/actions/amazon.js +++ b/mv2_simple_crx/src/actions/amazon.js @@ -39,14 +39,20 @@ export function amazon_search_list(data, sendResponse) { try { const tab = await tab_task.open_async(); - await tab.execute_script(injected_amazon_search_list, [{ url, category_keyword }], 'document_idle'); + const injected_result_list = await tab.execute_script(injected_amazon_search_list, [{ url, category_keyword }], 'document_idle'); + const injected_result = Array.isArray(injected_result_list) ? injected_result_list[0] : null; - const result = { code: 0, status: true, message: 'ok', data: { tab_id: tab.id, url, category_keyword } }; + const result = { + code: 0, + status: true, + message: 'ok', + data: { tab_id: tab.id, url, category_keyword, result: injected_result }, + }; send_action('amazon_search_list', result); - resolve({ tab_id: tab.id, url, category_keyword }); + resolve({ tab_id: tab.id, url, category_keyword, result: injected_result }); - // 可选:自动关 tab(默认不关,方便调试) - // tab && tab.remove && tab.remove(3500); + // 成功后关闭打开的 tab(同时会关闭 popup window) + tab.remove(0); } catch (err) { const result = { code: 30, diff --git a/mv2_simple_crx/src/injected/amazon_search_list.js b/mv2_simple_crx/src/injected/amazon_search_list.js index 2bd50be..4065150 100644 --- a/mv2_simple_crx/src/injected/amazon_search_list.js +++ b/mv2_simple_crx/src/injected/amazon_search_list.js @@ -1,18 +1,9 @@ // 注入到页面的 Amazon 搜索列表解析逻辑 export function injected_amazon_search_list(params) { - const action = 'amazon_search_list'; const start_url = params && params.url ? String(params.url) : location.href; const category_keyword = params && params.category_keyword ? String(params.category_keyword).trim() : ''; - function send(action_name, data) { - try { - chrome.runtime.sendMessage({ type: 'push', action: action_name, data }); - } catch (e) { - // ignore - } - } - function abs_url(href) { try { return new URL(href, location.origin).toString(); @@ -69,7 +60,12 @@ export function injected_amazon_search_list(params) { const items = extract_results(); - send(action, { stage: 'start', start_url, href: location.href, category_keyword, total: items.length }); - send(action, { stage: 'list', category_keyword, total: items.length, items }); - send(action, { stage: 'end', category_keyword, total: items.length }); + // 只返回一次结果,避免 send 太多影响判定 + return { + start_url, + href: location.href, + category_keyword, + total: items.length, + items, + }; } diff --git a/mv2_simple_crx/src/libs/tabs.js b/mv2_simple_crx/src/libs/tabs.js index dd00358..7ccd7c1 100644 --- a/mv2_simple_crx/src/libs/tabs.js +++ b/mv2_simple_crx/src/libs/tabs.js @@ -16,6 +16,15 @@ function attach_tab_helpers(tab) { return await execute_script(tab.id, fn, args, run_at); }; + tab.close_window = function close_window(delay_ms) { + delay_ms = Number.isFinite(delay_ms) ? delay_ms : 0; + setTimeout(() => { + if (tab.windowId) { + chrome.windows.remove(tab.windowId, () => void 0); + } + }, Math.max(0, delay_ms)); + }; + return tab; } @@ -96,9 +105,48 @@ export function create_tab_task(url) { return this; }, async open_async() { - // 直接返回 tab 对象(带 remove / execute_script) - const { tab } = await open_tab(this.url, { active: this.active !== false }); - return tab; + // 用 chrome.windows.create 新开窗口承载 tab + const win = await new Promise((resolve, reject) => { + chrome.windows.create( + { + url: 'about:blank', + type: 'popup', + focused: true, + top: this.top, + left: this.left, + width: this.width, + height: this.height, + }, + (w) => { + if (chrome.runtime.lastError) return reject(new Error(chrome.runtime.lastError.message)); + resolve(w); + }, + ); + }); + + const tab0 = win && win.tabs && win.tabs[0] ? win.tabs[0] : null; + if (!tab0 || !tab0.id) { + throw new Error('popup window 创建失败'); + } + + await new Promise((resolve, reject) => { + chrome.tabs.update(tab0.id, { url: this.url, active: this.active !== false }, () => { + if (chrome.runtime.lastError) return reject(new Error(chrome.runtime.lastError.message)); + resolve(true); + }); + }); + + const tab_done = await new Promise((resolve) => { + const on_updated = (tab_id, change_info, tab) => { + if (tab_id !== tab0.id) return; + if (change_info.status !== 'complete') return; + chrome.tabs.onUpdated.removeListener(on_updated); + resolve(tab); + }; + chrome.tabs.onUpdated.addListener(on_updated); + }); + + return attach_tab_helpers(tab_done); }, }; diff --git a/mv2_simple_crx/src/ui/index.css b/mv2_simple_crx/src/ui/index.css index 418519b..9055c1a 100644 --- a/mv2_simple_crx/src/ui/index.css +++ b/mv2_simple_crx/src/ui/index.css @@ -1,11 +1,9 @@ :root { - --bg: #0b1220; - --card: #121b2d; - --card2: #0f1728; - --text: #e7eefc; - --muted: #9db0d1; - --border: rgba(255, 255, 255, 0.08); - --primary: #4f8cff; + --bg: #ffffff; + --text: #111111; + --muted: #666666; + --border: #e5e7eb; + --primary: #2563eb; } * { box-sizing: border-box; } @@ -13,16 +11,19 @@ body { margin: 0; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Microsoft YaHei", Arial, sans-serif; - background: radial-gradient(1200px 700px at 20% 10%, rgba(79, 140, 255, 0.25), transparent 60%), - radial-gradient(900px 600px at 90% 20%, rgba(167, 139, 250, 0.18), transparent 55%), - var(--bg); + background: var(--bg); color: var(--text); + height: 100vh; } .app { - max-width: 1200px; - margin: 0 auto; + max-width: none; + margin: 0; padding: 18px; + height: 100vh; + display: flex; + flex-direction: column; + gap: 10px; } .header { @@ -42,28 +43,37 @@ body { .grid { display: grid; - grid-template-columns: 1fr 1fr; + grid-template-columns: 300px 1fr; gap: 14px; + flex: 1; + min-height: 0; } .card { border: 1px solid var(--border); - background: linear-gradient(180deg, rgba(255, 255, 255, 0.04), rgba(255, 255, 255, 0.02)); - backdrop-filter: blur(8px); - border-radius: 12px; + background: #ffffff; + border-radius: 8px; overflow: hidden; + display: flex; + flex-direction: column; + min-height: 0; } .card_title { padding: 12px 14px; border-bottom: 1px solid var(--border); - background: rgba(0, 0, 0, 0.15); + background: #f9fafb; font-weight: 600; font-size: 13px; } .form { padding: 14px; + display: flex; + flex-direction: column; + gap: 0; + flex: 1; + min-height: 0; } .label { @@ -77,19 +87,20 @@ body { .textarea { width: 100%; border: 1px solid var(--border); - background: rgba(0, 0, 0, 0.25); + background: #ffffff; color: var(--text); - border-radius: 10px; + border-radius: 6px; padding: 10px 10px; outline: none; } .textarea { - min-height: 160px; + min-height: 220px; resize: vertical; font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; font-size: 12px; line-height: 1.5; + flex: 1; } .row { @@ -100,21 +111,22 @@ body { .btn { border: 1px solid var(--border); - background: rgba(0, 0, 0, 0.25); + background: #ffffff; color: var(--text); - border-radius: 10px; + border-radius: 6px; padding: 10px 12px; cursor: pointer; font-weight: 600; } .btn.primary { - border-color: rgba(79, 140, 255, 0.45); - background: rgba(79, 140, 255, 0.18); + border-color: var(--primary); + color: #ffffff; + background: var(--primary); } .btn:hover { - border-color: rgba(255, 255, 255, 0.18); + border-color: #cbd5e1; } .hint { @@ -125,18 +137,26 @@ body { } .hint code { - color: #c7d2fe; + color: #111111; } .pre { margin: 0; padding: 14px; - background: rgba(0, 0, 0, 0.22); + background: #ffffff; font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; font-size: 12px; line-height: 1.5; - min-height: 260px; - color: #dbeafe; + min-height: 0; + color: #111111; + flex: 1; + overflow: auto; +} + +.pre_small { + flex: 0 0 180px; + max-height: 180px; + overflow: auto; } .pre_scroll { diff --git a/mv2_simple_crx/src/ui/index.html b/mv2_simple_crx/src/ui/index.html index 2359984..9530670 100644 --- a/mv2_simple_crx/src/ui/index.html +++ b/mv2_simple_crx/src/ui/index.html @@ -1,52 +1,59 @@ -
- - -__REQUEST_DONE 命中接口,会在「推送事件」里持续追加。
- __REQUEST_DONE 命中接口,会在「推送事件」里持续追加。
+