From f3e6ecdf4bd28b97543a7accceafb61faad2fb64 Mon Sep 17 00:00:00 2001
From: cyfung1031 <44498510+cyfung1031@users.noreply.github.com>
Date: Sun, 10 May 2026 13:54:16 +0900
Subject: [PATCH 1/5] =?UTF-8?q?fix(UI):=20=E9=81=BF=E5=85=8D=E9=A1=B5?=
=?UTF-8?q?=E9=9D=A2=E4=BA=92=E5=8A=A8=E8=A7=A6=E5=8F=91=20body=20?=
=?UTF-8?q?=E7=9A=84=20swipe=20=E8=B7=B3=E9=A1=B5?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/index.css | 20 +-
src/pages/components/layout/MainLayout.tsx | 371 +++++++++++----------
src/pages/utility/InteractiveContainer.tsx | 57 ++++
3 files changed, 260 insertions(+), 188 deletions(-)
create mode 100644 src/pages/utility/InteractiveContainer.tsx
diff --git a/src/index.css b/src/index.css
index 6eb4944b4..dca7dbe5f 100644
--- a/src/index.css
+++ b/src/index.css
@@ -2,22 +2,34 @@
@unocss default;
@unocss;
+* {
+ scrollbar-color: inherit;
+}
+
body {
scrollbar-color: var(--color-scrollbar-thumb) var(--color-scrollbar-track);
- /* 对于webkit浏览器的滚动条样式 */
- scrollbar-width: thin;
}
body[arco-theme='dark'] {
--color-scrollbar-thumb: #6b6b6b;
--color-scrollbar-track: #2d2d2d;
- --color-scrollbar-thumb-hover: #8c8c8c;
}
body[arco-theme='light'] {
--color-scrollbar-thumb: #6b6b6b;
--color-scrollbar-track: #f0f0f0;
- --color-scrollbar-thumb-hover: #8c8c8c;
+}
+
+.interactive-container {
+ overscroll-behavior: none;
+ height: 100vh;
+ width: 100vw;
+ position: relative;
+ contain: strict;
+ padding: 0px;
+ margin: 0px;
+ box-sizing: border-box;
+ overflow: auto;
}
:root {
diff --git a/src/pages/components/layout/MainLayout.tsx b/src/pages/components/layout/MainLayout.tsx
index e7a6b6480..db20d4c76 100644
--- a/src/pages/components/layout/MainLayout.tsx
+++ b/src/pages/components/layout/MainLayout.tsx
@@ -36,6 +36,7 @@ import { arcoLocale } from "@App/locales/arco";
import { prepareScriptByCode } from "@App/pkg/utils/script";
import { saveHandle } from "@App/pkg/utils/filehandle-db";
import { makeBlobURL } from "@App/pkg/utils/utils";
+import InteractiveContainer from "@App/pages/utility/InteractiveContainer";
// --- 工具函数移出组件外,避免每次 Render 重新定义 ---
@@ -297,215 +298,217 @@ const MainLayout: React.FC<{
};
return (
- {
- return ;
- }}
- locale={arcoLocale(i18n.language)}
- componentConfig={{
- Select: {
- getPopupContainer: (node) => {
- return getSafePopupParent(node as Element);
+
+ {
+ return ;
+ }}
+ locale={arcoLocale(i18n.language)}
+ componentConfig={{
+ Select: {
+ getPopupContainer: (node) => {
+ return getSafePopupParent(node as Element);
+ },
},
- },
- }}
- getPopupContainer={(node) => {
- return getSafePopupParent(node.parentNode as Element);
- }}
- >
- {contextHolder}
-
-
- {
- setImportVisible(false);
+ }}
+ getPopupContainer={(node) => {
+ return getSafePopupParent(node.parentNode as Element);
+ }}
+ >
+ {contextHolder}
+
+
- {
- if (e.ctrlKey && e.key === "Enter") {
- e.preventDefault();
- handleImport();
- }
+ {
+ setImportVisible(false);
}}
- />
-
-
-

-
- {"ScriptCat"}
-
-
-
- {pageName === "options" && (
-
-
-
- {t("create_user_script")}
-
-
-
-
- {t("create_background_script")}
-
-
-
-
- {t("create_scheduled_script")}
-
-
- {
- if ("showOpenFilePicker" in window) {
- // 使用新的文件打开接口,解决无法监听本地文件的问题
- //@ts-ignore
- window
- .showOpenFilePicker({
- multiple: true,
- types: [
- {
- description: "JavaScript",
- accept: { "text/javascript": [".js"] },
- },
- ],
- })
- .then((handles: any) => {
- onDrop(handles as FileWithPath[]);
- });
- } else {
- // 旧的方式,无法监听本地文件变更
- document.getElementById("import-local")?.click();
- }
- }}
- >
- {t("import_by_local")}
-
- {
- setImportVisible(true);
- }}
- >
- {t("import_link")}
-
-
- }
- position="bl"
- >
-
-
- )}
- {
- const theme = key as "auto" | "light" | "dark";
- updateColorTheme(theme);
- }}
- selectedKeys={[colorThemeState]}
- >
-
- {t("light")}
-
-
- {t("dark")}
-
-
- {t("system_follow")}
-
-
- }
- position="bl"
>
-
- {showLanguage && (
-
- {languageList.map((value) => (
+
+
+

+
+ {"ScriptCat"}
+
+
+
+ {pageName === "options" && (
+
+
+
+ {t("create_user_script")}
+
+
+
+
+ {t("create_background_script")}
+
+
+
+
+ {t("create_scheduled_script")}
+
+
{
- if (value.key === "help") {
- window.open("https://github.com/scriptscat/scriptcat/discussions/531", "_blank");
- return;
+ if ("showOpenFilePicker" in window) {
+ // 使用新的文件打开接口,解决无法监听本地文件的问题
+ //@ts-ignore
+ window
+ .showOpenFilePicker({
+ multiple: true,
+ types: [
+ {
+ description: "JavaScript",
+ accept: { "text/javascript": [".js"] },
+ },
+ ],
+ })
+ .then((handles: any) => {
+ onDrop(handles as FileWithPath[]);
+ });
+ } else {
+ // 旧的方式,无法监听本地文件变更
+ document.getElementById("import-local")?.click();
}
- systemConfig.setLanguage(value.key);
- Message.success(t("language_change_tip", { lng: value.key })!);
}}
>
- {value.title}
+ {t("import_by_local")}
+
+ {
+ setImportVisible(true);
+ }}
+ >
+ {t("import_link")}
- ))}
+
+ }
+ position="bl"
+ >
+
+
+ )}
+ {
+ const theme = key as "auto" | "light" | "dark";
+ updateColorTheme(theme);
+ }}
+ selectedKeys={[colorThemeState]}
+ >
+
+ {t("light")}
+
+
+ {t("dark")}
+
+
+ {t("system_follow")}
+
}
+ position="bl"
>
}
+ icon={
+ <>
+ {colorThemeState === "auto" && }
+ {colorThemeState === "light" && }
+ {colorThemeState === "dark" && }
+ >
+ }
style={{
color: "var(--color-text-1)",
}}
className="!tw-text-lg"
- >
+ />
- )}
-
-
-
-
- {/* 性能关键:抽离遮罩组件,只有 active 变化时此小组件重绘 */}
-
- {children}
+ {showLanguage && (
+
+ {languageList.map((value) => (
+ {
+ if (value.key === "help") {
+ window.open("https://github.com/scriptscat/scriptcat/discussions/531", "_blank");
+ return;
+ }
+ systemConfig.setLanguage(value.key);
+ Message.success(t("language_change_tip", { lng: value.key })!);
+ }}
+ >
+ {value.title}
+
+ ))}
+
+ }
+ >
+ }
+ style={{
+ color: "var(--color-text-1)",
+ }}
+ className="!tw-text-lg"
+ >
+
+ )}
+
+
+
+
+ {/* 性能关键:抽离遮罩组件,只有 active 变化时此小组件重绘 */}
+
+ {children}
+
-
-
+
+
);
};
diff --git a/src/pages/utility/InteractiveContainer.tsx b/src/pages/utility/InteractiveContainer.tsx
new file mode 100644
index 000000000..54762f206
--- /dev/null
+++ b/src/pages/utility/InteractiveContainer.tsx
@@ -0,0 +1,57 @@
+import type { CSSProperties, HTMLAttributes, ReactNode } from "react";
+
+type InteractiveContainerProps = {
+ children: ReactNode;
+ className?: string;
+ style?: CSSProperties;
+} & HTMLAttributes;
+
+const handleContainerWheel = (evt: Event) => {
+ if ((evt.target as Element).closest(".monaco-editor")) {
+ evt.preventDefault();
+ } else {
+ evt.stopImmediatePropagation();
+ evt.stopPropagation();
+ // evt.preventDefault();
+ }
+};
+
+const attachMainHandler = (target: Node) => {
+ const o = { capture: false, passive: false, once: false };
+ target.removeEventListener("wheel", handleContainerWheel, o);
+ target.addEventListener("wheel", handleContainerWheel, o);
+};
+
+const weakSet = new WeakSet();
+
+const setRef = (div: HTMLDivElement) => {
+ if (!div) return;
+ if (weakSet.has(div)) return;
+ weakSet.add(div);
+ attachMainHandler(div);
+};
+
+/**
+ * Wraps arbitrary interactive content and intercepts wheel events at the
+ * container boundary.
+ *
+ * Wheel behavior:
+ * - Inside Monaco editor instances, prevent the browser from handling the
+ * event so editor scrolling remains isolated.
+ * - Outside Monaco, stop propagation so parent/page-level handlers do not
+ * react to wheel gestures from this container.
+ */
+export default function InteractiveContainer({ children, className, style, ...props }: InteractiveContainerProps) {
+ return (
+
+ {children}
+
+ );
+}
From afc4e148c6b9a1d750d9d5d3e7393d67127dd20f Mon Sep 17 00:00:00 2001
From: cyfung1031 <44498510+cyfung1031@users.noreply.github.com>
Date: Sun, 10 May 2026 14:11:41 +0900
Subject: [PATCH 2/5] code update
---
src/index.css | 10 ++-----
src/pages/utility/InteractiveContainer.tsx | 35 +++++-----------------
2 files changed, 9 insertions(+), 36 deletions(-)
diff --git a/src/index.css b/src/index.css
index dca7dbe5f..6b2ef8509 100644
--- a/src/index.css
+++ b/src/index.css
@@ -20,15 +20,9 @@ body[arco-theme='light'] {
--color-scrollbar-track: #f0f0f0;
}
-.interactive-container {
+#root {
overscroll-behavior: none;
- height: 100vh;
- width: 100vw;
- position: relative;
- contain: strict;
- padding: 0px;
- margin: 0px;
- box-sizing: border-box;
+ contain: content;
overflow: auto;
}
diff --git a/src/pages/utility/InteractiveContainer.tsx b/src/pages/utility/InteractiveContainer.tsx
index 54762f206..2e7350961 100644
--- a/src/pages/utility/InteractiveContainer.tsx
+++ b/src/pages/utility/InteractiveContainer.tsx
@@ -1,9 +1,7 @@
-import type { CSSProperties, HTMLAttributes, ReactNode } from "react";
+import type { HTMLAttributes, ReactNode } from "react";
type InteractiveContainerProps = {
children: ReactNode;
- className?: string;
- style?: CSSProperties;
} & HTMLAttributes;
const handleContainerWheel = (evt: Event) => {
@@ -16,19 +14,10 @@ const handleContainerWheel = (evt: Event) => {
}
};
-const attachMainHandler = (target: Node) => {
+const attachMainHandler = (target: Node | null) => {
const o = { capture: false, passive: false, once: false };
- target.removeEventListener("wheel", handleContainerWheel, o);
- target.addEventListener("wheel", handleContainerWheel, o);
-};
-
-const weakSet = new WeakSet();
-
-const setRef = (div: HTMLDivElement) => {
- if (!div) return;
- if (weakSet.has(div)) return;
- weakSet.add(div);
- attachMainHandler(div);
+ target?.removeEventListener("wheel", handleContainerWheel, o);
+ target?.addEventListener("wheel", handleContainerWheel, o);
};
/**
@@ -41,17 +30,7 @@ const setRef = (div: HTMLDivElement) => {
* - Outside Monaco, stop propagation so parent/page-level handlers do not
* react to wheel gestures from this container.
*/
-export default function InteractiveContainer({ children, className, style, ...props }: InteractiveContainerProps) {
- return (
-
- {children}
-
- );
+export default function InteractiveContainer({ children }: InteractiveContainerProps) {
+ attachMainHandler(document.getElementById("root"));
+ return <>{children}>;
}
From c741b2e8c52ec9ab24132e93eb7b5f72b1b87d38 Mon Sep 17 00:00:00 2001
From: cyfung1031 <44498510+cyfung1031@users.noreply.github.com>
Date: Sun, 10 May 2026 14:18:37 +0900
Subject: [PATCH 3/5] InteractiveContainer -> ScrollBoundary
---
src/pages/components/layout/MainLayout.tsx | 6 +-
.../components/layout/ScrollBoundary.tsx | 57 +++++++++++++++++++
src/pages/utility/InteractiveContainer.tsx | 36 ------------
3 files changed, 60 insertions(+), 39 deletions(-)
create mode 100644 src/pages/components/layout/ScrollBoundary.tsx
delete mode 100644 src/pages/utility/InteractiveContainer.tsx
diff --git a/src/pages/components/layout/MainLayout.tsx b/src/pages/components/layout/MainLayout.tsx
index db20d4c76..34c92742d 100644
--- a/src/pages/components/layout/MainLayout.tsx
+++ b/src/pages/components/layout/MainLayout.tsx
@@ -36,7 +36,7 @@ import { arcoLocale } from "@App/locales/arco";
import { prepareScriptByCode } from "@App/pkg/utils/script";
import { saveHandle } from "@App/pkg/utils/filehandle-db";
import { makeBlobURL } from "@App/pkg/utils/utils";
-import InteractiveContainer from "@App/pages/utility/InteractiveContainer";
+import ScrollBoundary from "@App/pages/components/layout/ScrollBoundary";
// --- 工具函数移出组件外,避免每次 Render 重新定义 ---
@@ -298,7 +298,7 @@ const MainLayout: React.FC<{
};
return (
-
+
{
return ;
@@ -508,7 +508,7 @@ const MainLayout: React.FC<{
-
+
);
};
diff --git a/src/pages/components/layout/ScrollBoundary.tsx b/src/pages/components/layout/ScrollBoundary.tsx
new file mode 100644
index 000000000..6cc808de8
--- /dev/null
+++ b/src/pages/components/layout/ScrollBoundary.tsx
@@ -0,0 +1,57 @@
+import type { HTMLAttributes, ReactNode } from "react";
+
+// Props extend native div attributes so callers can pass className, style, etc.
+type ScrollBoundaryProps = {
+ children: ReactNode;
+ parentNodeSelector: string;
+} & HTMLAttributes;
+
+/**
+ * Handles wheel events bubbling up to the scroll boundary.
+ *
+ * - Monaco editor: prevent default so the editor's own scroll logic runs
+ * without interference from the browser or ancestor handlers.
+ * - Everywhere else: stop propagation so parent/page-level handlers ignore
+ * wheel gestures originating inside this boundary.
+ * (preventDefault is intentionally left commented out to allow native
+ * scrolling within non-editor children.)
+ */
+const handleScrollBoundaryWheel = (evt: Event) => {
+ if ((evt.target as Element).closest(".monaco-editor")) {
+ evt.preventDefault();
+ } else {
+ evt.stopImmediatePropagation();
+ evt.stopPropagation();
+ // evt.preventDefault();
+ }
+};
+
+/**
+ * Registers the wheel handler on the given target node.
+ * Removes any existing listener first to guarantee exactly one handler is
+ * attached, even if called multiple times (e.g. on re-renders).
+ *
+ * Options: non-capturing, non-passive (required for preventDefault to work),
+ * and persistent (once: false).
+ */
+const attachScrollBoundaryHandler = (target: Node | null) => {
+ const o = { capture: false, passive: false, once: false };
+ target?.removeEventListener("wheel", handleScrollBoundaryWheel, o);
+ target?.addEventListener("wheel", handleScrollBoundaryWheel, o);
+};
+
+/**
+ * Establishes a wheel-event boundary at the root level.
+ *
+ * Wheel behavior within the boundary:
+ * - Inside Monaco editor instances: prevents default so editor scrolling
+ * stays isolated from browser and ancestor handlers.
+ * - Outside Monaco: stops propagation so parent/page-level handlers do not
+ * react to wheel gestures originating inside this boundary.
+ */
+export default function ScrollBoundary({ children, parentNodeSelector }: ScrollBoundaryProps) {
+ // Attach once per render; the handler is idempotent due to the
+ // remove-then-add pattern in attachScrollBoundaryHandler.
+ attachScrollBoundaryHandler(document.querySelector(parentNodeSelector));
+ return <>{children}>;
+}
diff --git a/src/pages/utility/InteractiveContainer.tsx b/src/pages/utility/InteractiveContainer.tsx
deleted file mode 100644
index 2e7350961..000000000
--- a/src/pages/utility/InteractiveContainer.tsx
+++ /dev/null
@@ -1,36 +0,0 @@
-import type { HTMLAttributes, ReactNode } from "react";
-
-type InteractiveContainerProps = {
- children: ReactNode;
-} & HTMLAttributes;
-
-const handleContainerWheel = (evt: Event) => {
- if ((evt.target as Element).closest(".monaco-editor")) {
- evt.preventDefault();
- } else {
- evt.stopImmediatePropagation();
- evt.stopPropagation();
- // evt.preventDefault();
- }
-};
-
-const attachMainHandler = (target: Node | null) => {
- const o = { capture: false, passive: false, once: false };
- target?.removeEventListener("wheel", handleContainerWheel, o);
- target?.addEventListener("wheel", handleContainerWheel, o);
-};
-
-/**
- * Wraps arbitrary interactive content and intercepts wheel events at the
- * container boundary.
- *
- * Wheel behavior:
- * - Inside Monaco editor instances, prevent the browser from handling the
- * event so editor scrolling remains isolated.
- * - Outside Monaco, stop propagation so parent/page-level handlers do not
- * react to wheel gestures from this container.
- */
-export default function InteractiveContainer({ children }: InteractiveContainerProps) {
- attachMainHandler(document.getElementById("root"));
- return <>{children}>;
-}
From 3de730331dba4a8ecca711e5ac96ed149cb517a4 Mon Sep 17 00:00:00 2001
From: cyfung1031 <44498510+cyfung1031@users.noreply.github.com>
Date: Sun, 10 May 2026 14:45:19 +0900
Subject: [PATCH 4/5] Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
---
src/pages/components/layout/ScrollBoundary.tsx | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/src/pages/components/layout/ScrollBoundary.tsx b/src/pages/components/layout/ScrollBoundary.tsx
index 6cc808de8..7d8184928 100644
--- a/src/pages/components/layout/ScrollBoundary.tsx
+++ b/src/pages/components/layout/ScrollBoundary.tsx
@@ -1,10 +1,9 @@
-import type { HTMLAttributes, ReactNode } from "react";
+import type { ReactNode } from "react";
-// Props extend native div attributes so callers can pass className, style, etc.
type ScrollBoundaryProps = {
children: ReactNode;
parentNodeSelector: string;
-} & HTMLAttributes;
+};
/**
* Handles wheel events bubbling up to the scroll boundary.
From 217110bfb05c004d747fe467e8d7a9c014a53c1b Mon Sep 17 00:00:00 2001
From: cyfung1031 <44498510+cyfung1031@users.noreply.github.com>
Date: Sun, 10 May 2026 14:45:34 +0900
Subject: [PATCH 5/5] Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
---
src/pages/components/layout/ScrollBoundary.tsx | 1 -
1 file changed, 1 deletion(-)
diff --git a/src/pages/components/layout/ScrollBoundary.tsx b/src/pages/components/layout/ScrollBoundary.tsx
index 7d8184928..aaf65a152 100644
--- a/src/pages/components/layout/ScrollBoundary.tsx
+++ b/src/pages/components/layout/ScrollBoundary.tsx
@@ -19,7 +19,6 @@ const handleScrollBoundaryWheel = (evt: Event) => {
if ((evt.target as Element).closest(".monaco-editor")) {
evt.preventDefault();
} else {
- evt.stopImmediatePropagation();
evt.stopPropagation();
// evt.preventDefault();
}