Skip to content

Commit ce43279

Browse files
committed
🐛 修复 PR #1331 Copilot review 在 main 分支同样存在的 4 处问题
PR #1331 的 Copilot review 指出了 5 个问题,其中 4 个 main 分支也是相同 实现,应一并修复(剩余 1 个为 MV2 独有的漏分号): - src/app/service/content/utils.ts: addStyleSheet 增加特性检测, 无 Constructable Stylesheets / adoptedStyleSheets 时回退到 <style> 注入; 同步调整 utils.test.ts 中的类型断言 - src/pkg/utils/monaco-editor/index.ts: declare-global quickfix 标题改用 multiLang,补全 7 种语言的 declareGlobal key(之前硬编码中文,非中文 用户会看到混合语言) - src/pages/components/CodeEditor/index.tsx: diff editor 的两个 createModel 在 cleanup 里显式 dispose,避免反复挂载导致 standalone model 泄漏 - src/pages/options/routes/script/ScriptEditor.tsx: 跟踪 addAction/onKeyUp 返回的 IDisposable 并清理;用 ref 避免 stale closure;移除对 editor 实例的二次 dispose(CodeEditor 已自行管理生命周期)
1 parent 283bbef commit ce43279

5 files changed

Lines changed: 75 additions & 30 deletions

File tree

src/app/service/content/utils.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -458,7 +458,7 @@ describe("utils", () => {
458458
expect((sheet as any).cssText).toBe(css);
459459

460460
const adopted = (document as any).adoptedStyleSheets as CSSStyleSheet[];
461-
expect(adopted.includes(sheet)).toBe(true);
461+
expect(adopted.includes(sheet as CSSStyleSheet)).toBe(true);
462462
});
463463

464464
it("应该在已有样式表的基础上追加新的样式表", () => {
@@ -487,7 +487,7 @@ describe("utils", () => {
487487
expect((sheet as any).cssText).toBe("");
488488

489489
const adopted = (document as any).adoptedStyleSheets as CSSStyleSheet[];
490-
expect(adopted.includes(sheet)).toBe(true);
490+
expect(adopted.includes(sheet as CSSStyleSheet)).toBe(true);
491491
});
492492

493493
it("应该允许添加多个样式表", () => {

src/app/service/content/utils.ts

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -222,14 +222,26 @@ export function addStyle(css: string): HTMLStyleElement {
222222
return document.documentElement.appendChild(dom);
223223
}
224224

225-
export function addStyleSheet(css: string): CSSStyleSheet {
225+
export function addStyleSheet(css: string): CSSStyleSheet | HTMLStyleElement {
226226
// see https://unarist.hatenablog.com/entry/2020/07/06/012540
227-
const sheet = new CSSStyleSheet();
228-
// it might return as Promise
229-
sheet.replaceSync(css);
230-
// adoptedStyleSheets is FrozenArray so it has to be re-assigned.
231-
document.adoptedStyleSheets = document.adoptedStyleSheets.concat(sheet);
232-
return sheet;
227+
// 旧浏览器(如 Firefox 100 以下)不支持 Constructable Stylesheets / adoptedStyleSheets,回退到 <style> 注入
228+
if (
229+
typeof CSSStyleSheet === "function" &&
230+
typeof CSSStyleSheet.prototype?.replaceSync === "function" &&
231+
Array.isArray(document.adoptedStyleSheets)
232+
) {
233+
try {
234+
const sheet = new CSSStyleSheet();
235+
// it might return as Promise
236+
sheet.replaceSync(css);
237+
// adoptedStyleSheets is FrozenArray so it has to be re-assigned.
238+
document.adoptedStyleSheets = document.adoptedStyleSheets.concat(sheet);
239+
return sheet;
240+
} catch {
241+
// 个别环境下 new CSSStyleSheet() 仍可能抛出,落到 <style> fallback
242+
}
243+
}
244+
return addStyle(css);
233245
}
234246

235247
export function metadataBlankOrTrue(metadata: SCMetadata, key: string): boolean {

src/pages/components/CodeEditor/index.tsx

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,8 @@ const CodeEditor: React.ForwardRefRenderFunction<{ editor: editor.IStandaloneCod
135135
largeFileOptimizations: true,
136136
colorDecorators: true,
137137
} as const;
138+
let originalModel: editor.ITextModel | undefined;
139+
let modifiedModel: editor.ITextModel | undefined;
138140
if (diffCode) {
139141
edit = editor.createDiffEditor(inlineDiv, {
140142
hideUnchangedRegions: {
@@ -146,9 +148,12 @@ const CodeEditor: React.ForwardRefRenderFunction<{ editor: editor.IStandaloneCod
146148
diffWordWrap: "off",
147149
...commonEditorOptions,
148150
});
151+
// standalone model 不会随 editor.dispose 自动清理,需手动跟踪并在 cleanup 释放
152+
originalModel = editor.createModel(diffCode, "javascript");
153+
modifiedModel = editor.createModel(code, "javascript");
149154
edit.setModel({
150-
original: editor.createModel(diffCode, "javascript"),
151-
modified: editor.createModel(code, "javascript"),
155+
original: originalModel,
156+
modified: modifiedModel,
152157
});
153158
} else {
154159
edit = editor.create(inlineDiv, {
@@ -165,6 +170,8 @@ const CodeEditor: React.ForwardRefRenderFunction<{ editor: editor.IStandaloneCod
165170
// 目前会出现:Uncaught (in promise) Canceled: Canceled
166171
// 问题追踪:https://github.com/microsoft/monaco-editor/issues/4702
167172
edit?.dispose();
173+
originalModel?.dispose();
174+
modifiedModel?.dispose();
168175
};
169176
}, [div, code, diffCode, editable, id]);
170177

src/pages/options/routes/script/ScriptEditor.tsx

Lines changed: 37 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import type { Script } from "@App/app/repo/scripts";
22
import { SCRIPT_TYPE_NORMAL, ScriptCodeDAO, ScriptDAO } from "@App/app/repo/scripts";
33
import CodeEditor from "@App/pages/components/CodeEditor";
4-
import React, { useCallback, useEffect, useMemo, useState } from "react";
4+
import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
55
import { useParams, useSearchParams } from "react-router-dom";
6-
import type { editor } from "monaco-editor";
6+
import type { editor, IDisposable } from "monaco-editor";
77
import { KeyCode, KeyMod } from "monaco-editor";
88
import { Button, Dropdown, Grid, Input, Menu, Message, Modal, Space, Tabs, Tooltip } from "@arco-design/web-react";
99
import TabPane from "@arco-design/web-react/es/Tabs/tab-pane";
@@ -53,31 +53,50 @@ const Editor: React.FC<{
5353
},
5454
[node]
5555
);
56+
// 用 ref 拿到最新的 hotKeys/onChange/callbackEditor,避免 stale closure
57+
// 同时让 effect 仅在 editor 实例变化时重跑(不会因父组件重渲染重复 addAction)
58+
const hotKeysRef = useRef(hotKeys);
59+
const onChangeRef = useRef(onChange);
60+
const callbackEditorRef = useRef(callbackEditor);
61+
const scriptRef = useRef(script);
62+
hotKeysRef.current = hotKeys;
63+
onChangeRef.current = onChange;
64+
callbackEditorRef.current = callbackEditor;
65+
scriptRef.current = script;
66+
5667
useEffect(() => {
5768
if (!node || !node.editor) {
5869
return;
5970
}
6071
// @ts-ignore
6172
if (!node.editor.uuid) {
6273
// @ts-ignore
63-
node.editor.uuid = script.uuid;
74+
node.editor.uuid = scriptRef.current.uuid;
6475
}
65-
hotKeys.forEach((item) => {
66-
node.editor.addAction({
67-
id: item.id,
68-
label: item.title,
69-
keybindings: [item.hotKey],
70-
run(editor) {
71-
// @ts-ignore
72-
item.action(script, editor);
73-
},
74-
});
75-
});
76-
node.editor.onKeyUp(() => {
77-
onChange(node.editor.getValue() || "");
76+
const disposables: IDisposable[] = [];
77+
hotKeysRef.current.forEach((item) => {
78+
disposables.push(
79+
node.editor.addAction({
80+
id: item.id,
81+
label: item.title,
82+
keybindings: [item.hotKey],
83+
run(editor) {
84+
// @ts-ignore
85+
item.action(scriptRef.current, editor);
86+
},
87+
})
88+
);
7889
});
79-
callbackEditor(node.editor);
80-
return node.editor.dispose.bind(node.editor);
90+
disposables.push(
91+
node.editor.onKeyUp(() => {
92+
onChangeRef.current(node.editor.getValue() || "");
93+
})
94+
);
95+
callbackEditorRef.current(node.editor);
96+
// editor 实例本身由 CodeEditor 自身负责 dispose,这里仅清理本 effect 注册的 listener/action
97+
return () => {
98+
disposables.forEach((d) => d.dispose());
99+
};
81100
}, [node?.editor]);
82101

83102
return <CodeEditor key={id} id={id} ref={ref} className={className} code={code} diffCode="" editable />;

src/pkg/utils/monaco-editor/index.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ const langs = {
1515
quickfix: "修复 {0} 问题",
1616
addEslintDisableNextLine: "添加 eslint-disable-next-line 注释",
1717
addEslintDisable: "添加 eslint-disable 注释",
18+
declareGlobal: "将 '{0}' 声明为全局变量 (/* global */)",
1819
prompt: {
1920
name: "脚本名称",
2021
namespace: "脚本命名空间",
@@ -84,6 +85,7 @@ tracking:该脚本会追踪你的用户信息`.replace(/\n/g, "<br>"),
8485
quickfix: "Fix {0} Issue",
8586
addEslintDisableNextLine: "Add eslint-disable-next-line Comment",
8687
addEslintDisable: "Add eslint-disable Comment",
88+
declareGlobal: "Declare '{0}' as a global variable (/* global */)",
8789
prompt: {
8890
name: "Script name",
8991
namespace: "Script namespace",
@@ -146,6 +148,7 @@ tracking:该脚本会追踪你的用户信息`.replace(/\n/g, "<br>"),
146148
quickfix: "修復 {0} 問題",
147149
addEslintDisableNextLine: "新增 eslint-disable-next-line 註解",
148150
addEslintDisable: "新增 eslint-disable 註解",
151+
declareGlobal: "將 '{0}' 宣告為全域變數 (/* global */)",
149152
prompt: {
150153
name: "腳本名稱",
151154
namespace: "腳本命名空間",
@@ -208,6 +211,7 @@ tracking:该脚本会追踪你的用户信息`.replace(/\n/g, "<br>"),
208211
quickfix: "{0} の問題を修正",
209212
addEslintDisableNextLine: "eslint-disable-next-line コメントを追加",
210213
addEslintDisable: "eslint-disable コメントを追加",
214+
declareGlobal: "'{0}' をグローバル変数として宣言 (/* global */)",
211215
prompt: {
212216
name: "スクリプト名",
213217
namespace: "スクリプトの名前空間",
@@ -270,6 +274,7 @@ tracking:该脚本会追踪你的用户信息`.replace(/\n/g, "<br>"),
270274
quickfix: "{0}-Problem beheben",
271275
addEslintDisableNextLine: "eslint-disable-next-line Kommentar hinzufügen",
272276
addEslintDisable: "eslint-disable Kommentar hinzufügen",
277+
declareGlobal: "'{0}' als globale Variable deklarieren (/* global */)",
273278
prompt: {
274279
name: "Skriptname",
275280
namespace: "Skript-Namensraum",
@@ -332,6 +337,7 @@ tracking:该脚本会追踪你的用户信息`.replace(/\n/g, "<br>"),
332337
quickfix: "Sửa lỗi {0}",
333338
addEslintDisableNextLine: "Thêm chú thích eslint-disable-next-line",
334339
addEslintDisable: "Thêm chú thích eslint-disable",
340+
declareGlobal: "Khai báo '{0}' là biến toàn cục (/* global */)",
335341
prompt: {
336342
name: "Tên script",
337343
namespace: "Namespace của script",
@@ -394,6 +400,7 @@ tracking:该脚本会追踪你的用户信息`.replace(/\n/g, "<br>"),
394400
quickfix: "Исправить проблему {0}",
395401
addEslintDisableNextLine: "Добавить комментарий eslint-disable-next-line",
396402
addEslintDisable: "Добавить комментарий eslint-disable",
403+
declareGlobal: "Объявить '{0}' как глобальную переменную (/* global */)",
397404
prompt: {
398405
name: "Имя скрипта",
399406
namespace: "Пространство имён скрипта",
@@ -587,7 +594,7 @@ export default function registerEditor() {
587594
}
588595

589596
actions.push(<languages.CodeAction>{
590-
title: `将 '${globalName}' 声明为全局变量 (/* global */)`,
597+
title: multiLang.declareGlobal.replace("{0}", globalName),
591598
diagnostics: [val],
592599
kind: "quickfix",
593600
edit: {

0 commit comments

Comments
 (0)