From 7a3faa36abd35f0cd144a3db91e6309a07a9cf3b Mon Sep 17 00:00:00 2001 From: lifegpc Date: Fri, 27 Feb 2026 15:42:52 +0800 Subject: [PATCH] Add support to open new tab --- build.js | 2 ++ package.json | 5 ++++- src/components/Icon.module.css | 22 ++++++++++++++++++++++ src/components/Icon.tsx | 8 ++++++++ src/css.d.ts | 4 ---- src/data.d.ts | 10 ++++++++++ src/manage/qd/VolumesList.module.css | 13 +++++++++++++ src/manage/qd/VolumesList.tsx | 26 ++++++++++++++++++++++++-- yarn.lock | 10 ++++++++++ 9 files changed, 93 insertions(+), 7 deletions(-) create mode 100644 src/components/Icon.module.css create mode 100644 src/components/Icon.tsx delete mode 100644 src/css.d.ts create mode 100644 src/data.d.ts diff --git a/build.js b/build.js index 05d4bf0..fcbca8c 100644 --- a/build.js +++ b/build.js @@ -4,6 +4,7 @@ import fs from 'node:fs'; import path from 'node:path'; import colors from 'colors'; import esbuildPluginEslint from 'esbuild-plugin-eslint'; +import { svgrPlugin } from 'esbuild-svgr-plugin' const is_dev = process.argv.includes('--dev'); const is_dbg = process.argv.includes('--debug'); @@ -45,6 +46,7 @@ function displayResult(result) { const plugins = [ esbuildPluginEslint(), + svgrPlugin(), ]; async function build(name, is_content_script = true) { diff --git a/package.json b/package.json index 7fddd2c..9a7b427 100644 --- a/package.json +++ b/package.json @@ -2,6 +2,7 @@ "name": "bookdownload", "dependencies": { "@ant-design/icons": "^6.1.0", + "@material-icons/svg": "^1.0.33", "@stablelib/sha256": "^2.0.1", "@types/chrome": "^0.1.36", "@types/react": "^19.2.14", @@ -10,6 +11,7 @@ "colors": "^1.4.0", "esbuild": "^0.27.3", "esbuild-plugin-eslint": "^0.3.12", + "esbuild-svgr-plugin": "^0.2.0", "eslint": "9", "eslint-plugin-react": "^7.37.5", "lodash.isequal": "^4.5.0", @@ -29,5 +31,6 @@ "buildrel": "node build.js", "lint": "eslint src" }, - "type": "module" + "type": "module", + "types": "./data.d.ts" } diff --git a/src/components/Icon.module.css b/src/components/Icon.module.css new file mode 100644 index 0000000..96f6e1c --- /dev/null +++ b/src/components/Icon.module.css @@ -0,0 +1,22 @@ +.icon { + display: inline-flex; + align-items: center; + color: inherit; + font-style: normal; + line-height: 0; + text-align: center; + text-transform: none; + vertical-align: -0.125em; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +.icon svg { + display: inline-block; + vertical-align: inherit; +} + +.icon > * { + line-height: 1; +} diff --git a/src/components/Icon.tsx b/src/components/Icon.tsx new file mode 100644 index 0000000..bfc35b4 --- /dev/null +++ b/src/components/Icon.tsx @@ -0,0 +1,8 @@ +import { HTMLProps } from "react"; +import styles from "./Icon.module.css"; + +export default function Icon(props: HTMLProps) { + return ( + {props.children} + ) +} diff --git a/src/css.d.ts b/src/css.d.ts deleted file mode 100644 index ed836a6..0000000 --- a/src/css.d.ts +++ /dev/null @@ -1,4 +0,0 @@ -declare module '*.css' { - const content: { [className: string]: string }; - export = content; -} diff --git a/src/data.d.ts b/src/data.d.ts new file mode 100644 index 0000000..8b7c495 --- /dev/null +++ b/src/data.d.ts @@ -0,0 +1,10 @@ +declare module '*.css' { + const content: { [className: string]: string }; + export = content; +} + +declare module "*.svg" { + import { ReactElement, SVGProps } from "react"; + const content: (props: SVGProps) => ReactElement; + export default content; +} diff --git a/src/manage/qd/VolumesList.module.css b/src/manage/qd/VolumesList.module.css index 70ebcf9..9770f78 100644 --- a/src/manage/qd/VolumesList.module.css +++ b/src/manage/qd/VolumesList.module.css @@ -8,7 +8,20 @@ width: 100%; } +.action { + margin-left: auto; +} + .saved { margin-left: auto; color: green; } + +.open { + color: gray; + cursor: pointer; +} + +.open:hover { + color: inherit; +} diff --git a/src/manage/qd/VolumesList.tsx b/src/manage/qd/VolumesList.tsx index 9d3a25c..2e4ab16 100644 --- a/src/manage/qd/VolumesList.tsx +++ b/src/manage/qd/VolumesList.tsx @@ -1,8 +1,10 @@ -import { Collapse, Flex } from "antd"; +import { Collapse, Flex, Tooltip } from "antd"; import type { Volume } from "../../qdtypes"; import styles from './VolumesList.module.css'; import { Link } from "react-router"; import { CheckCircleOutlined } from "@ant-design/icons"; +import OpenInNewTab from "../../../node_modules/@material-icons/svg/svg/open_in_new/twotone.svg"; +import Icon from "../../components/Icon"; export type VolumesListProps = { volumes: Volume[]; @@ -10,13 +12,33 @@ export type VolumesListProps = { oneLine?: boolean; } +async function open_in_qidian(bookId: number, chapterId: number) { + const url = `https://www.qidian.com/chapter/${bookId}/${chapterId}/`; + if (chrome && chrome.tabs) { + try { + const current = await chrome.tabs.getCurrent(); + await chrome.tabs.create({ url, active: true, openerTabId: current?.id, index: current ? current.index + 1 : undefined }); + } catch (e) { + console.error('Failed to open in new tab, falling back to window.open', e); + window.open(url, '_blank'); + } + } else { + window.open(url, '_blank'); + } +} + export default function VolumesList({ volumes, bookId, oneLine }: VolumesListProps) { return ( { const children = v.chapters.map(chapter => ( {chapter.name} - {chapter.isSaved && } + + {chapter.isSaved && } + + open_in_qidian(bookId, chapter.id)} /> + + )); return { diff --git a/yarn.lock b/yarn.lock index c481bb4..b65e067 100644 --- a/yarn.lock +++ b/yarn.lock @@ -297,6 +297,11 @@ resolved "https://registry.yarnpkg.com/@humanwhocodes/retry/-/retry-0.4.3.tgz#c2b9d2e374ee62c586d3adbea87199b1d7a7a6ba" integrity sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ== +"@material-icons/svg@^1.0.33": + version "1.0.33" + resolved "https://registry.yarnpkg.com/@material-icons/svg/-/svg-1.0.33.tgz#c3f4a5395c3b2267ce032081e9fe426995442ced" + integrity sha512-sYXcybBWH3rNijK1D6Dv1Se/aZ7OAC2cCNkn4HiZOXQwkswtRVoDqHPw+GfsBoIC70UintfPXUsmiuaxMicWtw== + "@rc-component/async-validator@^5.1.0": version "5.1.0" resolved "https://registry.yarnpkg.com/@rc-component/async-validator/-/async-validator-5.1.0.tgz#e81f31e676d9cadc71e4310bbf1749c7a5882291" @@ -1364,6 +1369,11 @@ esbuild-plugin-eslint@^0.3.12: resolved "https://registry.yarnpkg.com/esbuild-plugin-eslint/-/esbuild-plugin-eslint-0.3.12.tgz#729cc6661acc5f3640078163e32e76efdf687129" integrity sha512-n37Nn6vmh2tdGMnm6GZebiYJDrqERAReyvLFwdCWCw15ZvAu251i6cIeJWoVB8AiS+j3N97gylvf58ysIwii4g== +esbuild-svgr-plugin@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/esbuild-svgr-plugin/-/esbuild-svgr-plugin-0.2.0.tgz#08319365a1ef642ce16c3d83c847ff31370dc4e5" + integrity sha512-2+bY5KBjBieCBcsETx3wrmbsTAgRz2NZvRAgHl/xHqUG4g0E7k3J1/uumP0LwBGuOU3xKdDBjccU8Me/sKWL6A== + esbuild@^0.27.3: version "0.27.3" resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.27.3.tgz#5859ca8e70a3af956b26895ce4954d7e73bd27a8"