diff --git a/.stylelintrc.js b/.stylelintrc.js
index 8d00b694..ae96662b 100644
--- a/.stylelintrc.js
+++ b/.stylelintrc.js
@@ -16,8 +16,11 @@ module.exports = {
// 允许 css 变量使用任意命名方式
'custom-property-pattern': null,
- // 允许任意类型的类名命名方式
+ // 允许任意类型的命名方式
'selector-class-pattern': null,
+ 'keyframes-name-pattern': null,
+ // 允许重复的 css 动画帧
+ 'keyframe-block-no-duplicate-selectors': null,
// 防止使用低性能的动画和过度属性
'plugin/no-low-performance-animation-properties': true,
diff --git a/docs/Dev.md b/docs/Dev.md
index 2fece392..4a3337ba 100644
--- a/docs/Dev.md
+++ b/docs/Dev.md
@@ -2,6 +2,7 @@
- 测试平板上的使用
- 300 自动签到功能在首次签到成功时报错
+- dmzj 隐藏漫画增加上下话切换
卷轴模式下图片的宽度应该要能简单调整,加设置项太麻烦,应该直接使用缩放功能来实现。目前已经可以在保持缩放的状态下滚动了,但现在使用的缩放库有几个问题
1. 没办法在缩小后保持图片居中
diff --git a/index.html b/index.html
index ed162e93..5b505be9 100644
--- a/index.html
+++ b/index.html
@@ -14,6 +14,7 @@
+ >
));
- }
-
- solidToast(message, opts);
};
-toast.success = (message: string, opts?: ToastOptions) =>
- toast(message, {
- icon: (
-
-
-
- ),
- ...opts,
- });
-toast.warn = (message: string, opts?: ToastOptions) =>
- toast(message, {
- icon: (
-
-
-
- ),
- ...opts,
- });
-toast.error = (message: string, opts?: ToastOptions) =>
- toast(message, {
- icon: (
-
-
-
- ),
- ...opts,
- });
+export const toast = new Proxy(_toast, {
+ get(target, propKey: keyof typeof _toast) {
+ init();
+ return target[propKey];
+ },
+ apply(target, propKey: keyof typeof _toast, args) {
+ init();
+ const fn: any = propKey ? target[propKey] : target;
+ return fn(...args) as unknown;
+ },
+});
diff --git a/src/components/useComponents/helper.ts b/src/components/useComponents/helper.ts
new file mode 100644
index 00000000..66407d63
--- /dev/null
+++ b/src/components/useComponents/helper.ts
@@ -0,0 +1,12 @@
+import type { JSX } from 'solid-js';
+import { render } from 'solid-js/web';
+
+/** 挂载 solid-js 组件 */
+export const mountComponents = (id: string, fc: () => JSX.Element) => {
+ const dom = document.createElement('div');
+ dom.id = id;
+ document.body.appendChild(dom);
+ const shadowDom = dom.attachShadow({ mode: 'open' });
+ render(fc, shadowDom);
+ return dom;
+};
diff --git a/src/components/useComponents/toast.module.css b/src/components/useComponents/toast.module.css
deleted file mode 100644
index c758bc25..00000000
--- a/src/components/useComponents/toast.module.css
+++ /dev/null
@@ -1,73 +0,0 @@
-.toast {
- will-change: transform;
-
- overflow: hidden;
- display: flex;
- align-items: center;
-
- padding: 0.7em 1em;
-
- color: #f3f4f6;
-
- background: #1f2937;
- border-radius: 0.5em;
- box-shadow: rgb(0 0 0 / 10%) 0 3px 10px, rgb(0 0 0 / 5%) 0 3px 3px;
-
- &[data-visible="true"] {
- animation: enter 0.2s ease-out;
- }
-
- &[data-visible="false"] {
- animation: leave 0.15s ease-in forwards;
- }
-}
-
-button {
- cursor: pointer;
-
- margin-left: 1em;
- padding: 0;
-
- color: currentcolor;
-
- background-color: transparent;
- border: none;
-
- &:hover {
- color: #e5e7ebcc;
- }
-}
-
-.progress {
- position: absolute;
- bottom: 0;
- left: 0;
-
- height: 2px;
-
- background-color: #41bcf4;
-}
-
-@keyframes enter {
- 0% {
- transform: scale(0.9);
- opacity: 0;
- }
-
- 100% {
- transform: scale(1);
- opacity: 1;
- }
-}
-
-@keyframes leave {
- 0% {
- transform: scale(1);
- opacity: 1;
- }
-
- 100% {
- transform: scale(0.9);
- opacity: 0;
- }
-}
diff --git a/src/helper/import.ts b/src/helper/import.ts
index a25b230e..41f0fc27 100644
--- a/src/helper/import.ts
+++ b/src/helper/import.ts
@@ -1,11 +1,16 @@
declare const isDevMode: boolean;
+const GMRe = /^GM/;
+const gmApiList = Object.keys(window).filter((name) => GMRe.test(name));
+const gmApiMap = Object.fromEntries(
+ gmApiList.map((name) => [name, window[name]]),
+);
+
unsafeWindow.crsLib = {
// 有些 cjs 模块会检查这个,所以在这里声明下
process: { env: { NODE_ENV: process.env.NODE_ENV } },
- // 把 GM 相关函数放进去以便其中使用
- GM_xmlhttpRequest,
- GM,
+ // 把 GM API 放进去以便使用
+ ...gmApiMap,
};
/**
@@ -18,11 +23,11 @@ const selfImportSync = (name: string) => {
// 通过提供 cjs 环境的变量来兼容 umd 模块加载器
// 将模块导出变量放到 crsLib 对象里,防止污染全局作用域和网站自身的模块产生冲突
- return GM_addElement('script', {
+ GM_addElement('script', {
textContent: `
window.crsLib['${name}'] = {};
${isDevMode ? `console.time('导入 ${name}');` : ''}
- (function (process, require, exports, module, GM, GM_xmlhttpRequest) {
+ (function (process, require, exports, module, ${gmApiList.join(', ')}) {
${code}
})(
window.crsLib.process,
@@ -36,8 +41,7 @@ const selfImportSync = (name: string) => {
return window.crsLib['${name}'];
},
},
- window.crsLib.GM,
- window.crsLib.GM_xmlhttpRequest,
+ ${gmApiList.map((apiName) => `window.crsLib.${apiName}`).join(', ')}
);
${isDevMode ? `console.timeEnd('导入 ${name}');` : ''}
`,
diff --git a/src/helper/index.ts b/src/helper/index.ts
index 46754623..955ef7e7 100644
--- a/src/helper/index.ts
+++ b/src/helper/index.ts
@@ -1,5 +1,4 @@
-import type { JSX } from 'solid-js';
-import { render } from 'solid-js/web';
+import { toast } from '../components/useComponents/Toast';
export type AsyncReturnType Promise> =
T extends (...args: any) => Promise ? R : any;
@@ -43,16 +42,6 @@ export const insertNode = (
node.insertBefore(frag, referenceNode);
};
-/** 挂载 solid-js 组件 */
-export const mountComponents = (id: string, fc: () => JSX.Element) => {
- const dom = document.createElement('div');
- dom.id = id;
- document.body.appendChild(dom);
- const shadowDom = dom.attachShadow({ mode: 'open' });
- render(fc, shadowDom);
- return dom;
-};
-
/** 返回 Dom 的点击函数 */
export const querySelectorClick = (selector: string) => {
const dom = querySelector(selector);
@@ -158,6 +147,7 @@ export const request = async (
url: string,
details?: Partial> & {
errorText?: string;
+ noTip?: true;
},
errorNum = 0,
): Promise> => {
@@ -172,11 +162,8 @@ export const request = async (
if (res.status !== 200) throw new Error(errorText);
return res;
} catch (error) {
- if (errorNum > 3) {
- if (errorText) {
- const { useToast } = require('../main');
- useToast().error(errorText);
- }
+ if (errorNum >= 3) {
+ if (errorText && !details?.noTip) toast.error(errorText);
throw new Error(errorText);
}
console.error(errorText, error);
diff --git a/src/helper/useInit.tsx b/src/helper/useInit.tsx
index eedd0891..2a399377 100644
--- a/src/helper/useInit.tsx
+++ b/src/helper/useInit.tsx
@@ -1,8 +1,10 @@
+import { For } from 'solid-js';
import { useManga } from '../components/useComponents/Manga';
import { useFab } from '../components/useComponents/Fab';
import { toast } from '../components/useComponents/Toast';
import { useSiteOptions } from './useSiteOptions';
import { useSpeedDial } from './useSpeedDial';
+import { request } from '.';
/**
* 对所有支持站点页面的初始化操作的封装
@@ -32,43 +34,56 @@ export const useInit = async >(
// 检查脚本的版本变化,提示用户
const version = await GM.getValue('Version');
if (version && version !== GM.info.script.version) {
- // FIXME: 实现通过 jsdelivr 获取指定版本的更新内容
- // const changelog = `
- // ## 新增
-
- // - 通过 M 键切换页面填充
-
- // ## 修复
-
- // - 增加拷贝漫画的支持域名
- // - 修复漫画柜失效问题
- // `;
(async () => {
- // const res = await request(
- // `https://cdn.jsdelivr.net/gh/hymbz/ComicReadScriptTest@${GM.info.script.version}/file`,
- // { errorText: '' },
- // );
- // toast(() => (
- //
- //
ComicReadScrip 已更新到 {GM.info.script.version}
- //
- // {res.responseText.match(/##.+?\n|(-.+?\n)+/g)!.map((mdText) => {
- // if (mdText[0] === '#') return
{mdText.split('##')}
;
- // if (mdText[0] === '-')
- // return (
- //
- // {mdText.match(/(?<=- ).+/g)!.map((item) => (
- // - {item}
- // ))}
- //
- // );
- // return null;
- // })}
- //
- //
- // ));
- // GM_setValue('Version', GM.info.script.version);
+ const res = await request(
+ `https://cdn.jsdelivr.net/gh/hymbz/ComicReadScriptTest@${GM.info.script.version}/docs/LatestChange.md`,
+ { errorText: '' },
+ );
+ toast(
+ () => (
+ <>
+ 🥳 ComicRead 已更新到 v{GM.info.script.version}
+
+
+ {(mdText) => {
+ switch (mdText[0]) {
+ case '#':
+ return {mdText.replace('### ', '')}
;
+ case '*':
+ return (
+
+
+ {(item) => - {item}
}
+
+
+ );
+ default:
+ return null;
+ }
+ }}
+
+
+ >
+ ),
+ {
+ id: 'Version Tip',
+ duration: Infinity,
+ // 手动点击关掉通知后才不会再次弹出
+ onDismiss: () => GM.setValue('Version', GM.info.script.version),
+ },
+ );
})();
+
+ // 监听储存的版本数据的变动,如果和当前版本一致就关掉弹窗
+ // 防止在更新版本后一次性打开多个页面,不得不一个一个关过去
+ const listenerId = await GM.addValueChangeListener(
+ 'Version',
+ async (_, __, newVersion) => {
+ if (newVersion !== GM.info.script.version) return;
+ toast.dismiss('Version Tip');
+ await GM.removeValueChangeListener(listenerId);
+ },
+ );
}
let menuId: number;
@@ -115,7 +130,7 @@ export const useInit = async >(
const showComic = async (show: boolean = options.autoShow) => {
if (loading) {
toast.warn('加载图片中,请稍候', {
- unmountDelay: 1500,
+ duration: 1500,
id: '加载图片中,请稍候',
});
return;
diff --git a/src/site/ehentai.tsx b/src/site/ehentai.tsx
index 47b5d96a..12ff63ff 100644
--- a/src/site/ehentai.tsx
+++ b/src/site/ehentai.tsx
@@ -146,7 +146,7 @@ declare const selected_link: HTMLElement;
`https://nhentai.net/api/galleries/search?query=${encodeURI(
titleDom.innerText,
)}`,
- { errorText: '' },
+ { errorText: 'nhentai 匹配出错', noTip: true },
);
} catch (_) {
newTagLine.innerHTML = `
diff --git a/src/site/jm.tsx b/src/site/jm.tsx
index 2e501420..9dff4527 100644
--- a/src/site/jm.tsx
+++ b/src/site/jm.tsx
@@ -15,7 +15,7 @@ import {
while (!unsafeWindow?.onImageLoaded) {
if (document.readyState === 'complete') {
- toast.error('无法获取图片', { duration: 60000 });
+ toast.error('无法获取图片', { duration: Infinity });
return;
}
// eslint-disable-next-line no-await-in-loop