mirror of
https://github.com/primedigitaltech/azon_seeker.git
synced 2026-01-19 13:13:22 +08:00
Adjust Config & Update wanderDetailPage
This commit is contained in:
parent
407ebaaa75
commit
063aca6b94
2
.vscode/launch.json
vendored
2
.vscode/launch.json
vendored
@ -10,7 +10,7 @@
|
||||
"name": "Attach to side panel",
|
||||
"webRoot": "${workspaceFolder}/src/",
|
||||
"port": 9222,
|
||||
"urlFilter": "chrome-extension://*"
|
||||
"urlFilter": "chrome-extension://fmalpbpehdilmjhnanhpjmnkgbahopfj/*"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@ -8,17 +8,14 @@ import { isDev, log, port, r } from './utils';
|
||||
* Stub index.html to use Vite in development
|
||||
*/
|
||||
async function stubIndexHtml() {
|
||||
const views = ['options', 'popup', 'sidepanel'];
|
||||
const views = ['sidepanel'];
|
||||
|
||||
for (const view of views) {
|
||||
await fs.ensureDir(r(`extension/dist/${view}`));
|
||||
let data = await fs.readFile(r(`src/${view}/index.html`), 'utf-8');
|
||||
data = data
|
||||
.replace('"./main.ts"', `"http://localhost:${port}/${view}/main.ts"`)
|
||||
.replace(
|
||||
'<div id="app"></div>',
|
||||
'<div id="app">Vite server did not start</div>',
|
||||
);
|
||||
.replace('<div id="app"></div>', '<div id="app">Vite server did not start</div>');
|
||||
await fs.writeFile(r(`extension/dist/${view}/index.html`), data, 'utf-8');
|
||||
log('PRE', `stub ${view}`);
|
||||
}
|
||||
|
||||
1
shim.d.ts
vendored
1
shim.d.ts
vendored
@ -4,6 +4,5 @@ declare module 'webext-bridge' {
|
||||
export interface ProtocolMap {
|
||||
// define message protocol types
|
||||
// see https://github.com/antfu/webext-bridge#type-safe-protocols
|
||||
'tab-update': { tabId: number };
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,23 +2,20 @@
|
||||
import App from './views/App.vue';
|
||||
import { setupApp } from '~/logic/common-setup';
|
||||
|
||||
// 是否挂在ContentScript Vue APP
|
||||
// 是否在ContentScript挂载Vue APP
|
||||
const MOUNT_COMPONENT = false;
|
||||
|
||||
/**
|
||||
* mount component to context window
|
||||
*/
|
||||
const mountComponent = () => {
|
||||
// mount component to context window
|
||||
const container = document.createElement('div');
|
||||
container.id = __NAME__;
|
||||
const root = document.createElement('div');
|
||||
const styleEl = document.createElement('link');
|
||||
const shadowDOM =
|
||||
container.attachShadow?.({ mode: __DEV__ ? 'open' : 'closed' }) ||
|
||||
container;
|
||||
const shadowDOM = container.attachShadow?.({ mode: __DEV__ ? 'open' : 'closed' }) || container;
|
||||
styleEl.setAttribute('rel', 'stylesheet');
|
||||
styleEl.setAttribute(
|
||||
'href',
|
||||
browser.runtime.getURL('dist/contentScripts/index.css'),
|
||||
);
|
||||
styleEl.setAttribute('href', browser.runtime.getURL('dist/contentScripts/index.css'));
|
||||
shadowDOM.appendChild(styleEl);
|
||||
shadowDOM.appendChild(root);
|
||||
document.body.appendChild(container);
|
||||
@ -33,4 +30,3 @@ const mountComponent = () => {
|
||||
mountComponent();
|
||||
}
|
||||
})();
|
||||
|
||||
|
||||
@ -1,5 +1,9 @@
|
||||
import type { App } from 'vue';
|
||||
|
||||
/**
|
||||
* Setup Vue app
|
||||
* @param app Vue app
|
||||
*/
|
||||
export function setupApp(app: App) {
|
||||
// Inject a globally available `$app` object in template
|
||||
app.config.globalProperties.$app = {
|
||||
|
||||
@ -1,13 +1,41 @@
|
||||
/**
|
||||
* Execute Script on Document
|
||||
* @param tabId
|
||||
* @param func
|
||||
* @returns
|
||||
* Executes a provided asynchronous function in the context of a specific browser tab.
|
||||
* @param tabId - The ID of the browser tab where the script will be executed.
|
||||
* @param func - The asynchronous function to execute in the tab's context. This function
|
||||
* should be serializable and must not rely on external closures.
|
||||
* @param payload - An optional payload object to pass as an argument to the executed function.
|
||||
*
|
||||
* @returns A promise that resolves to the result of the executed function, or `null` if an error occurs.
|
||||
*
|
||||
* @throws This function does not throw directly but logs an error to the console if the script injection fails.
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* const result = await exec<number, { value: number }>(
|
||||
* tabId,
|
||||
* async (payload) => {
|
||||
* return payload?.value ?? 0;
|
||||
* },
|
||||
* { value: 42 }
|
||||
* );
|
||||
* console.log(result); // Outputs: 42
|
||||
* ```
|
||||
*/
|
||||
export async function exec<T>(tabId: number, func: () => Promise<T>): Promise<T | null> {
|
||||
export async function exec<T>(tabId: number, func: () => Promise<T>): Promise<T | null>;
|
||||
export async function exec<T, P extends Record<string, unknown>>(
|
||||
tabId: number,
|
||||
func: (payload: P) => Promise<T>,
|
||||
payload: P,
|
||||
): Promise<T | null>;
|
||||
export async function exec<T, P extends Record<string, unknown>>(
|
||||
tabId: number,
|
||||
func: (payload?: P) => Promise<T>,
|
||||
payload?: P,
|
||||
): Promise<T | null> {
|
||||
const injectResults = await browser.scripting.executeScript({
|
||||
target: { tabId },
|
||||
func,
|
||||
args: payload ? [payload] : undefined,
|
||||
});
|
||||
const ret = injectResults.pop();
|
||||
if (ret?.error) {
|
||||
|
||||
@ -1 +0,0 @@
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import Emittery from 'emittery';
|
||||
import type { AmazonGoodsLinkItem, AmazonPageWorker, AmazonPageWorkerEvents } from './types';
|
||||
import Browser from 'webextension-polyfill';
|
||||
import type { Tabs } from 'webextension-polyfill';
|
||||
import { exec } from '../execute-script';
|
||||
|
||||
/**
|
||||
@ -19,7 +19,7 @@ class AmazonPageWorkerImpl implements AmazonPageWorker {
|
||||
|
||||
readonly channel = new Emittery<AmazonPageWorkerEvents>();
|
||||
|
||||
private async getCurrentTab(): Promise<Browser.Tabs.Tab> {
|
||||
private async getCurrentTab(): Promise<Tabs.Tab> {
|
||||
const tab = await browser.tabs
|
||||
.query({ active: true, currentWindow: true })
|
||||
.then((tabs) => tabs[0]);
|
||||
@ -41,7 +41,7 @@ class AmazonPageWorkerImpl implements AmazonPageWorker {
|
||||
return url.toString();
|
||||
}
|
||||
|
||||
private async wanderSearchSinglePage(tab: Browser.Tabs.Tab) {
|
||||
private async wanderSearchSinglePage(tab: Tabs.Tab) {
|
||||
const tabId = tab.id!;
|
||||
// #region Wait for the Next button to appear, indicating that the product items have finished loading
|
||||
await exec(tabId, async () => {
|
||||
@ -134,7 +134,7 @@ class AmazonPageWorkerImpl implements AmazonPageWorker {
|
||||
return { data, hasNextPage };
|
||||
}
|
||||
|
||||
public async wanderSearchList(): Promise<void> {
|
||||
public async wanderSearchPage(): Promise<void> {
|
||||
const tab = await this.getCurrentTab();
|
||||
let stopSignal = false;
|
||||
const stop = async (_: unknown): Promise<void> => {
|
||||
@ -240,6 +240,23 @@ class AmazonPageWorkerImpl implements AmazonPageWorker {
|
||||
});
|
||||
}
|
||||
//#endregion
|
||||
//#region Fetch Goods' Images
|
||||
const imageUrls = await exec(tab.id!, async () => {
|
||||
const node = document.evaluate(
|
||||
`//div[@id='imgTagWrapperId']/img`,
|
||||
document,
|
||||
null,
|
||||
XPathResult.FIRST_ORDERED_NODE_TYPE,
|
||||
null,
|
||||
).singleNodeValue as HTMLImageElement | null;
|
||||
return node ? [node.getAttribute('src')!] : null;
|
||||
});
|
||||
imageUrls &&
|
||||
this.channel.emit('item-images-collected', {
|
||||
asin,
|
||||
urls: imageUrls,
|
||||
});
|
||||
//#endregion
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
10
src/logic/page-worker/types.d.ts
vendored
10
src/logic/page-worker/types.d.ts
vendored
@ -26,6 +26,14 @@ interface AmazonPageWorkerEvents {
|
||||
category2?: { name: string; rank: number };
|
||||
};
|
||||
|
||||
/**
|
||||
* The event is fired when images collected
|
||||
*/
|
||||
['item-images-collected']: {
|
||||
asin: string;
|
||||
urls: string[];
|
||||
};
|
||||
|
||||
/**
|
||||
* Error event that occurs when there is an issue with the Amazon page worker.
|
||||
*/
|
||||
@ -49,7 +57,7 @@ interface AmazonPageWorker {
|
||||
/**
|
||||
* Browsing goods search page and collect links to those goods.
|
||||
*/
|
||||
wanderSearchList(): Promise<void>;
|
||||
wanderSearchPage(): Promise<void>;
|
||||
|
||||
/**
|
||||
* Browsing goods detail page and collect target information.
|
||||
|
||||
@ -15,11 +15,6 @@ export async function getManifest() {
|
||||
description: pkg.description,
|
||||
action: {
|
||||
default_icon: './assets/icon-512.png',
|
||||
default_popup: './dist/popup/index.html',
|
||||
},
|
||||
options_ui: {
|
||||
page: './dist/options/index.html',
|
||||
open_in_tab: true,
|
||||
},
|
||||
background: isFirefox
|
||||
? {
|
||||
|
||||
@ -1,39 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import logo from '~/assets/logo.svg';
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<main class="option-page">
|
||||
<img :src="logo" class="" alt="extension icon" />
|
||||
<div class="title">Options Page</div>
|
||||
<SharedSubtitle />
|
||||
<div class="footer">
|
||||
Powered by Vite <pixelarticons-zap class="align-middle inline-block" />
|
||||
</div>
|
||||
</main>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.option-page {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100vh;
|
||||
background-color: #a3a8d4;
|
||||
|
||||
.title {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.input-field {
|
||||
padding: 0.5rem;
|
||||
border: 1px solid #333;
|
||||
border-radius: 0.25rem;
|
||||
}
|
||||
|
||||
.footer {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -1,12 +0,0 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<base target="_blank" />
|
||||
<title>Options</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="./main.ts"></script>
|
||||
</body>
|
||||
</html>
|
||||
@ -1,8 +0,0 @@
|
||||
import { createApp } from 'vue';
|
||||
import App from './Options.vue';
|
||||
import { setupApp } from '~/logic/common-setup';
|
||||
import '../styles';
|
||||
|
||||
const app = createApp(App);
|
||||
setupApp(app);
|
||||
app.mount('#app');
|
||||
@ -1,43 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
function openOptionsPage() {
|
||||
browser.runtime.openOptionsPage();
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<main class="popup">
|
||||
<Logo />
|
||||
<div class="title">Popup</div>
|
||||
<SharedSubtitle />
|
||||
<button @click="openOptionsPage">Open Options</button>
|
||||
</main>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.popup {
|
||||
min-width: 240px;
|
||||
min-height: 300px;
|
||||
padding: 1rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
.title {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
button {
|
||||
padding: 0.5rem 1rem;
|
||||
margin-top: 1rem;
|
||||
border: 1px solid #333;
|
||||
border-radius: 0.25rem;
|
||||
background-color: #fff;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.footer {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -1,12 +0,0 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<base target="_blank" />
|
||||
<title>Popup</title>
|
||||
</head>
|
||||
<body style="min-width: 100px">
|
||||
<div id="app"></div>
|
||||
<script type="module" src="./main.ts"></script>
|
||||
</body>
|
||||
</html>
|
||||
@ -1,7 +0,0 @@
|
||||
import App from './Popup.vue';
|
||||
import { setupApp } from '~/logic/common-setup';
|
||||
import '../styles';
|
||||
|
||||
const app = createApp(App);
|
||||
setupApp(app);
|
||||
app.mount('#app');
|
||||
@ -72,7 +72,7 @@ const onCollect = async () => {
|
||||
message.error(msg);
|
||||
});
|
||||
await worker.doSearch(keywords.value);
|
||||
await worker.wanderSearchList();
|
||||
await worker.wanderSearchPage();
|
||||
message.info('完成');
|
||||
};
|
||||
</script>
|
||||
|
||||
@ -94,8 +94,6 @@ export default defineConfig(({ command }) => ({
|
||||
},
|
||||
rollupOptions: {
|
||||
input: {
|
||||
options: r('src/options/index.html'),
|
||||
popup: r('src/popup/index.html'),
|
||||
sidepanel: r('src/sidepanel/index.html'),
|
||||
},
|
||||
},
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user