diff --git a/electron/main/index.ts b/electron/main/index.ts index a720720db..02cedbb87 100644 --- a/electron/main/index.ts +++ b/electron/main/index.ts @@ -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(); diff --git a/index.html b/index.html index 9c2d1457f..5c3481fab 100644 --- a/index.html +++ b/index.html @@ -6,7 +6,7 @@ Eigent diff --git a/src/components/AddWorker/ToolSelect.tsx b/src/components/AddWorker/ToolSelect.tsx index 0b29ebd66..92922f92e 100644 --- a/src/components/AddWorker/ToolSelect.tsx +++ b/src/components/AddWorker/ToolSelect.tsx @@ -56,7 +56,7 @@ const ToolSelect = forwardRef< const [integrations, setIntegrations] = useState([]); 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]); diff --git a/src/components/Folder/index.tsx b/src/components/Folder/index.tsx index 7834bfc4e..b12816221 100644 --- a/src/components/Folder/index.tsx +++ b/src/components/Folder/index.tsx @@ -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 }) { ) : selectedFile.type === "pdf" ? (