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];
+}