Skip to content

Commit 32badfe

Browse files
committed
🐛 修复 PR #1331 Copilot review 提出的 5 处问题
- src/runtime/content/utils.ts: 补回漏掉的分号 - src/runtime/content/utils.ts: addStyleSheet 增加特性检测,旧浏览器 (无 Constructable Stylesheets / adoptedStyleSheets)回退到 <style> 注入 - src/pkg/utils/monaco-editor: declare-global quickfix 标题改用 multiLang, 补全 7 种语言的 declareGlobal key(之前硬编码中文) - src/pages/components/CodeEditor: diff editor 的两个 createModel 在 cleanup 里显式 dispose,避免反复挂载导致 standalone model 泄漏 - src/pages/options/routes/script/ScriptEditor: 跟踪 addAction/onKeyUp 返回的 IDisposable 并清理;使用 ref 避免 stale closure;移除对 editor 实例的 二次 dispose(CodeEditor 已自行管理生命周期)
1 parent 731cf65 commit 32badfe

5 files changed

Lines changed: 74 additions & 28 deletions

File tree

src/pages/components/CodeEditor/index.tsx

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,8 @@ const CodeEditor: React.ForwardRefRenderFunction<
138138
largeFileOptimizations: true,
139139
colorDecorators: true,
140140
} as const;
141+
let originalModel: editor.ITextModel | undefined;
142+
let modifiedModel: editor.ITextModel | undefined;
141143
if (diffCode) {
142144
edit = editor.createDiffEditor(inlineDiv, {
143145
hideUnchangedRegions: {
@@ -149,9 +151,12 @@ const CodeEditor: React.ForwardRefRenderFunction<
149151
diffWordWrap: "off",
150152
...commonEditorOptions,
151153
});
154+
// standalone model 不会随 editor.dispose 自动清理,需手动跟踪并在 cleanup 释放
155+
originalModel = editor.createModel(diffCode, "javascript");
156+
modifiedModel = editor.createModel(code, "javascript");
152157
edit.setModel({
153-
original: editor.createModel(diffCode, "javascript"),
154-
modified: editor.createModel(code, "javascript"),
158+
original: originalModel,
159+
modified: modifiedModel,
155160
});
156161
} else {
157162
edit = editor.create(inlineDiv, {
@@ -168,6 +173,8 @@ const CodeEditor: React.ForwardRefRenderFunction<
168173
// 目前会出现:Uncaught (in promise) Canceled: Canceled
169174
// 问题追踪:https://github.com/microsoft/monaco-editor/issues/4702
170175
edit?.dispose();
176+
originalModel?.dispose();
177+
modifiedModel?.dispose();
171178
};
172179
}, [code, diffCode]);
173180

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

Lines changed: 37 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
/* eslint-disable no-shadow */
22
import { Script, ScriptDAO } from "@App/app/repo/scripts";
33
import CodeEditor from "@App/pages/components/CodeEditor";
4-
import React, { useCallback, useEffect, useState } from "react";
4+
import React, { useCallback, useEffect, useRef, useState } from "react";
5+
import type { IDisposable } from "monaco-editor";
56
import { useNavigate, useParams, useSearchParams } from "react-router-dom";
67
import { editor, KeyCode, KeyMod } from "monaco-editor";
78
import {
@@ -59,31 +60,50 @@ const Editor: React.FC<{
5960
[node]
6061
);
6162

63+
// 用 ref 拿到最新的 hotKeys/onChange/callbackEditor,避免 stale closure
64+
// 同时让 effect 仅在 editor 实例变化时重跑(不会因父组件重渲染重复 addAction)
65+
const hotKeysRef = useRef(hotKeys);
66+
const onChangeRef = useRef(onChange);
67+
const callbackEditorRef = useRef(callbackEditor);
68+
const scriptRef = useRef(script);
69+
hotKeysRef.current = hotKeys;
70+
onChangeRef.current = onChange;
71+
callbackEditorRef.current = callbackEditor;
72+
scriptRef.current = script;
73+
6274
useEffect(() => {
6375
if (!node || !node.editor) {
6476
return;
6577
}
6678
// @ts-ignore
6779
if (!node.editor.uuid) {
6880
// @ts-ignore
69-
node.editor.uuid = script.uuid;
81+
node.editor.uuid = scriptRef.current.uuid;
7082
}
71-
hotKeys.forEach((item) => {
72-
node.editor.addAction({
73-
id: item.id,
74-
label: item.title,
75-
keybindings: [item.hotKey],
76-
run(editor) {
77-
// @ts-ignore
78-
item.action(script, editor);
79-
},
80-
});
81-
});
82-
node.editor.onKeyUp(() => {
83-
onChange(node.editor.getValue() || "");
83+
const disposables: IDisposable[] = [];
84+
hotKeysRef.current.forEach((item) => {
85+
disposables.push(
86+
node.editor.addAction({
87+
id: item.id,
88+
label: item.title,
89+
keybindings: [item.hotKey],
90+
run(editor) {
91+
// @ts-ignore
92+
item.action(scriptRef.current, editor);
93+
},
94+
})
95+
);
8496
});
85-
callbackEditor(node.editor);
86-
return node.editor.dispose.bind(node.editor);
97+
disposables.push(
98+
node.editor.onKeyUp(() => {
99+
onChangeRef.current(node.editor.getValue() || "");
100+
})
101+
);
102+
callbackEditorRef.current(node.editor);
103+
// editor 实例本身由 CodeEditor 自身负责 dispose,这里仅清理本 effect 注册的 listener/action
104+
return () => {
105+
disposables.forEach((d) => d.dispose());
106+
};
87107
}, [node?.editor]);
88108

89109
return (

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ export function registerEditor() {
195195
}
196196

197197
actions.push({
198-
title: `将 '${globalName}' 声明为全局变量 (/* global */)`,
198+
title: multiLang.declareGlobal.replace("{0}", globalName),
199199
diagnostics: [val],
200200
kind: "quickfix",
201201
edit: { edits: [{ resource: model.uri, textEdit, versionId: undefined }] },

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ export const editorLangs = {
66
quickfix: "修复 {0} 问题",
77
addEslintDisableNextLine: "添加 eslint-disable-next-line 注释",
88
addEslintDisable: "添加 eslint-disable 注释",
9+
declareGlobal: "将 '{0}' 声明为全局变量 (/* global */)",
910
prompt: {
1011
name: "脚本名称",
1112
namespace: "脚本命名空间",
@@ -81,6 +82,7 @@ tracking:该脚本会追踪你的用户信息`.replace(/\n/g, "<br>"),
8182
quickfix: "Fix {0} Issue",
8283
addEslintDisableNextLine: "Add eslint-disable-next-line Comment",
8384
addEslintDisable: "Add eslint-disable Comment",
85+
declareGlobal: "Declare '{0}' as a global variable (/* global */)",
8486
prompt: {
8587
name: "Script name",
8688
namespace: "Script namespace",
@@ -149,6 +151,7 @@ tracking:该脚本会追踪你的用户信息`.replace(/\n/g, "<br>"),
149151
quickfix: "修復 {0} 問題",
150152
addEslintDisableNextLine: "新增 eslint-disable-next-line 註解",
151153
addEslintDisable: "新增 eslint-disable 註解",
154+
declareGlobal: "將 '{0}' 宣告為全域變數 (/* global */)",
152155
prompt: {
153156
name: "腳本名稱",
154157
namespace: "腳本命名空間",
@@ -217,6 +220,7 @@ tracking:该脚本会追踪你的用户信息`.replace(/\n/g, "<br>"),
217220
quickfix: "{0} の問題を修正",
218221
addEslintDisableNextLine: "eslint-disable-next-line コメントを追加",
219222
addEslintDisable: "eslint-disable コメントを追加",
223+
declareGlobal: "'{0}' をグローバル変数として宣言 (/* global */)",
220224
prompt: {
221225
name: "スクリプト名",
222226
namespace: "スクリプトの名前空間",
@@ -285,6 +289,7 @@ tracking:该脚本会追踪你的用户信息`.replace(/\n/g, "<br>"),
285289
quickfix: "{0}-Problem beheben",
286290
addEslintDisableNextLine: "eslint-disable-next-line Kommentar hinzufügen",
287291
addEslintDisable: "eslint-disable Kommentar hinzufügen",
292+
declareGlobal: "'{0}' als globale Variable deklarieren (/* global */)",
288293
prompt: {
289294
name: "Skriptname",
290295
namespace: "Skript-Namensraum",
@@ -353,6 +358,7 @@ tracking:该脚本会追踪你的用户信息`.replace(/\n/g, "<br>"),
353358
quickfix: "Sửa lỗi {0}",
354359
addEslintDisableNextLine: "Thêm chú thích eslint-disable-next-line",
355360
addEslintDisable: "Thêm chú thích eslint-disable",
361+
declareGlobal: "Khai báo '{0}' là biến toàn cục (/* global */)",
356362
prompt: {
357363
name: "Tên script",
358364
namespace: "Namespace của script",
@@ -421,6 +427,7 @@ tracking:该脚本会追踪你的用户信息`.replace(/\n/g, "<br>"),
421427
quickfix: "Исправить проблему {0}",
422428
addEslintDisableNextLine: "Добавить комментарий eslint-disable-next-line",
423429
addEslintDisable: "Добавить комментарий eslint-disable",
430+
declareGlobal: "Объявить '{0}' как глобальную переменную (/* global */)",
424431
prompt: {
425432
name: "Имя скрипта",
426433
namespace: "Пространство имён скрипта",

src/runtime/content/utils.ts

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { type ScriptRunResource } from "@App/app/repo/scripts";
22
import { has } from "@App/pkg/utils/lodash";
3-
import { sourceMapTo } from "@App/pkg/utils/utils"
3+
import { sourceMapTo } from "@App/pkg/utils/utils";
44

55
// 构建脚本运行代码
66
export function compileScriptCode(scriptRes: ScriptRunResource): string {
@@ -293,12 +293,24 @@ export function addStyle(css: string): HTMLStyleElement {
293293
return document.documentElement.appendChild(dom);
294294
}
295295

296-
export function addStyleSheet(css: string): CSSStyleSheet {
296+
export function addStyleSheet(css: string): CSSStyleSheet | HTMLStyleElement {
297297
// see https://unarist.hatenablog.com/entry/2020/07/06/012540
298-
const sheet = new CSSStyleSheet();
299-
// it might return as Promise
300-
sheet.replaceSync(css);
301-
// adoptedStyleSheets is FrozenArray so it has to be re-assigned.
302-
document.adoptedStyleSheets = document.adoptedStyleSheets.concat(sheet);
303-
return sheet;
298+
// 旧浏览器(如 Firefox 100 以下)不支持 Constructable Stylesheets / adoptedStyleSheets,回退到 <style> 注入
299+
if (
300+
typeof CSSStyleSheet === "function" &&
301+
"replaceSync" in CSSStyleSheet.prototype &&
302+
"adoptedStyleSheets" in Document.prototype
303+
) {
304+
try {
305+
const sheet = new CSSStyleSheet();
306+
// it might return as Promise
307+
sheet.replaceSync(css);
308+
// adoptedStyleSheets is FrozenArray so it has to be re-assigned.
309+
document.adoptedStyleSheets = document.adoptedStyleSheets.concat(sheet);
310+
return sheet;
311+
} catch {
312+
// 个别环境下 new CSSStyleSheet() 仍可能抛出,落到 <style> fallback
313+
}
314+
}
315+
return addStyle(css);
304316
}

0 commit comments

Comments
 (0)