{"id":15699226,"url":"https://github.com/frinyvonnick/electron-workshop","last_synced_at":"2025-05-09T02:21:08.484Z","repository":{"id":68828388,"uuid":"66583828","full_name":"frinyvonnick/electron-workshop","owner":"frinyvonnick","description":"A workshop to discover Electron and its ecosytem step by step","archived":false,"fork":false,"pushed_at":"2017-02-21T19:45:25.000Z","size":18623,"stargazers_count":6,"open_issues_count":1,"forks_count":2,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-03-31T21:27:47.920Z","etag":null,"topics":["desktop","electron","french","javascript","nantes","workshop","zenika"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/frinyvonnick.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2016-08-25T18:41:24.000Z","updated_at":"2021-11-23T22:37:38.000Z","dependencies_parsed_at":null,"dependency_job_id":"065fa810-b36a-4d69-817c-2b24ddbc736e","html_url":"https://github.com/frinyvonnick/electron-workshop","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/frinyvonnick%2Felectron-workshop","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/frinyvonnick%2Felectron-workshop/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/frinyvonnick%2Felectron-workshop/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/frinyvonnick%2Felectron-workshop/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/frinyvonnick","download_url":"https://codeload.github.com/frinyvonnick/electron-workshop/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253176668,"owners_count":21866185,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["desktop","electron","french","javascript","nantes","workshop","zenika"],"created_at":"2024-10-03T19:38:55.397Z","updated_at":"2025-05-09T02:21:08.455Z","avatar_url":"https://github.com/frinyvonnick.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![Electron Logo](http://electron.atom.io/images/electron-logo.svg)](http://electron.atom.io/)\n\n# Workshop Electron NightClazz Zenika\n\nLe but du workshop est de construire une application de génération de memes avec le framework Electron.\n\nLe workshop commencera d'abord par une présentation d'Electron avant de vous laissez avancer à votre rythme.\n\nPour pouvoir continuer d'avancer même si une étape est problématique, nous vous fournissons les solutions de chacune des étapes.\n\n## Description des répertoires :\n```\nsolutions // solutions pour les différentes étapes\n|-etape-01\n|-etape-02\n...\nsrc\n|-assets            CSS/images/js pour outillage\n|-main-process      JS côté main-process\n|-renderer-process  JS côté main-process\n|-windows           HTML des différents windows\n|-main.js         Point d'entrée de l'application\n```\n\n## Description des étapes\n\nAfin de mettre en oeuvre les concepts d'Electron, nous vous proposons de développer une application de meme generator.\n\nNous partirons d'un squelette de projet Electron simple, qui sera enrichi au fur et à mesure des étapes. Le résultat final sera une application desktop multi-fenêtrée, avec les interactions et comportements d'une application desktop moderne.\n\nLe squelette est composé de tous les fichiers de l'application. Vous n'aurez qu'à compléter ces fichiers. Dans chacun de ces fichiers, vous trouverez pour chacune des étapes des commentaires du type `// TODO (Etape X)` qui vous permettra de voir où placer votre code.\n\n### Etape 01 : Hello world!\n\nNous allons commencer par démarrer notre application Electron en affichant une première page statique.\n\n- Ouvrir le fichier `src/main.js`\n- Importer les dépendances `app` et `BrowserWindow` depuis `electron`\n- Importer la librairie `path`\n- Sur l'événement `ready` de app, instancier une nouvelle `BrowserWindow` et assigner à `mainWindow`\n- Charger le fichier `windows/hello.html` dans votre fenêtre nouvellement créée\n- Démarrer votre application en exécutant `electron .`\n\nDocumentation nécessaire à l'étape :\n- http://electron.atom.io/docs/api/app/\n- http://electron.atom.io/docs/api/browser-window/\n\n\u003cdetails\u003e\n\u003csummary\u003eSolution\u003c/summary\u003e\nDans le fichier `src/main.js`\n```js\n  const { app, BrowserWindow } = require('electron')\n  const path = require('path')\n```\n```js\n  mainWindow = new BrowserWindow()\n  mainWindow.loadURL(path.join('file://', __dirname, 'windows/hello.html'))\n```\n\u003c/details\u003e\n\n![Capture de l'application à l'étape 01](screenshots/etape-01.png)Etat de l'application à la fin de l'étape\n\n### Etape 02 : Customiser la fenêtre\n\nMaintenant que notre application Electron affiche une première fenêtre, nous vous proposons de changer l'affichage à l'aide de différentes options. Nous allons aussi exploiter la capacité de live-reloading du module `electron-connect`.\n\n- Démarrer l'application en lançant `npm run dev`, l'application va démarrer en mode dev avec du live-reloading\n- Changer la taille de la fenêtre dans le fichier `src/main.js`\n- Enlever les bordures de la fenêtre\n- Ouvrir par défaut les devTools via `mainWindow.webContents.openDevTools()`\n\nDocumentation nécessaire à l'étape :\n- https://github.com/Quramy/electron-connect\n- http://electron.atom.io/docs/api/browser-window/#new-browserwindowoptions\n\n\u003cdetails\u003e\n\u003csummary\u003eSolution\u003c/summary\u003e\nDans le fichier `src/main.js`\n```js\n  mainWindow = new BrowserWindow({ width: 1000, height: 800, frame : false })\n  mainWindow.webContents.openDevTools()\n```\n\u003c/details\u003e\n\n![Capture de l'application à l'étape 02](screenshots/etape-02.png)Etat de l'application à la fin de l'étape\n\n### Etape 03 : Affichage de la liste des memes\n\nNous allons maintenant afficher la galerie de memes dans notre fenêtre.\n\nDans le fichier `src/main.js`\n- Changer le fichier HTML chargé dans la mainWindow par le fichier `windows/index.html`\n\nDans le fichier `src/windows/index.html`\n- Avec la fonction require, importer le fichier `src/renderer-process/grid.js` de manière relative au fichier `index.html` dans la balise `\u003cscript\u003e`\n\n\u003cdetails\u003e\n\u003csummary\u003eSolution\u003c/summary\u003e\nDans le fichier `src/main.js`\n```js\n  mainWindow.loadURL(path.join('file://', __dirname, 'windows/index.html'))\n```\nDans le fichier `src/windows/index.html`\n```html\n  \u003cscript type=\"text/javascript\"\u003e\n    require('../renderer-process/grid.js');\n  \u003c/script\u003e\n```\n\u003c/details\u003e\n\n![Capture de l'application à l'étape 03](screenshots/etape-03.png)Etat de l'application à la fin de l'étape\n\n### Etape 04 : Inter Process Communication\n\nNotre application affiche maintenant une liste statique d'images. La prochaine étape va consister à récupérer la liste des memes à afficher depuis un espace de stockage local (`electron-json-storage`). Nous allons utiliser l'IPC (Inter Process Communication) pour échanger des informations entre le main-process et le renderer-process.\n\nDans le fichier `src/renderer-process/grid.js`\n- Envoyer un message `get-memes` via le module `ipcRenderer`\n- Déplacer le rendu de la galerie dans le callback appelé lors de la réception d'un message `memes-sended`\n- Utiliser la liste des images passée en paramètre de ce callback\n\nDans le fichier `src/main-process/grid.js`\n- Mettre en place un handler pour le message `get-memes` avec le module `ipcMain`\n- Dans le callback du handler, appeler la fonction `getMemes` qui prend un callback comme paramètre\n- Dans le callback de `getMemes`, émettre en retour un message `memes-sended` avec la liste des images fournie en paramètre\n\nDocumentation nécessaire à l'étape :\n- http://electron.atom.io/docs/api/ipc-renderer/#sending-messages\n- http://electron.atom.io/docs/api/ipc-main/#listening-for-messages\n- http://electron.atom.io/docs/api/ipc-main/#sending-messages\n\n\u003cdetails\u003e\n\u003csummary\u003eSolution\u003c/summary\u003e\nDans le fichier `src/renderer-process/grid.js`\n```js\nipcRenderer.send('get-memes')\n```\n```js\nipcRenderer.on('memes-sended', (e, images) =\u003e {\n  document.getElementById('content').innerHTML = images.reduce((prev, next, index) =\u003e {\n    return `${prev}\n    \u003cdiv class=\"card meme\" data-index=\"${index}\"\u003e\n    \u003cdiv class=\"img\" style=\"background-image:url('${next.path.split('\\\\').join('\\\\\\\\')}')\"\u003e\u003c/div\u003e\n    \u003ch3 title=\"${next.title}\"\u003e\u003cspan\u003e${next.title}\u003c/span\u003e\u003c/h3\u003e\n    \u003c/div\u003e`\n  }, '')\n```\nDans le fichier `src/main-process/grid.js`\n```js\nipcMain.on('get-memes', (e) =\u003e {\n  getMemes(memes =\u003e {\n    e.sender.send('memes-sended', memes)\n  })\n})\n```\n\u003c/details\u003e\n\n![Capture de l'application à l'étape 04](screenshots/etape-04.png)Etat de l'application à la fin de l'étape\n\n### Etape 05 : File dialog\n\nMaintenant que nous avons une liste de memes par défaut, nous allons donner la possibilité à l'utilisateur d'ajouter l'image de son choix via une file dialog.\n\nDans le fichier `src/renderer-process/grid.js`\n- Ajouter un event listener `click` sur l'élément avec l'id `new-meme`\n- Dans cet event listener, émettre un événement `open-file-dialog` avec l'IPC\n\nDans le fichier `src/main-process/grid.js`\n- Dans celui-ci, importer le module `dialog` depuis `electron`\n- Déclarer l'event handler `open-file-dialog`\n- En réponse à cet event, afficher une `dialog` qui va lister seulement les fichiers images (extensions jpg, gif, png)\n- Implémenter un callback qui va appeler la fonction `newEditWindow` avec le fichier choisi par l'utilisateur\n- Gérer l'événement `closed` de la fenêtre nouvellement créée en renvoyant la liste de memes à jour\n\nDocumentation nécessaire à l'étape :\n- http://electron.atom.io/docs/api/dialog/\n\n\u003cdetails\u003e\n\u003csummary\u003eSolution\u003c/summary\u003e\nDans le fichier `src/renderer-process/grid.js`\n```js\ndocument.getElementById('new-meme').addEventListener('click', () =\u003e ipcRenderer.send('open-file-dialog'))\n```\nDans le fichier `src/main-process/grid.js`\n```js\nconst { ipcMain, dialog } = require('electron')\n```\n```js\nipcMain.on('open-file-dialog', (event) =\u003e {\n  dialog.showOpenDialog({\n    properties: ['openFile'],\n    filters: [{ name: 'Images', extensions: ['jpg', 'png', 'gif'] }]\n  }, (files) =\u003e {\n    if (files) {\n      const editWindow = newEditWindow(files[0])\n      editWindow.on('closed', () =\u003e {\n        getMemes(memes =\u003e {\n          event.sender.send('memes-sended', memes)\n        })\n      })\n    }\n  })\n})\n```\n\u003c/details\u003e\n\n![Capture de l'application à l'étape 05](screenshots/etape-05.png)Etat de l'application à la fin de l'étape\n\n### Etape 06 : Menu contextuel\n\nA cette étape, nous allons ajouter un menu contextuel afin de supprimer et de sauvegarder chacune des images de la galerie de memes. Nous allons utiliser les classes de menu présentes dans Electron.\n\n- Ouvrir le fichier `src/renderer-process/grid.js`\n- Importer le module `remote` depuis le module `electron` pour pouvoir accéder à l'API du main process\n- Importer les classes `Menu` et `MenuItem` depuis `remote`\n- Ajouter un event listener `contextmenu` sur chacun des éléments de la galerie\n- Créer un menu contextuel dans le callback de l'event listener avec comme items :\n - `Save as` qui enverra un message `save-from-grid` sur l'IPC\n - `Delete` qui enverra un message `deleted-selected-meme` sur l'IPC\n\nDocumentation nécessaire à l'étape :\n- http://electron.atom.io/docs/api/menu/#render-process\n- http://electron.atom.io/docs/api/menu-item/\n- http://electron.atom.io/docs/api/remote/#remotegetcurrentwindow\n\n\u003cdetails\u003e\n\u003csummary\u003eSolution\u003c/summary\u003e\nDans le fichier `src/renderer-process/grid.js`\n```js\nconst { remote, ipcRenderer } = require('electron')\nconst { Menu, MenuItem } = remote\n```\n```js\nelement.addEventListener('contextmenu', e =\u003e {\n  e.preventDefault()\n  let menu = new Menu()\n  menu.append(new MenuItem({label: 'Save as', click (item, browserWindow) { ipcRenderer.send('save-from-grid', images[parseInt(element.getAttribute('data-index'), 10)].path) }}))\n  menu.append(new MenuItem({label: 'Delete', click (item, browserWindow) { ipcRenderer.send('delete-selected-meme', images[parseInt(element.getAttribute('data-index'), 10)]) }}))\n  menu.popup(remote.getCurrentWindow())\n})\n```\n\u003c/details\u003e\n\n![Capture de l'application à l'étape 06](screenshots/etape-06.png)Etat de l'application à la fin de l'étape\n\n### Etape 07 : Notifications\n\nMaintenant que nous avons la possibilité d'ajouter et d'enlever des memes, nous allons émettre des notifications pour que l'utilisateur obtienne une confirmation de ses actions. Pour ce faire nous allons utiliser la classe `Notification` de l'API HTML5.\n\n- Ouvrir le fichier `src/renderer-process/grid.js`\n- Ajouter une notification en utilisant la classe `Notification` après la suppression d'un meme\n- Ajouter une notification après l'enregistrement d'un meme\n\nDocumentation nécessaire à l'étape :\n- https://notifications.spec.whatwg.org/\n- http://electron.atom.io/docs/tutorial/desktop-environment-integration/#notifications-windows-linux-macos\n\n\u003cdetails\u003e\n\u003csummary\u003eSolution\u003c/summary\u003e\nDans le fichier `src/renderer-process/grid.js`\n```js\nnew Notification('Meme Generator', { // eslint-disable-line no-new\n  body: 'Le meme a bien été supprimé'\n})\n```\n```js\nnew Notification('Meme Generator', { // eslint-disable-line no-new\n  body: `Le meme a été sauvegardé à l'emplacement ${path}`\n})\n```\n\u003c/details\u003e\n\n![Capture de l'application à l'étape 07](screenshots/etape-07.png)Etat de l'application à la fin de l'étape\n\n### Etape 08 : Packaging\n\nNous allons terminer l'atelier en packageant notre application. Pour cela, nous allons utiliser electron-packager qui est maintenu par la communauté.\n\n- Ouvrir le fichier `package.json`\n- Implémenter le npm script `package` qui va appeler electron-packager\n- Ajouter les options pour :\n  - cibler votre plateforme et son architecture\n  - choisir `dist` comme répertoire de sortie\n  - pouvoir repackager l'application même si le packaging a déjà été créé\n\nDocumentation nécessaire à l'étape :\n- https://github.com/electron-userland/electron-packager\n- https://github.com/electron-userland/electron-packager/blob/master/usage.txt\n\n\u003cdetails\u003e\n\u003csummary\u003eSolution\u003c/summary\u003e\nDans le fichier `package.json`\n```bash\nelectron-packager . --out=dist --app-version=$npm_package_version --platform=win32 --arch=x64 --asar --overwrite --ignore \\\"node_modules/\\\\.bin\\\"\n```\n\u003c/details\u003e\n\n### Etape bonus : Testing\n\nPour ceux qui veulent aller plus loin, vous pouvez ajouter des tests.\n\n- Ouvrir le fichier `src/tests/index.js`\n- Rajouter les tests suivant :\n  - l'application n'ouvre qu'une seule fenêtre au lancement\n  - le titre de la fenêtre est bien `Electron meme generator`\n  - la taille de la fenêtre est bien celle que vous avez précisée au lancement\n  - l'application affiche au moins un meme (élément HTML avec la classe CSS `meme`)\n\nDocumentation nécessaire à l'étape :\n- http://chaijs.com/api/bdd/\n- https://github.com/domenic/chai-as-promised#shouldexpect-interface\n- https://github.com/electron/spectron#clientgetwindowcount\n- https://github.com/electron/spectron#browserwindow\n- http://electron.atom.io/docs/api/browser-window/#wingetbounds\n- https://github.com/electron/spectron#client\n\n\u003cdetails\u003e\n\u003csummary\u003eSolution\u003c/summary\u003e\nDans le fichier `src/tests/index.js`\n```js\nit('opens only one window', function () {\n  return app.client.getWindowCount().should.eventually.be.equal(1)\n})\n\nit('opens a window with \"Electron meme generator\" title', function () {\n  return app.client.browserWindow.getTitle().should.eventually.equal('Electron meme generator')\n})\n\nit('opens a window with the right size', function () {\n  return app.client.browserWindow.getBounds().should.eventually.have.property('width').and.be.equal(1000)\n                   .browserWindow.getBounds().should.eventually.have.property('height').and.be.equal(800)\n})\n\nit('displays the list of memes', function () {\n  return app.client.element('.meme').should.eventually.exist\n})\n```\n\u003c/details\u003e\n\nPour aller plus loin :\n- Le site d'Electron : http://electron.atom.io\n- La liste awesome-electron qui regroupe plein de projets autour d'Electron : https://github.com/sindresorhus/awesome-electron\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffrinyvonnick%2Felectron-workshop","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffrinyvonnick%2Felectron-workshop","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffrinyvonnick%2Felectron-workshop/lists"}