Merge branch 'main' into feat/add-task-hover

This commit is contained in:
Wendong-Fan 2025-11-21 02:21:43 +08:00 committed by GitHub
commit ac83bdea88
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 105 additions and 13 deletions

View file

@ -89,6 +89,21 @@ app.commandLine.appendSwitch('max_old_space_size', '4096');
app.commandLine.appendSwitch('enable-features', 'MemoryPressureReduction');
app.commandLine.appendSwitch('renderer-process-limit', '8');
// ==================== protocol privileges ====================
// Register custom protocol privileges before app ready
protocol.registerSchemesAsPrivileged([
{
scheme: 'localfile',
privileges: {
standard: true,
secure: true,
supportFetchAPI: true,
corsEnabled: false,
bypassCSP: false,
},
},
]);
// ==================== app config ====================
process.env.APP_ROOT = MAIN_DIST;
process.env.VITE_PUBLIC = VITE_PUBLIC;
@ -1577,12 +1592,24 @@ app.whenReady().then(async () => {
});
// ==================== protocol handle ====================
protocol.handle('localfile', async (request) => {
// Register protocol handler for both default session and main window session
const protocolHandler = async (request: Request) => {
const url = decodeURIComponent(request.url.replace('localfile://', ''));
const filePath = path.normalize(url);
log.info(`[PROTOCOL] Handling localfile request: ${request.url}`);
log.info(`[PROTOCOL] Decoded path: ${filePath}`);
try {
// Check if file exists
const fileExists = await fsp.access(filePath).then(() => true).catch(() => false);
if (!fileExists) {
log.error(`[PROTOCOL] File not found: ${filePath}`);
return new Response('File Not Found', { status: 404 });
}
const data = await fsp.readFile(filePath);
log.info(`[PROTOCOL] Successfully read file, size: ${data.length} bytes`);
// set correct Content-Type according to file extension
const ext = path.extname(filePath).toLowerCase();
@ -1596,17 +1623,46 @@ app.whenReady().then(async () => {
case '.htm':
contentType = 'text/html';
break;
case '.png':
contentType = 'image/png';
break;
case '.jpg':
case '.jpeg':
contentType = 'image/jpeg';
break;
case '.gif':
contentType = 'image/gif';
break;
case '.svg':
contentType = 'image/svg+xml';
break;
case '.webp':
contentType = 'image/webp';
break;
}
log.info(`[PROTOCOL] Returning file with Content-Type: ${contentType}`);
return new Response(new Uint8Array(data), {
headers: {
'Content-Type': contentType,
'Content-Length': data.length.toString(),
},
});
} catch (err) {
return new Response('Not Found', { status: 404 });
log.error(`[PROTOCOL] Error reading file: ${err}`);
return new Response('Internal Server Error', { status: 500 });
}
});
};
// Register on default session
protocol.handle('localfile', protocolHandler);
// Also register on main window session
const mainSession = session.fromPartition('persist:main_window');
mainSession.protocol.handle('localfile', protocolHandler);
log.info('[PROTOCOL] Registered localfile protocol on both default and main_window sessions');
// ==================== initialize app ====================
initializeApp();

View file

@ -6,7 +6,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta
http-equiv="Content-Security-Policy"
content="script-src 'self' 'unsafe-inline' 'unsafe-eval' https://cdn.amplitude.com; worker-src 'self' blob:; child-src 'self' blob:;frame-src 'self' localfile: blob:;"
content="script-src 'self' 'unsafe-inline' 'unsafe-eval' https://cdn.amplitude.com; worker-src 'self' blob:; child-src 'self' blob:;frame-src 'self' localfile: blob: data:;"
/>
<script src="https://cdn.amplitude.com/libs/analytics-browser-2.11.1-min.js.gz"></script><script src="https://cdn.amplitude.com/libs/plugin-session-replay-browser-1.8.0-min.js.gz"></script><script>window.amplitude.add(window.sessionReplay.plugin({sampleRate: 1}));window.amplitude.init('87ce6adbb14b24ffe1703d18bf405e40', {"autocapture":{"elementInteractions":true}});</script>
<title>Eigent</title>

View file

@ -56,7 +56,7 @@ const ToolSelect = forwardRef<
const [integrations, setIntegrations] = useState<any[]>([]);
const fetchIntegrationsData = (keyword?: string) => {
proxyFetchGet("/api/config/info").then((res) => {
if (res && typeof res === "object") {
if (res && typeof res === "object" && !res.error) {
const baseURL = getProxyBaseURL();
const list = Object.entries(res)
@ -187,7 +187,13 @@ const ToolSelect = forwardRef<
};
});
setIntegrations(list);
} else {
console.error("Failed to fetch integrations:", res);
setIntegrations([]);
}
}).catch((error) => {
console.error("Error fetching integrations:", error);
setIntegrations([]);
});
};
@ -217,7 +223,16 @@ const ToolSelect = forwardRef<
page: 1,
size: 100,
}).then((res) => {
setAllMcpList(res.items);
// Add defensive check for API errors
if (res && res.items && Array.isArray(res.items)) {
setAllMcpList(res.items);
} else {
console.error("Failed to fetch MCPs:", res);
setAllMcpList([]);
}
}).catch((error) => {
console.error("Error fetching MCPs:", error);
setAllMcpList([]);
});
};
@ -228,7 +243,7 @@ const ToolSelect = forwardRef<
if (Array.isArray(res)) {
ids = res.map((item: any) => item.mcp_id);
dataList = res;
} else if (Array.isArray(res.items)) {
} else if (res && Array.isArray(res.items)) {
ids = res.items.map((item: any) => item.mcp_id);
dataList = res.items;
}
@ -236,14 +251,22 @@ const ToolSelect = forwardRef<
const customMcpList = dataList.filter((item: any) => item.mcp_id === 0);
setCustomMcpList(customMcpList);
}).catch((error) => {
console.error("Error fetching installed MCPs:", error);
setInstalledIds([]);
setCustomMcpList([]);
});
};
// only surface installed MCPs from the market list
useEffect(() => {
if (!installedIds.length) {
// Add defensive check and fix logic: should filter when installedIds has items
if (Array.isArray(allMcpList) && installedIds.length > 0) {
const filtered = allMcpList.filter((item) => installedIds.includes(item.id));
setMcpList(filtered);
} else if (Array.isArray(allMcpList)) {
// If no installed IDs, show empty list instead of all
setMcpList([]);
}
}, [allMcpList, installedIds]);

View file

@ -183,7 +183,23 @@ export default function Folder({ data }: { data?: Agent }) {
setLoading(true);
console.log("file", JSON.parse(JSON.stringify(file)));
// all files call open-file interface, the backend handles download and parsing
// For PDF files, use data URL instead of custom protocol
if (file.type === "pdf") {
window.ipcRenderer
.invoke("read-file-dataurl", file.path)
.then((dataUrl: string) => {
setSelectedFile({ ...file, content: dataUrl });
chatStore.setSelectedFile(chatStore.activeTaskId as string, file);
setLoading(false);
})
.catch((error) => {
console.error("read-file-dataurl error:", error);
setLoading(false);
});
return;
}
// all other files call open-file interface, the backend handles download and parsing
window.ipcRenderer
.invoke("open-file", file.type, file.path, isShowSourceCode)
.then((res) => {
@ -539,10 +555,7 @@ export default function Folder({ data }: { data?: Agent }) {
</div>
) : selectedFile.type === "pdf" ? (
<iframe
src={
"localfile://" +
encodeURIComponent(selectedFile.content as string)
}
src={selectedFile.content as string}
className="w-full h-full border-0"
title={selectedFile.name}
/>