diff --git a/package.json b/package.json
index 34d865520..ea6d9a83c 100644
--- a/package.json
+++ b/package.json
@@ -34,6 +34,7 @@
"@dnd-kit/modifiers": "^9.0.0",
"@dnd-kit/sortable": "^10.0.0",
"@dnd-kit/utilities": "^3.2.2",
+ "acorn": "^8.16.0",
"chardet": "^2.1.1",
"cron": "^4.4.0",
"crypto-js": "^4.2.0",
@@ -44,6 +45,7 @@
"eventemitter3": "^5.0.1",
"fast-xml-parser": "^5.5.8",
"i18next": "^23.16.4",
+ "magic-string": "^0.30.21",
"monaco-editor": "^0.52.2",
"react": "^18.3.1",
"react-dom": "^18.3.1",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index ca4e846db..1335be835 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -23,6 +23,9 @@ importers:
'@dnd-kit/utilities':
specifier: ^3.2.2
version: 3.2.2(react@18.3.1)
+ acorn:
+ specifier: ^8.16.0
+ version: 8.16.0
chardet:
specifier: ^2.1.1
version: 2.1.1
@@ -53,6 +56,9 @@ importers:
i18next:
specifier: ^23.16.4
version: 23.16.4
+ magic-string:
+ specifier: ^0.30.21
+ version: 0.30.21
monaco-editor:
specifier: ^0.52.2
version: 0.52.2
diff --git a/rspack.config.ts b/rspack.config.ts
index 91969281c..691277ecb 100644
--- a/rspack.config.ts
+++ b/rspack.config.ts
@@ -52,6 +52,7 @@ export default {
service_worker: `${src}/service_worker.ts`,
offscreen: `${src}/offscreen.ts`,
sandbox: `${src}/sandbox.ts`,
+ persistent_frame: `${src}/persistent_frame.ts`,
content: `${src}/content.ts`,
scripting: `${src}/scripting.ts`,
inject: `${src}/inject.ts`,
@@ -233,6 +234,13 @@ export default {
minify: true,
chunks: ["sandbox"],
}),
+ new rspack.HtmlRspackPlugin({
+ filename: `${dist}/ext/src/persistent_frame.html`,
+ template: `${src}/pages/persistent_frame.html`,
+ inject: "head",
+ minify: true,
+ chunks: ["persistent_frame"],
+ }),
new ZipExecutionPlugin(),
].filter(Boolean),
experiments: {
diff --git a/src/pages/persistent_frame.html b/src/pages/persistent_frame.html
new file mode 100644
index 000000000..bb5973e67
--- /dev/null
+++ b/src/pages/persistent_frame.html
@@ -0,0 +1,11 @@
+
+
+
+
+
+ persistent_frame
+
+
+
+
+
diff --git a/src/persistent_frame.ts b/src/persistent_frame.ts
new file mode 100644
index 000000000..c6bfe23de
--- /dev/null
+++ b/src/persistent_frame.ts
@@ -0,0 +1,32 @@
+// persistent_frame.ts
+const WAKE_UP_INTERVAL = 2000;
+let waitState = 0;
+let lastNow = 0;
+if (typeof frameElement === "object" && frameElement) {
+ const cNode = document.createComment("0");
+ let cVal = 0;
+ const runner = (ts: number) => {
+ waitState = 1;
+ cVal = cVal > 8 ? 1 : cVal + 1;
+ cNode.data = `${cVal}`;
+ const now = ts;
+ if (now - lastNow > WAKE_UP_INTERVAL) {
+ lastNow = now;
+ chrome.storage.session.set({ persistentWakeup: `${now}` });
+ document.title = `wakup at ${now}`; // debug
+ }
+ };
+ window.addEventListener("message", (ev) => {
+ if (waitState === 2) {
+ if (typeof ev.data === "object" && ev.data?.myCustomAction === "waked-up") runner(ev.timeStamp);
+ }
+ });
+ const mutObserver = new MutationObserver(() => {
+ if (waitState === 1) {
+ waitState = 2;
+ window?.postMessage({ myCustomAction: "waked-up" }, "*");
+ }
+ });
+ mutObserver.observe(cNode, { characterData: true });
+ runner(0);
+}
diff --git a/src/pkg/utils/persistent.ts b/src/pkg/utils/persistent.ts
new file mode 100644
index 000000000..e6066b408
--- /dev/null
+++ b/src/pkg/utils/persistent.ts
@@ -0,0 +1,13 @@
+export const keepEventPageRunning = () => {
+ if (typeof frames !== "object") return;
+ if (typeof document === "undefined") return;
+ if (typeof document.documentElement === "undefined") return;
+ if (document.getElementById("persistent_frame")) return;
+ chrome.storage.session.onChanged.addListener((obj) => {
+ typeof obj.persistentWakeup !== "undefined";
+ });
+ const iframe = document.createElement("iframe");
+ iframe.id = "persistent_frame";
+ iframe.src = chrome.runtime.getURL("/src/persistent_frame.html");
+ document.documentElement.appendChild(iframe);
+};
diff --git a/src/service_worker.ts b/src/service_worker.ts
index 407a27922..3e465b26c 100644
--- a/src/service_worker.ts
+++ b/src/service_worker.ts
@@ -8,9 +8,11 @@ import { MessageQueue } from "@Packages/message/message_queue";
import { ServiceWorkerMessageSend } from "@Packages/message/window_message";
import migrate, { migrateChromeStorage } from "./app/migrate";
import { cleanInvalidKeys } from "./app/repo/resource";
+import { keepEventPageRunning } from "@App/pkg/utils/persistent";
migrate();
migrateChromeStorage();
+keepEventPageRunning();
const OFFSCREEN_DOCUMENT_PATH = "src/offscreen.html";