إنتقل إلى المحتوى الرئيسي

تطوير تطبيقات سطح المكتب بـ Electron في Cursor: IPC و Context Isolation و Auto-Update

تطوير تطبيقات سطح المكتب بـ Electron في Cursor

يتيح لك Electron بناء تطبيقات سطح مكتب متعددة المنصات باستخدام تقنيات الويب التي تعرفها بالفعل: HTML و CSS و JavaScript. Cursor، وهو محرر IDE مدعوم بالذكاء الاصطناعي، يبسط تطوير Electron من خلال إنشاء هياكل المشاريع، وتوليد معالجات IPC، وكتابة نصوص preload معزولة السياق، وأتمتة سلسلة التغليف والتحديث التلقائي. يغطي هذا الدليل العمليات الرئيسية والعارضة، وتواصل IPC الآمن، وعزل السياق، والتغليف بـ electron-builder، واستراتيجيات التحديث التلقائي، وسير عمل واجهة سطح المكتب بمساعدة الذكاء الاصطناعي.

المتطلبات الأساسية​

قبل البدء، تأكد من أن لديك ما يلي:

  • Cursor (يُوصى بأحدث إصدار) كمحرر IDE الخاص بك
  • Node.js 18 أو أحدث
  • معرفة أساسية بـ JavaScript و TypeScript و npm
  • شهادة توقيع الكود (اختياري، ولكن مطلوب للتحديث التلقائي على macOS و Windows)
تلميح

إذا كنت جديدًا على Electron، اطلب من Cursor شرح نموذج العملية الرئيسية والعارضة قبل البدء بالبرمجة. فهم حدود العملية مسبقًا يمنع أكثر أخطاء Electron الأمنية والمعمارية شيوعًا.

فهم نموذج عمليات Electron​

تتكون تطبيقات Electron من نوعين من العمليات:

  • العملية الرئيسية (Main process): تعمل في Node.js، ولديها وصول كامل إلى نظام التشغيل، وتدير دورة حياة التطبيق وتنشئ نوافذ المتصفح.
  • العملية العارضة (Renderer process): تعمل في عارض Chromium، وتعرض واجهة الويب الخاصة بك، ولديها وصول مقيد إلى واجهات برمجة التطبيقات الأصلية افتراضيًا.

تتصل العمليتان من خلال الاتصال بين العمليات (IPC). هذا الفصل متعمد: فهو يبقي واجهات برمجة التطبيقات الأصلية الخطرة بعيدة عن محتوى الويب غير الموثوق.

إنشاء هيكل المشروع في Cursor​

اطلب من Cursor إنشاء هيكل مشروع TypeScript لـ Electron:

Create a minimal Electron + TypeScript + Vite project.
Include a main process file, a preload script with contextBridge,
and a renderer entry point. Use electron-builder for packaging.

يقوم Cursor بتوليد هيكل الدليل وملفات التكوين:

my-electron-app/
electron/
main.ts
preload.ts
src/
index.html
renderer.ts
package.json
electron-builder.yml
tsconfig.json

تتضمن package.json النصوص والتبعيات الأساسية:

{
"name": "my-electron-app",
"version": "1.0.0",
"main": "dist/electron/main.js",
"scripts": {
"dev": "concurrently \"vite\" \"wait-on tcp:5173 && cross-env NODE_ENV=development electron .\"",
"build": "tsc && vite build",
"package": "npm run build && electron-builder",
"package:mac": "npm run build && electron-builder --mac",
"package:win": "npm run build && electron-builder --win"
},
"devDependencies": {
"electron": "^31.0.0",
"electron-builder": "^24.13.3",
"typescript": "^5.4.0",
"vite": "^5.3.0",
"vite-plugin-electron": "^0.28.0"
}
}
warning

حدد دائمًا حقل main في package.json. إذا كان مفقودًا، يفترض Electron index.js في جذر المشروع، مما قد يحمل كودًا غير متوقع أثناء التطوير. اذكر نقطة الدخول المجمّعة صراحةً لتجنب الالتباس.

العملية الرئيسية​

العملية الرئيسية هي نقطة الدخول لتطبيق Electron الخاص بك. تنشئ النوافذ، وتتعامل مع دورة حياة التطبيق، وتسجل معالجات IPC.

إنشاء نوافذ المتصفح​

// electron/main.ts
import { app, BrowserWindow, ipcMain, shell } from 'electron';
import * as path from 'path';
import { isDev } from './utils/env';

let mainWindow: BrowserWindow | null = null;

function createWindow() {
mainWindow = new BrowserWindow({
width: 1200,
height: 800,
minWidth: 800,
minHeight: 600,
show: false,
backgroundColor: '#1e1e2e',
titleBarStyle: process.platform === 'darwin' ? 'hiddenInset' : 'default',
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
contextIsolation: true,
nodeIntegration: false,
sandbox: true,
},
});

mainWindow.once('ready-to-show', () => {
mainWindow?.show();
});

if (isDev) {
mainWindow.loadURL('http://localhost:5173');
mainWindow.webContents.openDevTools();
} else {
mainWindow.loadFile(path.join(__dirname, '../dist/index.html'));
}

// Handle external links in the default browser, not a new Electron window
mainWindow.webContents.setWindowOpenHandler(({ url }) => {
shell.openExternal(url);
return { action: 'deny' };
});
}

app.whenReady().then(() => {
createWindow();

app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});
});

app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
}
});
تلميح

اطلب من Cursor مراجعة تكوين BrowserWindow الخاص بك للبحث عن مشكلات أمنية. أمر مثل "Audit my Electron webPreferences for security best practices" سيكتشف الإعدادات الخطرة مثل nodeIntegration: true أو غياب نص preload.

تسجيل معالجات IPC​

تستقبل معالجات IPC في العملية الرئيسية الرسائل من العارض وتعيد النتائج. عرّف عقدًا محدد النوع بحيث يتفق الجانبان على سطح واجهة برمجة التطبيقات.

// electron/ipc-handlers.ts
import { ipcMain, dialog, BrowserWindow } from 'electron';
import * as fs from 'fs/promises';

export function registerIpcHandlers(): void {
ipcMain.handle('dialog:openFile', async () => {
const result = await dialog.showOpenDialog({
properties: ['openFile'],
filters: [{ name: 'Text', extensions: ['txt', 'md', 'json'] }],
});
if (result.canceled) return null;

const content = await fs.readFile(result.filePaths[0], 'utf-8');
return { path: result.filePaths[0], content };
});

ipcMain.handle('file:save', async (_event, filePath: string, content: string) => {
await fs.writeFile(filePath, content, 'utf-8');
return { success: true };
});

ipcMain.handle('window:minimize', (event) => {
const win = BrowserWindow.fromWebContents(event.sender);
win?.minimize();
});

ipcMain.handle('window:toggleMaximize', (event) => {
const win = BrowserWindow.fromWebContents(event.sender);
if (win?.isMaximized()) {
win.unmaximize();
} else {
win?.maximize();
}
});
}
warning

لا تمرر أبدًا كائن BrowserWindow أو واجهات Electron أخرى عبر IPC إلى العارض. غلف دائمًا العمليات الأصلية في معالجات محددة النوع وأعد بيانات عادية. تمرير الكائنات الحية عبر حدود العملية ينشئ مخاطر أمنية وتسرب ذاكرة خطيرة.

عزل السياق ونصوص Preload​

عزل السياق هو الحد الأمني الأساسي لـ Electron. عند تفعيله، يكون كائن window للعارض منفصلاً تمامًا عن نطاق نص preload. الطريقة الوحيدة لكشف الوظائف الأصلية للعارض هي من خلال واجهة contextBridge.

كتابة نص preload آمن​

// electron/preload.ts
import { contextBridge, ipcRenderer } from 'electron';

// Define the API surface exposed to the renderer
const api = {
// File operations
openFile: () => ipcRenderer.invoke('dialog:openFile'),
saveFile: (path: string, content: string) =>
ipcRenderer.invoke('file:save', path, content),

// Window controls
minimizeWindow: () => ipcRenderer.invoke('window:minimize'),
toggleMaximize: () => ipcRenderer.invoke('window:toggleMaximize'),

// Event subscriptions with cleanup
onUpdateAvailable: (callback: (info: { version: string }) => void) => {
const handler = (_event: unknown, info: { version: string }) => callback(info);
ipcRenderer.on('update-available', handler);
return () => ipcRenderer.removeListener('update-available', handler);
},
} as const;

// Expose a frozen, read-only API to the renderer
contextBridge.exposeInMainWorld('electronAPI', api);

استهلاك واجهة برمجة التطبيقات في العارض​

// src/renderer.ts
// TypeScript declaration so the renderer knows about window.electronAPI
declare global {
interface Window {
electronAPI: {
openFile: () => Promise<{ path: string; content: string } | null>;
saveFile: (path: string, content: string) => Promise<{ success: boolean }>;
minimizeWindow: () => Promise<void>;
toggleMaximize: () => Promise<void>;
onUpdateAvailable: (callback: (info: { version: string }) => void) => () => void;
};
}
}

const openButton = document.getElementById('open-btn');
const editor = document.getElementById('editor') as HTMLTextAreaElement;
let currentFilePath: string | null = null;

openButton?.addEventListener('click', async () => {
const result = await window.electronAPI.openFile();
if (result) {
currentFilePath = result.path;
editor.value = result.content;
}
});

document.getElementById('save-btn')?.addEventListener('click', async () => {
if (currentFilePath) {
await window.electronAPI.saveFile(currentFilePath, editor.value);
}
});
تلميح

ولّد عقد النوع بالكامل لـ preload والعارض دفعة واحدة بطلب Cursor: "Create a TypeScript interface shared between my Electron preload script and renderer that lists every IPC channel I use. Put it in a shared types file." هذا يبقي كلا الجانبين متزامنًا ويكتشف عدم التطابق وقت الترجمة.

warning

لا تستخدم أبدًا nodeIntegration: true أو contextIsolation: false في تطبيق Electron إنتاجي. هذه الإعدادات تكشف واجهات Node.js مباشرة لمحتوى الويب، وهو ثغرة أمنية حرجة إذا كان تطبيقك يحمل عناوين URL بعيدة أو يعالج إدخال مستخدم غير موثوق.

التغليف بـ electron-builder​

يقوم electron-builder بتجميع تطبيقك في مثبتات لـ macOS (.dmg) و Windows (.exe, .nsis) و Linux (.AppImage, .deb). يوجد التكوين في ملف YAML أو JSON واحد.

ملف التكوين​

# electron-builder.yml
appId: com.example.my-electron-app
productName: My Electron App
directories:
output: release
buildResources: build
files:
- dist/**/*
- dist/electron/**/*
extraMetadata:
main: dist/electron/main.js
mac:
category: public.app-category.productivity
target:
- dmg
- zip
hardenedRuntime: true
gatekeeperAssess: false
entitlements: build/entitlements.mac.plist
entitlementsInherit: build/entitlements.mac.plist
win:
target:
- nsis
artifactName: ${productName}-${version}-${arch}.${ext}
nsis:
oneClick: false
allowToChangeInstallationDirectory: true
createDesktopShortcut: true
linux:
target:
- AppImage
- deb
category: Office

البناء لجميع المنصات​

# Build for current platform
npm run package

# Build for macOS (requires macOS host)
npm run package:mac

# Build for Windows
npm run package:win
تلميح

اطلب من Cursor توليد سير عمل GitHub Actions يبني تطبيق Electron الخاص بك لـ macOS و Windows و Linux عند كل دفع وسم. أمر مثل "Create a CI workflow that runs electron-builder on three platforms and uploads artifacts to a GitHub release" ينتج ملف سير عمل جاهزًا للاستخدام.

warning

بناء مثبتات macOS يتطلب جهاز macOS مع شهادة Apple Developer صالحة. التجميع المتقاطع من Linux أو Windows غير مدعوم لبناءات macOS الموقعة. خطط لبنية CI الخاصة بك وفقًا لذلك.

التحديث التلقائي بـ electron-updater​

يتيح التحديث التلقائي للمستخدمين تلقي إصدارات جديدة دون تنزيل المثبتات يدويًا. حزمة electron-updater تتكامل مع إصدارات GitHub أو أي خادم ملفات ثابت.

إعداد التحديث التلقائي​

// electron/updater.ts
import { autoUpdater } from 'electron-updater';
import { BrowserWindow } from 'electron';

export function setupAutoUpdater(mainWindow: BrowserWindow): void {
autoUpdater.autoDownload = false;
autoUpdater.autoInstallOnAppQuit = true;

autoUpdater.on('update-available', (info) => {
// Notify the renderer that an update is available
mainWindow.webContents.send('update-available', { version: info.version });
});

autoUpdater.on('update-not-available', () => {
mainWindow.webContents.send('update-not-available');
});

autoUpdater.on('download-progress', (progress) => {
mainWindow.webContents.send('download-progress', {
percent: Math.round(progress.percent),
});
});

autoUpdater.on('update-downloaded', () => {
// Ask the user, then quit and install
mainWindow.webContents.send('update-downloaded');
});

// Check for updates after startup
app.whenReady().then(() => {
autoUpdater.checkForUpdates();
});
}

// Expose control methods through IPC
ipcMain.handle('update:download', () => {
autoUpdater.downloadUpdate();
});

ipcMain.handle('update:install', () => {
setImmediate(() => {
app.isQuiting = true;
autoUpdater.quitAndInstall();
});
});

واجهة التحديث في العارض​

// src/components/UpdateNotification.tsx
import { useEffect, useState } from 'react';

export function UpdateNotification() {
const [updateInfo, setUpdateInfo] = useState<{ version: string } | null>(null);
const [progress, setProgress] = useState(0);
const [downloaded, setDownloaded] = useState(false);

useEffect(() => {
const cleanup = window.electronAPI.onUpdateAvailable((info) => {
setUpdateInfo(info);
});

const progressHandler = (_event: unknown, data: { percent: number }) => {
setProgress(data.percent);
};
window.electronAPI.onDownloadProgress?.(progressHandler);

return cleanup;
}, []);

if (!updateInfo) return null;

return (
<div className="fixed bottom-4 right-4 rounded-lg border bg-white p-4 shadow-lg">
<p className="font-semibold">Version {updateInfo.version} is available</p>
{downloaded ? (
<button onClick={() => window.electronAPI.installUpdate?.()}>
Restart and Install
</button>
) : progress > 0 ? (
<div className="mt-2 h-2 rounded bg-gray-200">
<div className="h-2 rounded bg-blue-500" style={{ width: `${progress}%` }} />
</div>
) : (
<button onClick={() => window.electronAPI.downloadUpdate?.()}>
Download Update
</button>
)}
</div>
);
}
تلميح

استخدم autoUpdater.autoDownload = false لمنح المستخدمين التحكم في التنزيلات. التنزيلات الخلفية القسرية يمكن أن تستهلك النطاق الترددي على اتصالات计量 وتحبط المستخدمين. اعرض دائمًا إشعارًا ودع المستخدم يختار متى التنزيل.

warning

التحديث التلقائي يتطلب توقيع الكود على macOS. التطبيقات غير الموقعة ستفشل بصمت في خطوة التحقق من التحديث وسيبلغ electron-updater عن خطأ. على Windows، يمكن للتطبيقات غير الموقعة التحديث ولكنها تطلق تحذيرات SmartScreen التي تقلل ثقة المستخدم.

توليد واجهة سطح المكتب بمساعدة الذكاء الاصطناعي​

يتفوق Cursor في توليد مكونات واجهة بدرجة سطح المكتب. صف الواجهة التي تريدها، وينتج Cursor مكونات React متاحة وقابلة للتنقل بلوحة المفاتيح ومصممة لسياق سطح المكتب.

توليد شريط عنوان مخصص​

Create a React component for a custom Electron title bar on Windows.
Include minimize, maximize/restore, and close buttons. Use inline SVG
icons. Wire each button to the window.electronAPI IPC methods.
Support keyboard shortcuts (Alt+F4 to close, Win+Up to maximize).

يقوم Cursor بتوليد:

// src/components/TitleBar.tsx
import { useEffect, useState } from 'react';

export function TitleBar({ title }: { title: string }) {
const [isMaximized, setIsMaximized] = useState(false);

useEffect(() => {
const checkMax = () => setIsMaximized(window.electronAPI.isMaximized());
checkMax();
const interval = setInterval(checkMax, 200);
return () => clearInterval(interval);
}, []);

return (
<div
className="flex h-8 items-center justify-between select-none"
style={{ WebkitAppRegion: 'drag' } as React.CSSProperties}
>
<span className="ml-3 text-sm font-medium text-gray-200">{title}</span>
<div
className="flex h-full"
style={{ WebkitAppRegion: 'no-drag' } as React.CSSProperties}
>
<button
onClick={() => window.electronAPI.minimizeWindow()}
className="flex h-full w-12 items-center justify-center hover:bg-white/10"
aria-label="Minimize"
>
<svg width="12" height="12" viewBox="0 0 12 12">
<rect y="5" width="12" height="2" fill="currentColor" />
</svg>
</button>
<button
onClick={() => window.electronAPI.toggleMaximize()}
className="flex h-full w-12 items-center justify-center hover:bg-white/10"
aria-label={isMaximized ? 'Restore' : 'Maximize'}
>
{isMaximized ? (
<svg width="12" height="12" viewBox="0 0 12 12">
<rect x="1" y="3" width="8" height="8" stroke="currentColor" fill="none" />
<rect x="3" y="1" width="8" height="2" fill="currentColor" />
</svg>
) : (
<svg width="12" height="12" viewBox="0 0 12 12">
<rect x="1" y="1" width="10" height="10" stroke="currentColor" fill="none" />
</svg>
)}
</button>
<button
onClick={() => window.electronAPI.closeWindow()}
className="flex h-full w-12 items-center justify-center hover:bg-red-600"
aria-label="Close"
>
<svg width="12" height="12" viewBox="0 0 12 12">
<path d="M1 1 L11 11 M11 1 L1 11" stroke="currentColor" strokeWidth="2" />
</svg>
</button>
</div>
</div>
);
}
تلميح

عند توليد واجهة سطح المكتب في Cursor، اذكر المنصة دائمًا. تتصرف أشرطة العنوان بشكل مختلف على macOS (أزرار إشارات المرور على اليسار) مقابل Windows (أزرار النظام على اليمين). تحديد المنصة ينتج تخطيطات صحيحة ومناسبة للمنصة.

تحسين الأداء​

يمكن أن تبدو تطبيقات Electron ثقيلة إذا لم تُدر الموارد بعناية. يساعدك Cursor في تحديد وإصلاح اختناقات الأداء.

تقليل استخدام الذاكرة و CPU​

// electron/utils/performance.ts
import { app, BrowserWindow } from 'electron';

export function optimizePerformance(): void {
// Limit the number of background processes
app.commandLine.appendSwitch('disable-background-networking');
app.commandLine.appendSwitch('disable-renderer-backgrounding');

// Reduce GPU overhead for non-accelerated content
app.commandLine.appendSwitch('disable-gpu-compositing');

// Suspend background timers when the window is hidden
app.on('browser-window-blur', () => {
// Pause expensive operations
});

// Aggressively release memory when the app is idle
setInterval(() => {
const windows = BrowserWindow.getAllWindows();
if (windows.length === 0) {
// Trigger garbage collection if running with --inspect
if (global.gc) global.gc();
}
}, 60000);
}
warning

لا تطبق جميع مفاتيح commandLine بشكل أعمى. تعطيل تركيب GPU يمكن أن يكسر حركات CSS وعرض WebGL canvas. اختبر واجهتك الكاملة بعد كل تغيير في الأداء وألغِ المفاتيح التي تسبب انحدارات بصرية.

قائمة المراجعة الأمنية​

شغّل هذا الأمر في Cursor لتدقيق مشروع Electron بالكامل للبحث عن المشكلات الأمنية:

Review my Electron app for security vulnerabilities. Check for:
1. contextIsolation enabled
2. nodeIntegration disabled
3. sandbox enabled
4. CSP headers set
5. No remote modules loaded
6. preload script uses contextBridge only
7. All IPC handlers validate input
8. Webview tags disabled or sandboxed

يفحص Cursor قاعدة الكود الخاصة بك ويعيد قائمة مرتبة حسب الأولوية من المشكلات مع مراجع ملفات محددة وإصلاحات مقترحة.

تلميح

أضف قائمة المراجعة الأمنية لـ Electron كملف .cursorrules في جذر مشروعك. سيقوم Cursor تلقائيًا بفرض هذه القواعد عند توليد كود Electron جديد، مما يمنع الانحدارات الأمنية بمرور الوقت.

الخلاصة​

بناء تطبيقات سطح المكتب بـ Electron في Cursor يجمع بين قوة المنصات المتعددة لتقنيات الويب وسرعة التطوير بمساعدة الذكاء الاصطناعي. باتباع أنماط هذا الدليل، IPC معزول السياق وآمن، وعقود preload محددة النوع، وتغليف بـ electron-builder، وتحديث تلقائي يتحكم فيه المستخدم، يمكنك إطلاق تطبيقات سطح مكتب احترافية أسرع من أي وقت مضى. تذكر إبقاء contextIsolation مفعلاً، وnodeIntegration معطلاً، ودع Cursor يدقق كودك بحثًا عن الأمان والأداء في كل خطوة.