Add support to open new tab

This commit is contained in:
2026-02-27 15:42:52 +08:00
parent c276073627
commit 7a3faa36ab
9 changed files with 93 additions and 7 deletions

View File

@@ -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) {

View File

@@ -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"
}

View File

@@ -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;
}

8
src/components/Icon.tsx Normal file
View File

@@ -0,0 +1,8 @@
import { HTMLProps } from "react";
import styles from "./Icon.module.css";
export default function Icon(props: HTMLProps<HTMLSpanElement>) {
return (
<span {...props} className={`${styles.icon}${props.className ? ` ${props.className}` : ''}`}>{props.children}</span>
)
}

4
src/css.d.ts vendored
View File

@@ -1,4 +0,0 @@
declare module '*.css' {
const content: { [className: string]: string };
export = content;
}

10
src/data.d.ts vendored Normal file
View File

@@ -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<SVGElement>) => ReactElement;
export default content;
}

View File

@@ -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;
}

View File

@@ -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 (<Collapse
items={volumes.map(v => {
const children = v.chapters.map(chapter => (
<Flex className={oneLine ? styles.chone : styles.ch} key={chapter.id}>
<Link to={`/qd/book/${bookId}/chapter/${chapter.id}`}>{chapter.name}</Link>
{chapter.isSaved && <CheckCircleOutlined className={styles.saved} />}
<Flex className={styles.action}>
{chapter.isSaved && <CheckCircleOutlined className={styles.saved} />}
<Tooltip title="在起点上查看(新标签页)">
<Icon><OpenInNewTab fill="currentColor" width="20" className={styles.open} onClick={() => open_in_qidian(bookId, chapter.id)} /></Icon>
</Tooltip>
</Flex>
</Flex>
));
return {

View File

@@ -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"