first commit
This commit is contained in:
314
test-inject-script.js
Normal file
314
test-inject-script.js
Normal file
@@ -0,0 +1,314 @@
|
||||
(() => {
|
||||
const SCRIPT_ID = 'excalidraw-control-script';
|
||||
if (window[SCRIPT_ID]) {
|
||||
return;
|
||||
}
|
||||
function getExcalidrawAPIFromDOM(domElement) {
|
||||
if (!domElement) {
|
||||
return null;
|
||||
}
|
||||
const reactFiberKey = Object.keys(domElement).find(
|
||||
(key) => key.startsWith('__reactFiber$') || key.startsWith('__reactInternalInstance$'),
|
||||
);
|
||||
if (!reactFiberKey) {
|
||||
return null;
|
||||
}
|
||||
let fiberNode = domElement[reactFiberKey];
|
||||
if (!fiberNode) {
|
||||
return null;
|
||||
}
|
||||
function isExcalidrawAPI(obj) {
|
||||
return (
|
||||
typeof obj === 'object' &&
|
||||
obj !== null &&
|
||||
typeof obj.updateScene === 'function' &&
|
||||
typeof obj.getSceneElements === 'function' &&
|
||||
typeof obj.getAppState === 'function'
|
||||
);
|
||||
}
|
||||
function findApiInObject(objToSearch) {
|
||||
if (isExcalidrawAPI(objToSearch)) {
|
||||
return objToSearch;
|
||||
}
|
||||
if (typeof objToSearch === 'object' && objToSearch !== null) {
|
||||
for (const key in objToSearch) {
|
||||
if (Object.prototype.hasOwnProperty.call(objToSearch, key)) {
|
||||
const found = findApiInObject(objToSearch[key]);
|
||||
if (found) {
|
||||
return found;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
let excalidrawApiInstance = null;
|
||||
let attempts = 0;
|
||||
const MAX_TRAVERSAL_ATTEMPTS = 25;
|
||||
while (fiberNode && attempts < MAX_TRAVERSAL_ATTEMPTS) {
|
||||
if (fiberNode.stateNode && fiberNode.stateNode.props) {
|
||||
const api = findApiInObject(fiberNode.stateNode.props);
|
||||
if (api) {
|
||||
excalidrawApiInstance = api;
|
||||
break;
|
||||
}
|
||||
if (isExcalidrawAPI(fiberNode.stateNode.props.excalidrawAPI)) {
|
||||
excalidrawApiInstance = fiberNode.stateNode.props.excalidrawAPI;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (fiberNode.memoizedProps) {
|
||||
const api = findApiInObject(fiberNode.memoizedProps);
|
||||
if (api) {
|
||||
excalidrawApiInstance = api;
|
||||
break;
|
||||
}
|
||||
if (isExcalidrawAPI(fiberNode.memoizedProps.excalidrawAPI)) {
|
||||
excalidrawApiInstance = fiberNode.memoizedProps.excalidrawAPI;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (fiberNode.tag === 1 && fiberNode.stateNode && fiberNode.stateNode.state) {
|
||||
const api = findApiInObject(fiberNode.stateNode.state);
|
||||
if (api) {
|
||||
excalidrawApiInstance = api;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
fiberNode.tag === 0 ||
|
||||
fiberNode.tag === 2 ||
|
||||
fiberNode.tag === 14 ||
|
||||
fiberNode.tag === 15 ||
|
||||
fiberNode.tag === 11
|
||||
) {
|
||||
if (fiberNode.memoizedState) {
|
||||
let currentHook = fiberNode.memoizedState;
|
||||
let hookAttempts = 0;
|
||||
const MAX_HOOK_ATTEMPTS = 15;
|
||||
while (currentHook && hookAttempts < MAX_HOOK_ATTEMPTS) {
|
||||
const api = findApiInObject(currentHook.memoizedState);
|
||||
if (api) {
|
||||
excalidrawApiInstance = api;
|
||||
break;
|
||||
}
|
||||
currentHook = currentHook.next;
|
||||
hookAttempts++;
|
||||
}
|
||||
if (excalidrawApiInstance) break;
|
||||
}
|
||||
}
|
||||
if (fiberNode.stateNode) {
|
||||
const api = findApiInObject(fiberNode.stateNode);
|
||||
if (api && api !== fiberNode.stateNode.props && api !== fiberNode.stateNode.state) {
|
||||
excalidrawApiInstance = api;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (
|
||||
fiberNode.tag === 9 &&
|
||||
fiberNode.memoizedProps &&
|
||||
typeof fiberNode.memoizedProps.value !== 'undefined'
|
||||
) {
|
||||
const api = findApiInObject(fiberNode.memoizedProps.value);
|
||||
if (api) {
|
||||
excalidrawApiInstance = api;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (fiberNode.return) {
|
||||
fiberNode = fiberNode.return;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
attempts++;
|
||||
}
|
||||
|
||||
if (excalidrawApiInstance) {
|
||||
window.excalidrawAPI = excalidrawApiInstance;
|
||||
console.log('现在您可以通过 `window.foundExcalidrawAPI` 在控制台访问它。');
|
||||
} else {
|
||||
console.error('在检查组件树后未能找到 excalidrawAPI。');
|
||||
}
|
||||
return excalidrawApiInstance;
|
||||
}
|
||||
|
||||
function createFullExcalidrawElement(skeleton) {
|
||||
const id = Math.random().toString(36).substring(2, 9);
|
||||
|
||||
const seed = Math.floor(Math.random() * 2 ** 31);
|
||||
const versionNonce = Math.floor(Math.random() * 2 ** 31);
|
||||
|
||||
const defaults = {
|
||||
isDeleted: false,
|
||||
fillStyle: 'hachure',
|
||||
strokeWidth: 1,
|
||||
strokeStyle: 'solid',
|
||||
roughness: 1,
|
||||
opacity: 100,
|
||||
angle: 0,
|
||||
groupIds: [],
|
||||
strokeColor: '#000000',
|
||||
backgroundColor: 'transparent',
|
||||
version: 1,
|
||||
locked: false,
|
||||
};
|
||||
|
||||
const fullElement = {
|
||||
id: id,
|
||||
seed: seed,
|
||||
versionNonce: versionNonce,
|
||||
updated: Date.now(),
|
||||
...defaults,
|
||||
...skeleton,
|
||||
};
|
||||
|
||||
return fullElement;
|
||||
}
|
||||
|
||||
let targetElementForAPI = document.querySelector('.excalidraw-app');
|
||||
|
||||
if (targetElementForAPI) {
|
||||
getExcalidrawAPIFromDOM(targetElementForAPI);
|
||||
}
|
||||
|
||||
const eventHandler = {
|
||||
getSceneElements: () => {
|
||||
try {
|
||||
return window.excalidrawAPI.getSceneElements();
|
||||
} catch (error) {
|
||||
return {
|
||||
error: true,
|
||||
msg: JSON.stringify(error),
|
||||
};
|
||||
}
|
||||
},
|
||||
addElement: (param) => {
|
||||
try {
|
||||
const existingElements = window.excalidrawAPI.getSceneElements();
|
||||
const newElements = [...existingElements];
|
||||
param.eles.forEach((ele, idx) => {
|
||||
const newEle = createFullExcalidrawElement(ele);
|
||||
newEle.index = `a${existingElements.length + idx + 1}`;
|
||||
newElements.push(newEle);
|
||||
});
|
||||
console.log('newElements ==>', newElements);
|
||||
const appState = window.excalidrawAPI.getAppState();
|
||||
window.excalidrawAPI.updateScene({
|
||||
elements: newElements,
|
||||
appState: appState,
|
||||
commitToHistory: true,
|
||||
});
|
||||
return {
|
||||
success: true,
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
error: true,
|
||||
msg: JSON.stringify(error),
|
||||
};
|
||||
}
|
||||
},
|
||||
deleteElement: (param) => {
|
||||
try {
|
||||
const existingElements = window.excalidrawAPI.getSceneElements();
|
||||
const newElements = [...existingElements];
|
||||
const idx = newElements.findIndex((e) => e.id === param.id);
|
||||
if (idx >= 0) {
|
||||
newElements.splice(idx, 1);
|
||||
const appState = window.excalidrawAPI.getAppState();
|
||||
window.excalidrawAPI.updateScene({
|
||||
elements: newElements,
|
||||
appState: appState,
|
||||
commitToHistory: true,
|
||||
});
|
||||
return {
|
||||
success: true,
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
error: true,
|
||||
msg: 'element not found',
|
||||
};
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
error: true,
|
||||
msg: JSON.stringify(error),
|
||||
};
|
||||
}
|
||||
},
|
||||
updateElement: (param) => {
|
||||
try {
|
||||
const existingElements = window.excalidrawAPI.getSceneElements();
|
||||
const resIds = [];
|
||||
for (let i = 0; i < param.length; i++) {
|
||||
const idx = existingElements.findIndex((e) => e.id === param[i].id);
|
||||
if (idx >= 0) {
|
||||
resIds.push[idx];
|
||||
window.excalidrawAPI.mutateElement(existingElements[idx], { ...param[i] });
|
||||
}
|
||||
}
|
||||
return {
|
||||
success: true,
|
||||
msg: `已更新元素:${resIds.join(',')}`,
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
error: true,
|
||||
msg: JSON.stringify(error),
|
||||
};
|
||||
}
|
||||
},
|
||||
cleanup: () => {
|
||||
try {
|
||||
window.excalidrawAPI.resetScene();
|
||||
return {
|
||||
success: true,
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
error: true,
|
||||
msg: JSON.stringify(error),
|
||||
};
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
const handleExecution = (event) => {
|
||||
const { action, payload, requestId } = event.detail;
|
||||
const param = JSON.parse(payload || '{}');
|
||||
let data, error;
|
||||
try {
|
||||
const handler = eventHandler[action];
|
||||
if (!handler) {
|
||||
error = 'event name not found';
|
||||
}
|
||||
data = handler(param);
|
||||
} catch (e) {
|
||||
error = e.message;
|
||||
}
|
||||
window.dispatchEvent(
|
||||
new CustomEvent('chrome-mcp:response', { detail: { requestId, data, error } }),
|
||||
);
|
||||
};
|
||||
|
||||
// --- Lifecycle Functions ---
|
||||
const initialize = () => {
|
||||
window.addEventListener('chrome-mcp:execute', handleExecution);
|
||||
window.addEventListener('chrome-mcp:cleanup', cleanup);
|
||||
window[SCRIPT_ID] = true;
|
||||
};
|
||||
|
||||
const cleanup = () => {
|
||||
window.removeEventListener('chrome-mcp:execute', handleExecution);
|
||||
window.removeEventListener('chrome-mcp:cleanup', cleanup);
|
||||
delete window[SCRIPT_ID];
|
||||
delete window.excalidrawAPI;
|
||||
};
|
||||
|
||||
initialize();
|
||||
})();
|
Reference in New Issue
Block a user