mirror of
https://github.com/BetterSEQTA/BetterSEQTA-Plus.git
synced 2026-06-05 19:24:39 +00:00
245 lines
6.7 KiB
JavaScript
245 lines
6.7 KiB
JavaScript
class ReactFiber {
|
|
constructor(selector, options = {}) {
|
|
this.selector = selector;
|
|
this.debug = options.debug || false;
|
|
this.nodes = [...document.querySelectorAll(selector)]; // Support multiple elements
|
|
this.fibers = this.nodes.map((node) => this.getFiberNode(node));
|
|
this.components = this.fibers.map((fiber) => this.getOwnerComponent(fiber));
|
|
|
|
if (this.debug) {
|
|
console.log("Selected Nodes:", this.nodes);
|
|
console.log("🔍 Found Fibers:", this.fibers);
|
|
console.log("🛠 Found Components:", this.components);
|
|
}
|
|
}
|
|
|
|
static find(selector, options = {}) {
|
|
return new ReactFiber(selector, options);
|
|
}
|
|
|
|
getFiberNode(node) {
|
|
if (!node) return null;
|
|
const fiberKey = Object.getOwnPropertyNames(node).find(
|
|
(name) =>
|
|
name.startsWith("__reactFiber") ||
|
|
name.startsWith("__reactInternalInstance"),
|
|
);
|
|
return fiberKey ? node[fiberKey] : null;
|
|
}
|
|
|
|
getOwnerComponent(fiberNode) {
|
|
let current = fiberNode;
|
|
while (current) {
|
|
if (
|
|
current.stateNode &&
|
|
(current.stateNode.setState || current.stateNode.forceUpdate)
|
|
) {
|
|
return current.stateNode;
|
|
}
|
|
current = current.return;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
getState(key) {
|
|
if (!this.components.length) return null;
|
|
const state = this.components[0]?.state || null;
|
|
|
|
if (key === undefined) {
|
|
return state;
|
|
} else if (typeof key === "string") {
|
|
return state?.[key];
|
|
} else if (Array.isArray(key)) {
|
|
const filteredState = {};
|
|
for (const k of key) {
|
|
if (state && Object.hasOwn(state, k)) {
|
|
filteredState[k] = state[k];
|
|
}
|
|
}
|
|
return filteredState;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
setState(update) {
|
|
this.components.forEach((component) => {
|
|
if (component?.setState) {
|
|
if (typeof update === "function") {
|
|
// Functional update
|
|
component.setState((prevState) => {
|
|
const newState = update(prevState);
|
|
if (this.debug)
|
|
console.log("✅ Updated State (Functional):", newState);
|
|
return newState;
|
|
});
|
|
} else {
|
|
// Object update (merge with existing state)
|
|
component.setState((prevState) => {
|
|
const newState = {
|
|
...prevState,
|
|
...update,
|
|
};
|
|
if (this.debug)
|
|
console.log("✅ Updated State (Object Merge):", newState);
|
|
return newState;
|
|
});
|
|
}
|
|
}
|
|
});
|
|
return this;
|
|
}
|
|
|
|
getProp(propName) {
|
|
if (!this.fibers.length) return null;
|
|
|
|
if (propName === undefined) {
|
|
return this.fibers[0]?.memoizedProps;
|
|
}
|
|
|
|
return this.fibers[0]?.memoizedProps?.[propName];
|
|
}
|
|
|
|
setProp(propName) {
|
|
this.fibers.forEach((fiber) => {
|
|
if (fiber?.memoizedProps) {
|
|
fiber.memoizedProps[propName] = value;
|
|
}
|
|
});
|
|
return this; // Enable chaining
|
|
}
|
|
|
|
forceUpdate() {
|
|
this.components.forEach((component) => {
|
|
if (component?.forceUpdate) {
|
|
component.forceUpdate();
|
|
if (this.debug) console.log("🔄 Forced React Re-render");
|
|
}
|
|
});
|
|
return this; // Enable chaining
|
|
}
|
|
}
|
|
|
|
function makeSerializable(obj) {
|
|
if (typeof obj !== "object" || obj === null) {
|
|
return obj;
|
|
}
|
|
|
|
if (Array.isArray(obj)) {
|
|
return obj.map((item) => makeSerializable(item));
|
|
}
|
|
|
|
const serializableObj = {};
|
|
for (const key in obj) {
|
|
if (Object.hasOwn(obj, key)) {
|
|
let value = obj[key];
|
|
|
|
if (typeof value === "function") {
|
|
value = "[Function]";
|
|
} else if (value instanceof HTMLElement) {
|
|
value = {
|
|
type: "HTMLElement",
|
|
id: value.id,
|
|
tagName: value.tagName,
|
|
}; // Replace DOM node with ID/tag info
|
|
} else if (typeof value === "symbol") {
|
|
value = value.toString();
|
|
} else if (typeof value === "object" && value !== null) {
|
|
value = makeSerializable(value);
|
|
}
|
|
|
|
serializableObj[key] = value;
|
|
}
|
|
}
|
|
return serializableObj;
|
|
}
|
|
|
|
window.addEventListener("message", (event) => {
|
|
if (event.data.type === "reactFiberRequest") {
|
|
const { selector, action, payload, debug, messageId } = event.data;
|
|
const fiberInstance = ReactFiber.find(selector, {
|
|
debug,
|
|
});
|
|
|
|
let response;
|
|
switch (action) {
|
|
case "getState":
|
|
response = fiberInstance.getState(payload.key);
|
|
break;
|
|
case "setState":
|
|
// Handle both function and object updates
|
|
if (payload.updateFn) {
|
|
const updateFn = new Function('return ' + payload.updateFn)();
|
|
fiberInstance.setState(updateFn);
|
|
} else {
|
|
fiberInstance.setState(payload.updateObject);
|
|
}
|
|
response = {};
|
|
break;
|
|
|
|
case "getProp":
|
|
response = fiberInstance.getProp(payload.propName);
|
|
break;
|
|
case "setProp":
|
|
fiberInstance.setProp(payload.propName, payload.value);
|
|
response = {};
|
|
break;
|
|
case "forceUpdate":
|
|
fiberInstance.forceUpdate();
|
|
response = {};
|
|
break;
|
|
default:
|
|
console.warn(`[pageState] Unknown action: ${action}`);
|
|
response = null;
|
|
}
|
|
|
|
if (response !== null && typeof response === "object") {
|
|
response = makeSerializable(response);
|
|
}
|
|
|
|
window.postMessage(
|
|
{
|
|
type: "reactFiberResponse",
|
|
response,
|
|
messageId,
|
|
},
|
|
"*",
|
|
);
|
|
} else if (event.data.type === "triggerKeyboardEvent") {
|
|
// Handle keyboard event triggering from content script
|
|
const { key, code, altKey, ctrlKey, metaKey, shiftKey, keyCode } = event.data;
|
|
|
|
const keyboardEvent = new KeyboardEvent('keydown', {
|
|
key,
|
|
code,
|
|
keyCode: keyCode || 0,
|
|
which: keyCode || 0,
|
|
altKey: altKey || false,
|
|
ctrlKey: ctrlKey || false,
|
|
metaKey: metaKey || false,
|
|
shiftKey: shiftKey || false,
|
|
bubbles: true,
|
|
cancelable: true
|
|
});
|
|
|
|
document.dispatchEvent(keyboardEvent);
|
|
} else if (event.data.type === "ckeditorSetData") {
|
|
// Handle CKEditor data setting
|
|
const { editorId, content } = event.data;
|
|
|
|
if (window.CKEDITOR && window.CKEDITOR.instances && window.CKEDITOR.instances[editorId]) {
|
|
window.CKEDITOR.instances[editorId].setData(content);
|
|
} else {
|
|
console.warn(`[pageState] CKEditor instance '${editorId}' not found`);
|
|
}
|
|
} else if (event.data.type === "ckeditorGetData") {
|
|
const { editorId } = event.data;
|
|
if (window.CKEDITOR && window.CKEDITOR.instances && window.CKEDITOR.instances[editorId]) {
|
|
const data = window.CKEDITOR.instances[editorId].getData();
|
|
window.postMessage({
|
|
type: "ckeditorGetDataResponse",
|
|
data,
|
|
}, "*");
|
|
}
|
|
}
|
|
});
|