开启开发者模式引导

This commit is contained in:
王一之 2025-04-28 15:20:26 +08:00
parent a26f1c5014
commit 51fe2a89e1
7 changed files with 191 additions and 158 deletions

View File

@ -15,7 +15,7 @@ import { subscribeScriptDelete, subscribeScriptEnable, subscribeScriptInstall }
import { ScriptService } from "./script"; import { ScriptService } from "./script";
import { runScript, stopScript } from "../offscreen/client"; import { runScript, stopScript } from "../offscreen/client";
import { getRunAt } from "./utils"; import { getRunAt } from "./utils";
import { isUserScriptsAvailable, randomString } from "@App/pkg/utils/utils"; import { InfoNotification, isUserScriptsAvailable, randomString } from "@App/pkg/utils/utils";
import Cache from "@App/app/cache"; import Cache from "@App/app/cache";
import { dealPatternMatches, UrlMatch } from "@App/pkg/utils/match"; import { dealPatternMatches, UrlMatch } from "@App/pkg/utils/match";
import { ExtensionContentMessageSend } from "@Packages/message/extension_message"; import { ExtensionContentMessageSend } from "@Packages/message/extension_message";
@ -27,6 +27,8 @@ import LoggerCore from "@App/app/logger/core";
import PermissionVerify from "./permission_verify"; import PermissionVerify from "./permission_verify";
import { SystemConfig } from "@App/pkg/config/config"; import { SystemConfig } from "@App/pkg/config/config";
import { ResourceService } from "./resource"; import { ResourceService } from "./resource";
import { LocalStorageDAO } from "@App/app/repo/localStorage";
import i18n from "@App/locales/locales";
// 为了优化性能存储到缓存时删除了code、value与resource // 为了优化性能存储到缓存时删除了code、value与resource
export interface ScriptMatchInfo extends ScriptRunResouce { export interface ScriptMatchInfo extends ScriptRunResouce {
@ -49,6 +51,8 @@ export class RuntimeService {
scriptCustomizeMatch: UrlMatch<string> = new UrlMatch<string>(); scriptCustomizeMatch: UrlMatch<string> = new UrlMatch<string>();
scriptMatchCache: Map<string, ScriptMatchInfo> | null | undefined; scriptMatchCache: Map<string, ScriptMatchInfo> | null | undefined;
isEnableDeveloperMode = false;
constructor( constructor(
private systemConfig: SystemConfig, private systemConfig: SystemConfig,
private group: Group, private group: Group,
@ -71,9 +75,32 @@ export class RuntimeService {
this.group.on("pageLoad", this.pageLoad.bind(this)); this.group.on("pageLoad", this.pageLoad.bind(this));
// 检查是否开启了开发者模式 // 检查是否开启了开发者模式
if(!isUserScriptsAvailable()){ this.isEnableDeveloperMode = isUserScriptsAvailable();
// 未开启加上警告引导 if (!this.isEnableDeveloperMode) {
// 未开启加上警告引导
// 判断是否首次
const localStorage = new LocalStorageDAO();
localStorage.get("firstShowDeveloperMode").then((res) => {
if (!res) {
localStorage.save({
key: "firstShowDeveloperMode",
value: true,
});
// 打开页面
chrome.tabs.create({
url: `https://docs.scriptcat.org/docs/use/open-dev/`,
});
}
});
chrome.action.setBadgeBackgroundColor({
color: "#ff8c00",
});
chrome.action.setBadgeTextColor({
color: "#ffffff",
});
chrome.action.setBadgeText({
text: "!",
});
} }
// 读取inject.js注入页面 // 读取inject.js注入页面
this.registerInjectScript(); this.registerInjectScript();
@ -439,7 +466,7 @@ export class RuntimeService {
this.addScriptMatch(scriptMatchInfo); this.addScriptMatch(scriptMatchInfo);
// 如果脚本开启, 则注册脚本 // 如果脚本开启, 则注册脚本
if (script.status === SCRIPT_STATUS_ENABLE) { if (this.isEnableDeveloperMode && script.status === SCRIPT_STATUS_ENABLE) {
if (!scriptRes.metadata["noframes"]) { if (!scriptRes.metadata["noframes"]) {
registerScript.allFrames = true; registerScript.allFrames = true;
} }
@ -467,7 +494,7 @@ export class RuntimeService {
} }
async unregistryPageScript(uuid: string) { async unregistryPageScript(uuid: string) {
if (!(await Cache.getInstance().get("registryScript:" + uuid))) { if (!this.isEnableDeveloperMode || !(await Cache.getInstance().get("registryScript:" + uuid))) {
return; return;
} }
chrome.userScripts.unregister( chrome.userScripts.unregister(

View File

@ -21,7 +21,7 @@ import { ResourceService } from "./resource";
import { ValueService } from "./value"; import { ValueService } from "./value";
import { compileScriptCode } from "../content/utils"; import { compileScriptCode } from "../content/utils";
import { SystemConfig } from "@App/pkg/config/config"; import { SystemConfig } from "@App/pkg/config/config";
import i18n from "@App/locales/locales"; import i18n, { localePath } from "@App/locales/locales";
export class ScriptService { export class ScriptService {
logger: Logger; logger: Logger;
@ -109,10 +109,6 @@ export class ScriptService {
} }
); );
// 获取i18n // 获取i18n
let localePath = "";
if (i18n.language !== "zh-CN") {
localePath = `/en`;
}
// 重定向到脚本安装页 // 重定向到脚本安装页
chrome.declarativeNetRequest.updateDynamicRules( chrome.declarativeNetRequest.updateDynamicRules(
{ {

View File

@ -27,10 +27,15 @@ i18n.use(initReactI18next).init({
}, },
}); });
export let localePath = "";
chrome.i18n.getAcceptLanguages((lngs) => { chrome.i18n.getAcceptLanguages((lngs) => {
systemConfig.getLanguage().then((lng) => { systemConfig.getLanguage(lngs).then((lng) => {
i18n.changeLanguage(lng); i18n.changeLanguage(lng);
dayjs.locale(lng.toLocaleLowerCase()); dayjs.locale(lng.toLocaleLowerCase());
if (lng !== "zh-CN") {
localePath = "en";
}
}); });
}); });

View File

@ -368,5 +368,6 @@
"eslint_config_format_error": "eslint配置格式错误", "eslint_config_format_error": "eslint配置格式错误",
"export_success": "导出成功", "export_success": "导出成功",
"get_backup_dir_url_failed": "获取备份目录地址失败", "get_backup_dir_url_failed": "获取备份目录地址失败",
"get_backup_files_failed": "获取备份文件失败" "get_backup_files_failed": "获取备份文件失败",
"develop_mode_guide": "检测到当前未开启开发者模式,您的脚本无法正常使用,<a href=\"https://docs.scriptcat.org/docs/use/open-dev/\" target=\"black\" style=\"color: var(--color-text-1)\">👉点我了解如何开启</a>"
} }

View File

@ -18,7 +18,7 @@ import ScriptMenuList from "../components/ScriptMenuList";
import { popupClient } from "../store/features/script"; import { popupClient } from "../store/features/script";
import { ScriptMenu } from "@App/app/service/service_worker/popup"; import { ScriptMenu } from "@App/app/service/service_worker/popup";
import { systemConfig } from "../store/global"; import { systemConfig } from "../store/global";
import { SystemConfig } from "@App/pkg/config/config"; import { isUserScriptsAvailable } from "@App/pkg/utils/utils";
const CollapseItem = Collapse.Item; const CollapseItem = Collapse.Item;
@ -79,143 +79,147 @@ function App() {
}); });
}, []); }, []);
return ( return (
<Card <>
size="small" {!isUserScriptsAvailable() && (
title={ <Alert type="warning" content={<div dangerouslySetInnerHTML={{ __html: t("develop_mode_guide") }} />} />
<div className="flex justify-between"> )}
<span className="text-xl">ScriptCat</span> <Card
<div className="flex flex-row items-center"> size="small"
<Switch title={
size="small" <div className="flex justify-between">
checked={isEnableScript} <span className="text-xl">ScriptCat</span>
onChange={(val) => { <div className="flex flex-row items-center">
setIsEnableScript(val); <Switch
if (val) { size="small"
localStorage.enable_script = "true"; checked={isEnableScript}
} else { onChange={(val) => {
localStorage.enable_script = "false"; setIsEnableScript(val);
} if (val) {
}} localStorage.enable_script = "true";
/> } else {
<Button localStorage.enable_script = "false";
type="text" }
icon={<IconHome />}
iconOnly
onClick={() => {
// 用a链接的方式,vivaldi竟然会直接崩溃
window.open("/src/options.html", "_blank");
}}
/>
<Badge count={checkUpdate.isRead ? 0 : 1} dot offset={[-8, 6]}>
<Button
type="text"
icon={<IconNotification />}
iconOnly
onClick={() => {
setShowAlert(!showAlert);
console.log(checkUpdate);
checkUpdate.isRead = true;
setCheckUpdate(checkUpdate);
systemConfig.setCheckUpdate(checkUpdate);
}} }}
/> />
</Badge> <Button
<Dropdown type="text"
droplist={ icon={<IconHome />}
<Menu iconOnly
style={{ onClick={() => {
maxHeight: "none", // 用a链接的方式,vivaldi竟然会直接崩溃
window.open("/src/options.html", "_blank");
}}
/>
<Badge count={checkUpdate.isRead ? 0 : 1} dot offset={[-8, 6]}>
<Button
type="text"
icon={<IconNotification />}
iconOnly
onClick={() => {
setShowAlert(!showAlert);
checkUpdate.isRead = true;
setCheckUpdate(checkUpdate);
systemConfig.setCheckUpdate(checkUpdate);
}} }}
onClickMenuItem={async (key) => { />
switch (key) { </Badge>
case "newScript": <Dropdown
await chrome.storage.local.set({ droplist={
activeTabUrl: { <Menu
url: currentUrl, style={{
}, maxHeight: "none",
}); }}
window.open("/src/options.html#/script/editor?target=initial", "_blank"); onClickMenuItem={async (key) => {
break; switch (key) {
default: case "newScript":
window.open(key, "_blank"); await chrome.storage.local.set({
break; activeTabUrl: {
} url: currentUrl,
}} },
> });
<Menu.Item key="newScript"> window.open("/src/options.html#/script/editor?target=initial", "_blank");
<IconPlus style={iconStyle} /> break;
{t("create_script")} default:
</Menu.Item> window.open(key, "_blank");
<Menu.Item key={`https://scriptcat.org/search?domain=${url && url.host}`}> break;
<IconSearch style={iconStyle} /> }
{t("get_script")} }}
</Menu.Item> >
<Menu.Item key="https://github.com/scriptscat/scriptcat/issues"> <Menu.Item key="newScript">
<IconBug style={iconStyle} /> <IconPlus style={iconStyle} />
{t("report_issue")} {t("create_script")}
</Menu.Item> </Menu.Item>
<Menu.Item key="https://docs.scriptcat.org/"> <Menu.Item key={`https://scriptcat.org/search?domain=${url && url.host}`}>
<IconBook style={iconStyle} /> <IconSearch style={iconStyle} />
{t("project_docs")} {t("get_script")}
</Menu.Item> </Menu.Item>
<Menu.Item key="https://bbs.tampermonkey.net.cn/"> <Menu.Item key="https://github.com/scriptscat/scriptcat/issues">
<RiMessage2Line style={iconStyle} /> <IconBug style={iconStyle} />
{t("community")} {t("report_issue")}
</Menu.Item> </Menu.Item>
<Menu.Item key="https://github.com/scriptscat/scriptcat"> <Menu.Item key="https://docs.scriptcat.org/">
<IconGithub style={iconStyle} /> <IconBook style={iconStyle} />
GitHub {t("project_docs")}
</Menu.Item> </Menu.Item>
</Menu> <Menu.Item key="https://bbs.tampermonkey.net.cn/">
} <RiMessage2Line style={iconStyle} />
trigger="click" {t("community")}
> </Menu.Item>
<Button type="text" icon={<IconMoreVertical />} iconOnly /> <Menu.Item key="https://github.com/scriptscat/scriptcat">
</Dropdown> <IconGithub style={iconStyle} />
GitHub
</Menu.Item>
</Menu>
}
trigger="click"
>
<Button type="text" icon={<IconMoreVertical />} iconOnly />
</Dropdown>
</div>
</div> </div>
</div> }
} bodyStyle={{ padding: 0 }}
bodyStyle={{ padding: 0 }} >
> <Alert
<Alert style={{ display: showAlert ? "flex" : "none" }}
style={{ display: showAlert ? "flex" : "none" }} type="info"
type="info" content={<div dangerouslySetInnerHTML={{ __html: checkUpdate.notice || "" }} />}
content={<div dangerouslySetInnerHTML={{ __html: checkUpdate.notice || "" }} />} />
/> <Collapse bordered={false} defaultActiveKey={["script", "background"]} style={{ maxWidth: 640 }}>
<Collapse bordered={false} defaultActiveKey={["script", "background"]} style={{ maxWidth: 640 }}> <CollapseItem
<CollapseItem header={t("current_page_scripts")}
header={t("current_page_scripts")} name="script"
name="script" style={{ padding: "0" }}
style={{ padding: "0" }} contentStyle={{ 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, checkUpdate.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")} <ScriptMenuList script={scriptList} isBackscript={false} currentUrl={currentUrl} />
</span> </CollapseItem>
)}
</div> <CollapseItem
</Card> 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, checkUpdate.version) && (
<span
onClick={() => {
window.open(`https://github.com/scriptscat/scriptcat/releases/tag/v${checkUpdate.version}`);
}}
className="text-1 font-500"
style={{ cursor: "pointer" }}
>
{t("popup.new_version_available")}
</span>
)}
</div>
</Card>
</>
); );
} }

View File

@ -225,18 +225,19 @@ export class SystemConfig {
this.set("menu_expand_num", val); this.set("menu_expand_num", val);
} }
async getLanguage() { async getLanguage(acceptLanguages?: string[]): Promise<string> {
const defaultLanguage = await new Promise<string>((resolve) => { const defaultLanguage = await new Promise<string>(async (resolve) => {
chrome.i18n.getAcceptLanguages((lngs) => { if (!acceptLanguages) {
// 遍历数组寻找匹配语言 acceptLanguages = await chrome.i18n.getAcceptLanguages();
for (let i = 0; i < lngs.length; i += 1) { }
const lng = lngs[i]; // 遍历数组寻找匹配语言
if (i18n.hasResourceBundle(lng, "translation")) { for (let i = 0; i < acceptLanguages.length; i += 1) {
resolve(lng); const lng = acceptLanguages[i];
break; if (i18n.hasResourceBundle(lng, "translation")) {
} resolve(lng);
break;
} }
}); }
}); });
return this.get("language", defaultLanguage || chrome.i18n.getUILanguage()); return this.get("language", defaultLanguage || chrome.i18n.getUILanguage());
} }

View File

@ -266,7 +266,6 @@ export function errorMsg(e: any): string {
} }
export function isUserScriptsAvailable() { export function isUserScriptsAvailable() {
return false;
try { try {
// Property access which throws if developer mode is not enabled. // Property access which throws if developer mode is not enabled.
chrome.userScripts; chrome.userScripts;