diff --git a/build.js b/build.js index fa6836a..69808ea 100644 --- a/build.js +++ b/build.js @@ -7,13 +7,13 @@ const is_dev = process.argv.includes('--dev'); const is_dbg = process.argv.includes('--debug'); const no_source_map = process.argv.includes('--no-sourcemap'); -function sourcemap() { +function sourcemap(is_content_script = false) { if (no_source_map) return false; - if (is_dev) return "inline"; + if (is_dev && is_content_script) return "inline"; return true; } -async function build(name) { +async function build(name, is_content_script = true) { return await esbuild.build({ entryPoints: [`src/${name}.ts`], bundle: true, @@ -21,42 +21,36 @@ async function build(name) { outfile: `dist/${name}.js`, platform: 'browser', target: ['chrome100'], - sourcemap: sourcemap(), + sourcemap: sourcemap(is_content_script), }) } -async function buildTsx(name) { +async function buildTsx(names) { + const entryPoints = []; + for (const name of names) { + entryPoints.push(`src/${name}.tsx`); + } await esbuild.build({ - entryPoints: [`src/${name}.tsx`], + entryPoints: entryPoints, bundle: true, minify: !is_dbg, - outfile: `dist/${name}.js`, + outdir: 'dist', platform: 'browser', target: ['chrome100'], sourcemap: sourcemap(), jsx: 'automatic', loader: { '.css': 'global-css', '.module.css': 'local-css' }, + splitting: true, + format: 'esm', }); - - // 确保输出目录存在并写入同名 HTML - fs.mkdirSync(path.dirname(`dist/${name}.html`), { recursive: true }); - const cssFile = `dist/${name}.css`; - const cssLink = fs.existsSync(cssFile) ? `` : ''; - const html = ` - - - - - ${name} - ${cssLink} - - -
- - -`; - fs.writeFileSync(`dist/${name}.html`, html, 'utf8'); + for (const name of names) { + const srcHtmlPath = path.join('src', `${name}.html`); + const distHtmlPath = path.join('dist', `${name}.html`); + fs.copyFileSync(srcHtmlPath, distHtmlPath); + } } +fs.rmSync('dist', { recursive: true, force: true }); +fs.mkdirSync('dist', { recursive: true }); await build('qdchapter'); -await buildTsx('popup'); +await buildTsx(['popup', 'settings']); diff --git a/manifest.json b/manifest.json index 3980a43..ff4b256 100644 --- a/manifest.json +++ b/manifest.json @@ -31,5 +31,9 @@ ], "run_at": "document_start" } - ] + ], + "options_ui": { + "open_in_tab": true, + "page": "dist/settings.html" + } } \ No newline at end of file diff --git a/package.json b/package.json index 6b0d0a0..8e69bd4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,7 @@ { "name": "bookdownload", "dependencies": { + "@ant-design/icons": "^6.1.0", "@types/chrome": "^0.1.36", "@types/react": "^19.2.14", "@types/react-dom": "^19.2.3", diff --git a/src/config.ts b/src/config.ts new file mode 100644 index 0000000..d7227a6 --- /dev/null +++ b/src/config.ts @@ -0,0 +1,30 @@ +import { loadConfig, saveConfig } from "./utils"; + +type QdConfigData = { + AutoSaveChapter?: boolean; +} + +export class QdConfig { + static STORAGE_KEY = 'qd_config'; + config?: QdConfigData; + constructor() { + } + async init() { + this.config = await loadConfig(QdConfig.STORAGE_KEY, {}); + } + async save() { + if (!this.config) { + throw new Error('Config not initialized'); + } + await saveConfig(QdConfig.STORAGE_KEY, this.config); + } + get AutoSaveChapter(): boolean { + return this.config?.AutoSaveChapter ?? false; + } + set AutoSaveChapter(value: boolean) { + if (!this.config) { + throw new Error('Config not initialized'); + } + this.config.AutoSaveChapter = value; + } +} diff --git a/src/models/QdChatperInfo.tsx b/src/models/QdChatperInfo.tsx index 09e5229..22615b0 100644 --- a/src/models/QdChatperInfo.tsx +++ b/src/models/QdChatperInfo.tsx @@ -3,7 +3,7 @@ import { Descriptions, Card, Typography, Tag, Space, Button } from 'antd'; import type { BookInfo, ChapterInfo } from '../qdtypes'; import { get_chapter_content, saveAsFile } from '../utils'; -const { Title, Text } = Typography; +const { Title, Text, Paragraph } = Typography; interface QdChapterInfoProps { bookInfo: BookInfo; @@ -127,7 +127,7 @@ export default function QdChapterInfo({ bookInfo, chapterInfo }: QdChapterInfoPr {/* 章节内容 */} {chapters.map((paragraph, index) => ( -

{paragraph}

+ {paragraph} ))}
diff --git a/src/popup.html b/src/popup.html new file mode 100644 index 0000000..75739e7 --- /dev/null +++ b/src/popup.html @@ -0,0 +1,13 @@ + + + + + + 扩展弹出页 + + + +
+ + + \ No newline at end of file diff --git a/src/settings.html b/src/settings.html new file mode 100644 index 0000000..1a6db34 --- /dev/null +++ b/src/settings.html @@ -0,0 +1,13 @@ + + + + + + 扩展设置 + + + +
+ + + \ No newline at end of file diff --git a/src/settings.tsx b/src/settings.tsx new file mode 100644 index 0000000..3e29f35 --- /dev/null +++ b/src/settings.tsx @@ -0,0 +1,23 @@ +import { createRoot } from "react-dom/client"; +import { Typography, Tabs } from "antd"; +import type { TabsProps } from 'antd'; +import QdSettings from "./settings/QdSettings"; + +const { Title } = Typography; + +const items: TabsProps['items'] = [ + { + 'key': '1', + 'label': `起点设置`, + 'children': , + } +]; + +function Settings() { + return (<> + 扩展设置 + + ); +} + +createRoot(document.getElementById("root")!).render(); diff --git a/src/settings/QdSettings.tsx b/src/settings/QdSettings.tsx new file mode 100644 index 0000000..af2234f --- /dev/null +++ b/src/settings/QdSettings.tsx @@ -0,0 +1,18 @@ +import { Typography, Switch, FloatButton, Affix, Button } from "antd"; +import { SaveTwoTone, SaveOutlined } from "@ant-design/icons"; +import { useState } from "react"; + +const { Title } = Typography; + +export default function QdSettings() { + const [container, setContainer] = useState(null); + return ( +
+ 起点设置 + container}> + + + } tooltip="保存设置" /> +
+ ); +} diff --git a/src/utils.ts b/src/utils.ts index 489a15f..b4b83e2 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,4 +1,4 @@ -import { UrlParams, SendMessage, Message } from "./types"; +import type { UrlParams, SendMessage, Message } from "./types"; export const QD_CHAPTER_URLPATH_REGEX = /^\/chapter\/(\d+)\/(\d+)\/?$/; @@ -58,3 +58,15 @@ export function saveAsFile(filename: string, content: string, mimeType: string = document.body.removeChild(a); URL.revokeObjectURL(url); } + +export async function saveConfig(key: string, config: any): Promise { + return await chrome.storage.managed.set({ [key]: config }); +} + +export async function loadConfig(key: string, defaultValue: T): Promise { + const r = await chrome.storage.managed.get<{[key]: T | undefined}>(key); + if (r[key] === undefined) { + return defaultValue; + } + return r[key]; +}