popup页面
Some checks failed
build / Build (push) Failing after 7s
test / Run tests (push) Failing after 8s
Some checks failed
build / Build (push) Failing after 7s
test / Run tests (push) Failing after 8s
This commit is contained in:
@ -116,7 +116,6 @@ export class Group {
|
|||||||
// 转发消息
|
// 转发消息
|
||||||
export function forwardMessage(prefix: string, path: string, from: Server, to: MessageSend, middleware?: ApiFunction) {
|
export function forwardMessage(prefix: string, path: string, from: Server, to: MessageSend, middleware?: ApiFunction) {
|
||||||
from.on(path, async (params, fromCon) => {
|
from.on(path, async (params, fromCon) => {
|
||||||
console.log("forwardMessage", path, prefix, params);
|
|
||||||
if (middleware) {
|
if (middleware) {
|
||||||
const resp = await middleware(params, new GetSender(fromCon));
|
const resp = await middleware(params, new GetSender(fromCon));
|
||||||
if (resp !== false) {
|
if (resp !== false) {
|
||||||
|
@ -152,7 +152,7 @@ export default defineConfig({
|
|||||||
}),
|
}),
|
||||||
new rspack.HtmlRspackPlugin({
|
new rspack.HtmlRspackPlugin({
|
||||||
filename: `${dist}/ext/src/popup.html`,
|
filename: `${dist}/ext/src/popup.html`,
|
||||||
template: `${src}/pages/popup/index.html`,
|
template: `${src}/pages/popup.html`,
|
||||||
inject: "head",
|
inject: "head",
|
||||||
title: "Home - ScriptCat",
|
title: "Home - ScriptCat",
|
||||||
minify: true,
|
minify: true,
|
||||||
|
@ -8,6 +8,7 @@ import { connect } from "@Packages/message/client";
|
|||||||
import Cache, { incr } from "@App/app/cache";
|
import Cache, { incr } from "@App/app/cache";
|
||||||
import { unsafeHeaders } from "@App/runtime/utils";
|
import { unsafeHeaders } from "@App/runtime/utils";
|
||||||
import EventEmitter from "eventemitter3";
|
import EventEmitter from "eventemitter3";
|
||||||
|
import { MessageQueue } from "@Packages/message/message_queue";
|
||||||
|
|
||||||
// GMApi,处理脚本的GM API调用请求
|
// GMApi,处理脚本的GM API调用请求
|
||||||
|
|
||||||
@ -35,6 +36,7 @@ export default class GMApi {
|
|||||||
constructor(
|
constructor(
|
||||||
private group: Group,
|
private group: Group,
|
||||||
private send: MessageSend,
|
private send: MessageSend,
|
||||||
|
private mq: MessageQueue,
|
||||||
private value: ValueService
|
private value: ValueService
|
||||||
) {
|
) {
|
||||||
this.logger = LoggerCore.logger().with({ service: "runtime/gm_api" });
|
this.logger = LoggerCore.logger().with({ service: "runtime/gm_api" });
|
||||||
@ -83,7 +85,6 @@ export default class GMApi {
|
|||||||
async buildDNRRule(reqeustId: number, params: GMSend.XHRDetails): Promise<{ [key: string]: string }> {
|
async buildDNRRule(reqeustId: number, params: GMSend.XHRDetails): Promise<{ [key: string]: string }> {
|
||||||
// 检查是否有unsafe header,有则生成dnr规则
|
// 检查是否有unsafe header,有则生成dnr规则
|
||||||
const headers = params.headers;
|
const headers = params.headers;
|
||||||
console.log(headers, !headers);
|
|
||||||
if (!headers) {
|
if (!headers) {
|
||||||
return Promise.resolve({});
|
return Promise.resolve({});
|
||||||
}
|
}
|
||||||
@ -181,12 +182,42 @@ export default class GMApi {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
start() {
|
@PermissionVerify.API()
|
||||||
this.group.on("gmApi", this.handlerRequest.bind(this));
|
GM_registerMenuCommand(request: Request, con: GetSender) {
|
||||||
|
console.log("registerMenuCommand", request.params);
|
||||||
|
const [id, name, accessKey] = request.params;
|
||||||
|
// 触发菜单注册, 在popup中处理
|
||||||
|
this.mq.emit("registerMenuCommand", {
|
||||||
|
uuid: request.script.uuid,
|
||||||
|
id: id,
|
||||||
|
name: name,
|
||||||
|
accessKey: accessKey,
|
||||||
|
con: con.getConnect(),
|
||||||
|
});
|
||||||
|
con.getConnect().onDisconnect(() => {
|
||||||
|
// 取消注册
|
||||||
|
this.mq.emit("unregisterMenuCommand", {
|
||||||
|
uuid: request.script.uuid,
|
||||||
|
name: name,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@PermissionVerify.API()
|
||||||
|
GM_unregisterMenuCommand(request: Request) {
|
||||||
|
const [id] = request.params;
|
||||||
|
// 触发菜单取消注册, 在popup中处理
|
||||||
|
this.mq.emit("unregisterMenuCommand", {
|
||||||
|
uuid: request.script.uuid,
|
||||||
|
id: id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理GM_xmlhttpRequest请求
|
||||||
|
handlerGmXhr() {
|
||||||
chrome.webRequest.onBeforeSendHeaders.addListener(
|
chrome.webRequest.onBeforeSendHeaders.addListener(
|
||||||
(details) => {
|
(details) => {
|
||||||
if (details.tabId === -1) {
|
if (details.tabId === -1) {
|
||||||
console.log(details);
|
|
||||||
// 判断是否存在X-Scriptcat-GM-XHR-Request-Id
|
// 判断是否存在X-Scriptcat-GM-XHR-Request-Id
|
||||||
// 讲请求id与chrome.webRequest的请求id关联
|
// 讲请求id与chrome.webRequest的请求id关联
|
||||||
if (details.requestHeaders) {
|
if (details.requestHeaders) {
|
||||||
@ -228,4 +259,9 @@ export default class GMApi {
|
|||||||
["responseHeaders", "extraHeaders"]
|
["responseHeaders", "extraHeaders"]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
start() {
|
||||||
|
this.group.on("gmApi", this.handlerRequest.bind(this));
|
||||||
|
this.handlerGmXhr();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ import { ResourceService } from "./resource";
|
|||||||
import { ValueService } from "./value";
|
import { ValueService } from "./value";
|
||||||
import { RuntimeService } from "./runtime";
|
import { RuntimeService } from "./runtime";
|
||||||
import { ServiceWorkerMessageSend } from "@Packages/message/window_message";
|
import { ServiceWorkerMessageSend } from "@Packages/message/window_message";
|
||||||
|
import { PopupService } from "./popup";
|
||||||
|
|
||||||
export type InstallSource = "user" | "system" | "sync" | "subscribe" | "vscode";
|
export type InstallSource = "user" | "system" | "sync" | "subscribe" | "vscode";
|
||||||
|
|
||||||
@ -31,5 +32,7 @@ export default class ServiceWorkerManager {
|
|||||||
script.init();
|
script.init();
|
||||||
const runtime = new RuntimeService(this.api.group("runtime"), this.sender, this.mq, value, script);
|
const runtime = new RuntimeService(this.api.group("runtime"), this.sender, this.mq, value, script);
|
||||||
runtime.init();
|
runtime.init();
|
||||||
|
const popup = new PopupService(this.api.group("popup"), this.mq, runtime);
|
||||||
|
popup.init();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
26
src/app/service/service_worker/popup.ts
Normal file
26
src/app/service/service_worker/popup.ts
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import { MessageQueue } from "@Packages/message/message_queue";
|
||||||
|
import { GetSender, Group } from "@Packages/message/server";
|
||||||
|
import { RuntimeService } from "./runtime";
|
||||||
|
|
||||||
|
// 处理popup页面的数据
|
||||||
|
export class PopupService {
|
||||||
|
constructor(
|
||||||
|
private group: Group,
|
||||||
|
private mq: MessageQueue,
|
||||||
|
private runtime: RuntimeService
|
||||||
|
) {}
|
||||||
|
|
||||||
|
registerMenuCommand(message: { uuid: string; id: string; name: string; accessKey: string; con: GetSender }) {
|
||||||
|
console.log("registerMenuCommand", message);
|
||||||
|
}
|
||||||
|
|
||||||
|
unregisterMenuCommand(message: { id: string }) {
|
||||||
|
console.log("unregisterMenuCommand", message);
|
||||||
|
}
|
||||||
|
|
||||||
|
init() {
|
||||||
|
// 处理脚本菜单数据
|
||||||
|
this.mq.subscribe("registerMenuCommand", this.registerMenuCommand.bind(this));
|
||||||
|
this.mq.subscribe("unregisterMenuCommand", this.unregisterMenuCommand.bind(this));
|
||||||
|
}
|
||||||
|
}
|
@ -34,7 +34,7 @@ export class RuntimeService {
|
|||||||
|
|
||||||
async init() {
|
async init() {
|
||||||
// 启动gm api
|
// 启动gm api
|
||||||
const gmApi = new GMApi(this.group, this.sender, this.value);
|
const gmApi = new GMApi(this.group, this.sender, this.mq, this.value);
|
||||||
gmApi.start();
|
gmApi.start();
|
||||||
|
|
||||||
this.group.on("stopScript", this.stopScript.bind(this));
|
this.group.on("stopScript", this.stopScript.bind(this));
|
||||||
@ -238,6 +238,15 @@ export class RuntimeService {
|
|||||||
this.saveScriptMatchInfo();
|
this.saveScriptMatchInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async deleteScriptMatch(uuid: string) {
|
||||||
|
if (!this.scriptMatchCache) {
|
||||||
|
await this.loadScriptMatchInfo();
|
||||||
|
}
|
||||||
|
this.scriptMatchCache!.delete(uuid);
|
||||||
|
this.scriptMatch.del(uuid);
|
||||||
|
this.saveScriptMatchInfo();
|
||||||
|
}
|
||||||
|
|
||||||
async registryPageScript(script: Script) {
|
async registryPageScript(script: Script) {
|
||||||
if (await Cache.getInstance().has("registryScript:" + script.uuid)) {
|
if (await Cache.getInstance().has("registryScript:" + script.uuid)) {
|
||||||
return;
|
return;
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
/* eslint-disable no-nested-ternary */
|
/* eslint-disable no-nested-ternary */
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import MessageInternal from "@App/app/message/internal";
|
|
||||||
import { MessageSender } from "@App/app/message/message";
|
|
||||||
import { ScriptMenu } from "@App/runtime/service_worker/runtime";
|
import { ScriptMenu } from "@App/runtime/service_worker/runtime";
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
@ -21,13 +19,9 @@ import {
|
|||||||
IconMinus,
|
IconMinus,
|
||||||
IconSettings,
|
IconSettings,
|
||||||
} from "@arco-design/web-react/icon";
|
} from "@arco-design/web-react/icon";
|
||||||
import IoC from "@App/app/ioc";
|
|
||||||
import ScriptController from "@App/app/service/script/controller";
|
|
||||||
import { SCRIPT_RUN_STATUS_RUNNING } from "@App/app/repo/scripts";
|
import { SCRIPT_RUN_STATUS_RUNNING } from "@App/app/repo/scripts";
|
||||||
import { RiPlayFill, RiStopFill } from "react-icons/ri";
|
import { RiPlayFill, RiStopFill } from "react-icons/ri";
|
||||||
import RuntimeController from "@App/runtime/content/runtime";
|
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { SystemConfig } from "@App/pkg/config/config";
|
|
||||||
import { ScriptIcons } from "@App/pages/options/routes/utils";
|
import { ScriptIcons } from "@App/pages/options/routes/utils";
|
||||||
|
|
||||||
const CollapseItem = Collapse.Item;
|
const CollapseItem = Collapse.Item;
|
||||||
@ -51,10 +45,6 @@ const ScriptMenuList: React.FC<{
|
|||||||
currentUrl: string;
|
currentUrl: string;
|
||||||
}> = ({ script, isBackscript, currentUrl }) => {
|
}> = ({ script, isBackscript, currentUrl }) => {
|
||||||
const [list, setList] = useState([] as ScriptMenu[]);
|
const [list, setList] = useState([] as ScriptMenu[]);
|
||||||
const message = IoC.instance(MessageInternal) as MessageInternal;
|
|
||||||
const scriptCtrl = IoC.instance(ScriptController) as ScriptController;
|
|
||||||
const runtimeCtrl = IoC.instance(RuntimeController) as RuntimeController;
|
|
||||||
const systemConfig = IoC.instance(SystemConfig) as SystemConfig;
|
|
||||||
const [expandMenuIndex, setExpandMenuIndex] = useState<{
|
const [expandMenuIndex, setExpandMenuIndex] = useState<{
|
||||||
[key: string]: boolean;
|
[key: string]: boolean;
|
||||||
}>({});
|
}>({});
|
||||||
@ -70,23 +60,23 @@ const ScriptMenuList: React.FC<{
|
|||||||
setList(script);
|
setList(script);
|
||||||
}, [script]);
|
}, [script]);
|
||||||
|
|
||||||
useEffect(() => {
|
// useEffect(() => {
|
||||||
// 监听脚本运行状态
|
// // 监听脚本运行状态
|
||||||
const channel = runtimeCtrl.watchRunStatus();
|
// const channel = runtimeCtrl.watchRunStatus();
|
||||||
channel.setHandler(([id, status]: any) => {
|
// channel.setHandler(([id, status]: any) => {
|
||||||
setList((prev) => {
|
// setList((prev) => {
|
||||||
const newList = [...prev];
|
// const newList = [...prev];
|
||||||
const index = newList.findIndex((item) => item.id === id);
|
// const index = newList.findIndex((item) => item.id === id);
|
||||||
if (index !== -1) {
|
// if (index !== -1) {
|
||||||
newList[index].runStatus = status;
|
// newList[index].runStatus = status;
|
||||||
}
|
// }
|
||||||
return newList;
|
// return newList;
|
||||||
});
|
// });
|
||||||
});
|
// });
|
||||||
return () => {
|
// return () => {
|
||||||
channel.disChannel();
|
// channel.disChannel();
|
||||||
};
|
// };
|
||||||
}, []);
|
// }, []);
|
||||||
|
|
||||||
const sendMenuAction = (sender: MessageSender, channelFlag: string) => {
|
const sendMenuAction = (sender: MessageSender, channelFlag: string) => {
|
||||||
let id = sender.tabId;
|
let id = sender.tabId;
|
||||||
|
@ -14,11 +14,8 @@
|
|||||||
padding: 0;
|
padding: 0;
|
||||||
border: 0;
|
border: 0;
|
||||||
width: 320px;
|
width: 320px;
|
||||||
/* height: 500px; */
|
|
||||||
min-height: 150px;
|
min-height: 150px;
|
||||||
max-height: 500px;
|
max-height: 500px;
|
||||||
/* overflow-y: auto; */
|
|
||||||
/* overflow: hidden; */
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
@ -1,41 +0,0 @@
|
|||||||
#root {
|
|
||||||
max-width: 1280px;
|
|
||||||
margin: 0 auto;
|
|
||||||
padding: 2rem;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.logo {
|
|
||||||
height: 6em;
|
|
||||||
padding: 1.5em;
|
|
||||||
will-change: filter;
|
|
||||||
}
|
|
||||||
.logo:hover {
|
|
||||||
filter: drop-shadow(0 0 2em #646cffaa);
|
|
||||||
}
|
|
||||||
.logo.react:hover {
|
|
||||||
filter: drop-shadow(0 0 2em #61dafbaa);
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes logo-spin {
|
|
||||||
from {
|
|
||||||
transform: rotate(0deg);
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
transform: rotate(360deg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (prefers-reduced-motion: no-preference) {
|
|
||||||
a > .logo {
|
|
||||||
animation: logo-spin infinite 20s linear;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.card {
|
|
||||||
padding: 2em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.read-the-docs {
|
|
||||||
color: #888;
|
|
||||||
}
|
|
@ -1,31 +1,221 @@
|
|||||||
import { useState } from "react";
|
import { ExtVersion } from "@App/app/const";
|
||||||
import reactLogo from "@App/assets/logo.png";
|
import { Alert, Badge, Button, Card, Collapse, Dropdown, Menu, Switch } from "@arco-design/web-react";
|
||||||
import "./App.css";
|
import {
|
||||||
|
IconBook,
|
||||||
|
IconBug,
|
||||||
|
IconGithub,
|
||||||
|
IconHome,
|
||||||
|
IconMoreVertical,
|
||||||
|
IconNotification,
|
||||||
|
IconPlus,
|
||||||
|
IconSearch,
|
||||||
|
} from "@arco-design/web-react/icon";
|
||||||
|
import React, { useEffect, useState } from "react";
|
||||||
|
import { RiMessage2Line } from "react-icons/ri";
|
||||||
|
import semver from "semver";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import ScriptMenuList from "../components/ScriptMenuList";
|
||||||
|
import { ScriptMenu } from "@App/runtime/service_worker/runtime";
|
||||||
|
|
||||||
|
const CollapseItem = Collapse.Item;
|
||||||
|
|
||||||
|
const iconStyle = {
|
||||||
|
marginRight: 8,
|
||||||
|
fontSize: 16,
|
||||||
|
transform: "translateY(1px)",
|
||||||
|
};
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
const [count, setCount] = useState(0);
|
const [scriptList, setScriptList] = useState<ScriptMenu[]>([]);
|
||||||
|
const [backScriptList, setBackScriptList] = useState<ScriptMenu[]>([]);
|
||||||
|
const [showAlert, setShowAlert] = useState(false);
|
||||||
|
const [notice, setNotice] = useState("");
|
||||||
|
const [isRead, setIsRead] = useState(true);
|
||||||
|
const [version, setVersion] = useState(ExtVersion);
|
||||||
|
const [currentUrl, setCurrentUrl] = useState("");
|
||||||
|
const [isEnableScript, setIsEnableScript] = useState(localStorage.enable_script !== "false");
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
return (
|
let url: URL | undefined;
|
||||||
<div className="App">
|
try {
|
||||||
<div>
|
url = new URL(currentUrl);
|
||||||
<a href="https://reactjs.org" target="_blank" rel="noreferrer">
|
} catch (e) {
|
||||||
<img src={reactLogo} className="logo react" alt="React logo" />
|
// ignore error
|
||||||
</a>
|
}
|
||||||
</div>
|
|
||||||
<h1>Rspack + React3 + TypeScript</h1>
|
// const message = IoC.instance(MessageInternal) as MessageInternal;
|
||||||
<div className="card">
|
// useEffect(() => {
|
||||||
<button type="button" onClick={() => setCount(count => count + 1)}>
|
// systemManage.getNotice().then((res) => {
|
||||||
count is {count}
|
// if (res) {
|
||||||
</button>
|
// setNotice(res.notice);
|
||||||
<p>
|
// setIsRead(res.isRead);
|
||||||
Edit <code>src/App.tsx</code> and save to test HMR
|
// }
|
||||||
</p>
|
// });
|
||||||
</div>
|
// systemManage.getVersion().then((res) => {
|
||||||
<p className="read-the-docs">
|
// res && setVersion(res);
|
||||||
Click on the Rspack and React logos to learn more
|
// });
|
||||||
</p>
|
// chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
|
||||||
</div>
|
// if (!tabs.length) {
|
||||||
);
|
// return;
|
||||||
|
// }
|
||||||
|
// setCurrentUrl(tabs[0].url || "");
|
||||||
|
// message
|
||||||
|
// .syncSend("queryPageScript", { url: tabs[0].url, tabId: tabs[0].id })
|
||||||
|
// .then((resp: { scriptList: ScriptMenu[]; backScriptList: ScriptMenu[] }) => {
|
||||||
|
// // 按照开启状态和更新时间排序
|
||||||
|
// const list = resp.scriptList;
|
||||||
|
// list.sort((a, b) => {
|
||||||
|
// if (a.enable === b.enable) {
|
||||||
|
// if (a.runNum !== b.runNum) {
|
||||||
|
// return b.runNum - a.runNum;
|
||||||
|
// }
|
||||||
|
// return b.updatetime - a.updatetime;
|
||||||
|
// }
|
||||||
|
// return a.enable ? -1 : 1;
|
||||||
|
// });
|
||||||
|
// setScriptList(list);
|
||||||
|
// setBackScriptList(resp.backScriptList);
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
// }, []);
|
||||||
|
return (
|
||||||
|
<Card
|
||||||
|
size="small"
|
||||||
|
title={
|
||||||
|
<div className="flex justify-between">
|
||||||
|
<span className="text-xl">ScriptCat</span>
|
||||||
|
<div className="flex flex-row items-center">
|
||||||
|
<Switch
|
||||||
|
size="small"
|
||||||
|
checked={isEnableScript}
|
||||||
|
onChange={(val) => {
|
||||||
|
setIsEnableScript(val);
|
||||||
|
if (val) {
|
||||||
|
localStorage.enable_script = "true";
|
||||||
|
} else {
|
||||||
|
localStorage.enable_script = "false";
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
type="text"
|
||||||
|
icon={<IconHome />}
|
||||||
|
iconOnly
|
||||||
|
onClick={() => {
|
||||||
|
// 用a链接的方式,vivaldi竟然会直接崩溃
|
||||||
|
window.open("/src/options.html", "_blank");
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Badge count={isRead ? 0 : 1} dot offset={[-8, 6]}>
|
||||||
|
<Button
|
||||||
|
type="text"
|
||||||
|
icon={<IconNotification />}
|
||||||
|
iconOnly
|
||||||
|
onClick={() => {
|
||||||
|
setShowAlert(!showAlert);
|
||||||
|
setIsRead(true);
|
||||||
|
systemManage.setRead(true);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Badge>
|
||||||
|
<Dropdown
|
||||||
|
droplist={
|
||||||
|
<Menu
|
||||||
|
style={{
|
||||||
|
maxHeight: "none",
|
||||||
|
}}
|
||||||
|
onClickMenuItem={async (key) => {
|
||||||
|
switch (key) {
|
||||||
|
case "newScript":
|
||||||
|
await chrome.storage.local.set({
|
||||||
|
activeTabUrl: {
|
||||||
|
url: currentUrl,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
window.open("/src/options.html#/script/editor?target=initial", "_blank");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
window.open(key, "_blank");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Menu.Item key="newScript">
|
||||||
|
<IconPlus style={iconStyle} />
|
||||||
|
{t("create_script")}
|
||||||
|
</Menu.Item>
|
||||||
|
<Menu.Item key={`https://scriptcat.org/search?domain=${url && url.host}`}>
|
||||||
|
<IconSearch style={iconStyle} />
|
||||||
|
{t("get_script")}
|
||||||
|
</Menu.Item>
|
||||||
|
<Menu.Item key="https://github.com/scriptscat/scriptcat/issues">
|
||||||
|
<IconBug style={iconStyle} />
|
||||||
|
{t("report_issue")}
|
||||||
|
</Menu.Item>
|
||||||
|
<Menu.Item key="https://docs.scriptcat.org/">
|
||||||
|
<IconBook style={iconStyle} />
|
||||||
|
{t("project_docs")}
|
||||||
|
</Menu.Item>
|
||||||
|
<Menu.Item key="https://bbs.tampermonkey.net.cn/">
|
||||||
|
<RiMessage2Line style={iconStyle} />
|
||||||
|
{t("community")}
|
||||||
|
</Menu.Item>
|
||||||
|
<Menu.Item key="https://github.com/scriptscat/scriptcat">
|
||||||
|
<IconGithub style={iconStyle} />
|
||||||
|
GitHub
|
||||||
|
</Menu.Item>
|
||||||
|
</Menu>
|
||||||
|
}
|
||||||
|
trigger="click"
|
||||||
|
>
|
||||||
|
<Button type="text" icon={<IconMoreVertical />} iconOnly />
|
||||||
|
</Dropdown>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
bodyStyle={{ padding: 0 }}
|
||||||
|
>
|
||||||
|
<Alert
|
||||||
|
style={{ marginBottom: 20, display: showAlert ? "flex" : "none" }}
|
||||||
|
type="info"
|
||||||
|
// eslint-disable-next-line react/no-danger
|
||||||
|
content={<div dangerouslySetInnerHTML={{ __html: notice }} />}
|
||||||
|
/>
|
||||||
|
<Collapse bordered={false} defaultActiveKey={["script", "background"]} style={{ maxWidth: 640 }}>
|
||||||
|
<CollapseItem
|
||||||
|
header={t("current_page_scripts")}
|
||||||
|
name="script"
|
||||||
|
style={{ padding: "0" }}
|
||||||
|
contentStyle={{ padding: "0" }}
|
||||||
|
>
|
||||||
|
<ScriptMenuList script={scriptList} isBackscript={false} currentUrl={currentUrl} />
|
||||||
|
</CollapseItem>
|
||||||
|
|
||||||
|
<CollapseItem
|
||||||
|
header={t("enabled_background_scripts")}
|
||||||
|
name="background"
|
||||||
|
style={{ padding: "0" }}
|
||||||
|
contentStyle={{ padding: "0" }}
|
||||||
|
>
|
||||||
|
<ScriptMenuList script={backScriptList} isBackscript currentUrl={currentUrl} />
|
||||||
|
</CollapseItem>
|
||||||
|
</Collapse>
|
||||||
|
<div className="flex flex-row arco-card-header !h-6">
|
||||||
|
<span className="text-[12px] font-500">{`v${ExtVersion}`}</span>
|
||||||
|
{semver.lt(ExtVersion, version) && (
|
||||||
|
<span
|
||||||
|
onClick={() => {
|
||||||
|
window.open(`https://github.com/scriptscat/scriptcat/releases/tag/v${version}`);
|
||||||
|
}}
|
||||||
|
className="text-1 font-500"
|
||||||
|
style={{ cursor: "pointer" }}
|
||||||
|
>
|
||||||
|
{t("popup.new_version_available")}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default App;
|
export default App;
|
||||||
|
@ -1,70 +1,19 @@
|
|||||||
:root {
|
.arco-collapse-item-header-title {
|
||||||
font-family: Inter, Avenir, Helvetica, Arial, sans-serif;
|
width: 100%;
|
||||||
font-size: 16px;
|
|
||||||
line-height: 24px;
|
|
||||||
font-weight: 400;
|
|
||||||
|
|
||||||
color-scheme: light dark;
|
|
||||||
color: rgba(255, 255, 255, 0.87);
|
|
||||||
background-color: #242424;
|
|
||||||
|
|
||||||
font-synthesis: none;
|
|
||||||
text-rendering: optimizeLegibility;
|
|
||||||
-webkit-font-smoothing: antialiased;
|
|
||||||
-moz-osx-font-smoothing: grayscale;
|
|
||||||
-webkit-text-size-adjust: 100%;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
a {
|
.arco-collapse-item-header-title .arco-space {
|
||||||
font-weight: 500;
|
width: 100%;
|
||||||
color: #646cff;
|
|
||||||
text-decoration: inherit;
|
|
||||||
}
|
|
||||||
a:hover {
|
|
||||||
color: #535bf2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
.arco-space-item:last-child {
|
||||||
margin: 0;
|
overflow: hidden;
|
||||||
display: flex;
|
|
||||||
place-items: center;
|
|
||||||
min-width: 320px;
|
|
||||||
min-height: 100vh;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
h1 {
|
.arco-collapse {
|
||||||
font-size: 3.2em;
|
border-bottom: 1px solid var(--color-neutral-3) !important;
|
||||||
line-height: 1.1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
button {
|
.arco-collapse-item {
|
||||||
border-radius: 8px;
|
border: 0;
|
||||||
border: 1px solid transparent;
|
|
||||||
padding: 0.6em 1.2em;
|
|
||||||
font-size: 1em;
|
|
||||||
font-weight: 500;
|
|
||||||
font-family: inherit;
|
|
||||||
background-color: #1a1a1a;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: border-color 0.25s;
|
|
||||||
}
|
|
||||||
button:hover {
|
|
||||||
border-color: #646cff;
|
|
||||||
}
|
|
||||||
button:focus,
|
|
||||||
button:focus-visible {
|
|
||||||
outline: 4px auto -webkit-focus-ring-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (prefers-color-scheme: light) {
|
|
||||||
:root {
|
|
||||||
color: #213547;
|
|
||||||
background-color: #ffffff;
|
|
||||||
}
|
|
||||||
a:hover {
|
|
||||||
color: #747bff;
|
|
||||||
}
|
|
||||||
button {
|
|
||||||
background-color: #f9f9f9;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,36 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import ReactDOM from "react-dom/client";
|
import ReactDOM from "react-dom/client";
|
||||||
import App from "./App.tsx";
|
import App from "./App.tsx";
|
||||||
import "./index.css";
|
import LoggerCore from "@App/app/logger/core.ts";
|
||||||
|
import migrate from "@App/app/migrate.ts";
|
||||||
|
import { LoggerDAO } from "@App/app/repo/logger.ts";
|
||||||
|
import DBWriter from "@App/app/logger/db_writer.ts";
|
||||||
|
import "@arco-design/web-react/dist/css/arco.css";
|
||||||
|
import "@App/locales/locales";
|
||||||
|
import "@App/index.css";
|
||||||
|
import { Provider } from "react-redux";
|
||||||
|
import { store } from "../store/store.ts";
|
||||||
|
|
||||||
|
// 初始化数据库
|
||||||
|
migrate();
|
||||||
|
// 初始化日志组件
|
||||||
|
const loggerCore = new LoggerCore({
|
||||||
|
writer: new DBWriter(new LoggerDAO()),
|
||||||
|
labels: { env: "install" },
|
||||||
|
});
|
||||||
|
|
||||||
|
loggerCore.logger().debug("page start");
|
||||||
|
|
||||||
ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
|
ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
|
||||||
<React.StrictMode>
|
<React.StrictMode>
|
||||||
<App />
|
<Provider store={store}>
|
||||||
</React.StrictMode>
|
<div
|
||||||
|
style={{
|
||||||
|
borderBottom: "1px solid var(--color-neutral-3)",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<App />
|
||||||
|
</div>
|
||||||
|
</Provider>
|
||||||
|
</React.StrictMode>
|
||||||
);
|
);
|
||||||
|
@ -11,20 +11,13 @@ export default class ContentRuntime {
|
|||||||
) {}
|
) {}
|
||||||
|
|
||||||
start(scripts: ScriptRunResouce[]) {
|
start(scripts: ScriptRunResouce[]) {
|
||||||
this.msg.onMessage((msg, sendResponse) => {
|
|
||||||
console.log("content onMessage", msg);
|
|
||||||
});
|
|
||||||
this.msg.onConnect((msg, connect) => {
|
|
||||||
console.log(msg, connect);
|
|
||||||
});
|
|
||||||
forwardMessage(
|
forwardMessage(
|
||||||
"serviceWorker",
|
"serviceWorker",
|
||||||
"runtime/gmApi",
|
"runtime/gmApi",
|
||||||
this.server,
|
this.server,
|
||||||
this.send,
|
this.send,
|
||||||
(data: { api: string; params: any }, con: GetSender) => {
|
(data: { api: string; params: any }, con: GetSender) => {
|
||||||
// 拦截关注的action
|
// 拦截关注的api
|
||||||
console.log("拦截", data);
|
|
||||||
switch (data.api) {
|
switch (data.api) {
|
||||||
case "CAT_createBlobUrl": {
|
case "CAT_createBlobUrl": {
|
||||||
const file = data.params[0] as File;
|
const file = data.params[0] as File;
|
||||||
|
@ -198,6 +198,53 @@ export default class GMApi {
|
|||||||
return (<CustomEventMessage>this.message).getAndDelRelatedTarget(data.relatedTarget);
|
return (<CustomEventMessage>this.message).getAndDelRelatedTarget(data.relatedTarget);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
menuId: number | undefined;
|
||||||
|
|
||||||
|
menuMap: Map<number, string> | undefined;
|
||||||
|
|
||||||
|
@GMContext.API()
|
||||||
|
GM_registerMenuCommand(name: string, listener: () => void, accessKey?: string): number {
|
||||||
|
if (!this.menuMap) {
|
||||||
|
this.menuMap = new Map();
|
||||||
|
}
|
||||||
|
let flag = 0;
|
||||||
|
this.menuMap.forEach((val, key) => {
|
||||||
|
if (val === name) {
|
||||||
|
flag = key;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (flag) {
|
||||||
|
return flag;
|
||||||
|
}
|
||||||
|
if (!this.menuId) {
|
||||||
|
this.menuId = 1;
|
||||||
|
} else {
|
||||||
|
this.menuId += 1;
|
||||||
|
}
|
||||||
|
const id = this.menuId;
|
||||||
|
this.connect("GM_registerMenuCommand", [id, name, accessKey]).then((con) => {
|
||||||
|
con.onMessage((data: { action: string; data: any }) => {
|
||||||
|
if (data.action === "onClick") {
|
||||||
|
listener();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
con.onDisconnect(() => {
|
||||||
|
this.menuMap?.delete(id);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
this.menuMap.set(id, name);
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@GMContext.API()
|
||||||
|
GM_unregisterMenuCommand(id: number): void {
|
||||||
|
if (!this.menuMap) {
|
||||||
|
this.menuMap = new Map();
|
||||||
|
}
|
||||||
|
this.menuMap.delete(id);
|
||||||
|
this.sendMessage("GM_unregisterMenuCommand", [id]);
|
||||||
|
}
|
||||||
|
|
||||||
// 用于脚本跨域请求,需要@connect domain指定允许的域名
|
// 用于脚本跨域请求,需要@connect domain指定允许的域名
|
||||||
@GMContext.API({
|
@GMContext.API({
|
||||||
depend: ["CAT_fetchBlob", "CAT_createBlobUrl", "CAT_fetchDocument"],
|
depend: ["CAT_fetchBlob", "CAT_createBlobUrl", "CAT_fetchDocument"],
|
||||||
@ -252,7 +299,6 @@ export default class GMApi {
|
|||||||
values.map(async (val) => {
|
values.map(async (val) => {
|
||||||
if (val instanceof File) {
|
if (val instanceof File) {
|
||||||
const url = await this.CAT_createBlobUrl(val);
|
const url = await this.CAT_createBlobUrl(val);
|
||||||
console.log(url);
|
|
||||||
data.push({
|
data.push({
|
||||||
key,
|
key,
|
||||||
type: "file",
|
type: "file",
|
||||||
|
Reference in New Issue
Block a user